这篇对整数的一些基本运算产生的溢出问题进行分析。函数
当你进行加减乘除运算的时候,若是这个数字很大,运算产生的结果就可能会出乎你最初设计程序的预料,这对程序来讲是一种很可怕的漏洞,这让一些恶意的访问者对程序做出一些破坏性的事情,这样形成的危害多是很大的。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; }