有符号整数溢出
根据 C99 和 C11 的第 6.5 / 5 段,如果结果不是表达式类型的可表示值,则表达式的求值会产生未定义的行为。对于算术类型,这称为溢出。无符号整数运算不会溢出,因为第 6.2.5 / 9 段适用,导致任何超出范围的无符号结果减少到范围内值。但是,对于有符号整数类型没有类似的规定 ; 这些可以并且确实溢出,产生未定义的行为。例如,
#include <limits.h> /* to get INT_MAX */
int main(void) {
int i = INT_MAX + 1; /* Overflow happens here */
return 0;
}
大多数此类未定义行为的实例更难以识别或预测。原则上,溢出可以来自对有符号整数的任何加法,减法或乘法运算(取决于通常的算术转换),其中没有有效的界限或操作数之间的关系以防止它。例如,这个功能:
int square(int x) {
return x * x; /* overflows for some values of x */
}
是合理的,并且对于足够小的参数值它是正确的,但是对于较大的参数值,它的行为是未定义的。你无法仅根据函数判断调用它的程序是否会显示未定义的行为。这取决于他们传递给它的论据。
另一方面,请考虑溢出安全有符号整数运算的这个简单示例:
int zero(int x) {
return x - x; /* Cannot overflow */
}
减法运算符的操作数之间的关系确保减法永不溢出。或者考虑这个更实际的例子:
int sizeDelta(FILE *f1, FILE *f2) {
int count1 = 0;
int count2 = 0;
while (fgetc(f1) != EOF) count1++; /* might overflow */
while (fgetc(f2) != EOF) count2++; /* might overflow */
return count1 - count2; /* provided no UB to this point, will not overflow */
}
只要计数器不单独溢出,最后减法的操作数都将是非负的。任何两个这样的值之间的所有差异都可以表示为 int
。