来自:C缺陷与陷阱3.9节
c语言中存在两类整数算术运算,有符号运算与无符号运算。在无符号算术运算中,没有所谓的“溢出”一说:所有的无符号运算都是以2的n次方为模,这里的n是结果中的位数。如果算术运算符的一个操作数为有符号整数,另一个为无符号整数,那么有符号整数会被转换成无符号整数,“溢出”也不可能发生。但是,当两个操作数都是有符号数时,溢出就有可能发生,而且溢出的结果是未定义的。当一个运算的结果发生溢出时,做出任何假设都是不安全的。
假如a和b是两个非负整型变量,我们需要检查a+b是否会溢出,一种想当然的办法是这样:
if( a + b < 0 )
{
complain();
}
这并不能正常运行。当a+b确实发生溢出时,所有关于结果如何的假设都不再可靠。例如,在某些机器上,加法运算将设置一个内部寄存器为四种状态之一:正、负、零、溢出。在这种机器上,c编译器完全有理由这样来实现上面的例子,即a与b相加,然后检查内部寄存器的标志是否为“负”。当加法操作发生溢出时,这个内部寄存器的状态是溢出而不是负,那么if的语句的检查就会失败。
一种正确的方式是将a和b都强制转换为无符号整数:
if( ( unsigned )a + ( unsigned )b > INT_MAX ) { complain(); } |
此处的INT_MAX是一个已定义的常量,代表可能的最大整数值。ANSI C标准在<limits.h>中定义了INT_MAX。不需要用到无符号算术运算的另外一种可行办法是:
if( a > INT_MAX - b )
{
complain();
}
#include <limits.h> #include <stdio.h>
int main() { int a = 2147483640, b = 2147483640, c = 0, d = 0;
if( ( unsigned )a + ( unsigned )b > INT_MAX ) { printf( " 最大的整数是:%d, 最小的整数是:%d /n",INT_MAX,INT_MIN ); printf( " INT_MAX的值为2147483647,INT_MIN的值为-2147483648 /n" ); }
c = a + b; d = -a + -b; printf( "***%d***%d***/n", c, d ); getchar(); return 0; } |