這篇對整數的一些基本運算產生的溢出問題進行分析。函數
當你進行加減乘除運算的時候,若是這個數字很大,運算產生的結果就可能會出乎你最初設計程序的預料,這對程序來講是一種很可怕的漏洞,這讓一些惡意的訪問者對程序做出一些破壞性的事情,這樣形成的危害多是很大的。spa
這裏先定義一些宏定義做爲返還值:設計
#define OVERFLOW 1 //算術溢出(正溢出) #define NOT_OVERFLOW 0 //未溢出 #define NEGATIVE_OVERFLOW -1 //負溢出
當進行無符號整數加法計算的時候。code
無符號整數的範圍是 0 ≤ x ≤ UMax ,UMax = 2w - 1 (w是當前類型的位數) ,若是兩個數相加的結果小於任何一個數,那麼就能夠判斷算術溢出。判斷的代碼以下:
blog
int uadd_ok(unsigned x, unsigned y) { unsigned uadd=x+y; if( uadd < x || uadd < y) return OVERFLOW; return NOT_OVERFLOW; }
當進行有符號整數加法計算的時候。get
有符號整數的範圍是 TMin ≤ x ≤ TMax ,TMin = -2w-1 ,TMax = 2w-1 - 1 那麼爲什TMax的絕對之爲何會比TMin的絕對之小一,那麼假設 w= 4 觀察一下。由於有符號數的最高位是符號位,因此TMax的最高位就是 0 ,爲了讓數字更大那麼其他就應該是 1 ,那麼 TMax (4) = 0111 = 22+21+20=23-1=2w-1-1。有符號數的負數部分通常都是用補碼錶示的 TMin 最高位就須要是 1 由於只有最高位是表明負數,其他位都是正數,最後這個數的值是各個位的值的加和獲得的,因此其他位都須要是 0 ,那麼 TMin(4)=1000= -23 = -2w-1 。那麼判斷有符號數加法的代碼一種錯誤以下,它利用了阿貝爾羣得知補碼加法的時候,(x+y)-y=y 在溢出的狀況下是成立的。因此就有可能出現以下的代碼:
it
int tadd_ok(int x, int y) { int sum=x+y; return (sum - y == y) && (sum - x == y) ; }
可是這個代碼的返回值永遠是 1, 由於在不溢出的狀況下表達式成立是顯而易見的。class
因此就須要考慮多方面的因素了,兩個數都大於零的時候,若是結果是負數那麼可判斷溢出。若是兩個數都是負數,結果爲正數那麼判斷也溢出。程序
代碼以下:di
int tadd_ok(int x, int y) { int tadd=x+y; if( x > 0 && y > 0 ) if( tadd < 0 ) return OVERFLOW; if( x < 0 && y < 0 ) if( tadd >= 0 ) return NEGATIVE_OVERFLOW; return NOT_OVERFLOW; }
當進行有符號減法運算的時候。
這個時候你可能想像這樣調用寫好的 tadd_ok( x, -y ); 函數,可是實際上在一些狀況下這是錯誤的,由於 TMin = -TMin ,這樣你想要的 -TMin 就沒有變成 | TMin | 卻變成了TMin 因此就須要增長一些判斷。
代碼以下:
int tsub_ok(int x, int y) { if( y != 0 && y == -y ) { if( x >= 0 ) return OVERFLOW; return NOT_OVERFLOW; } return tadd_ok(x,-y); }
當是乘法運算的時候不論有符號仍是無符號,溢出的部分都是直接截掉的。
固然乘法是可使用 ( x * y ) / x = y 進行判斷的由於溢出的時候這是不成立的,固然你須要保證 x , y 不是零 ,若是是零做爲除數是不合理的。
因此能夠寫出以下代碼:
int tmult_ok(int x, int y) { int tmult=x*y; if( x != 0 && y!= 0 ) { if( tmult / x == y ) return NOT_OVERFLOW; return OVERFLOW; } return NOT_OVERFLOW; }