在C语言中,对整数类型的算术操作可能导致溢出(overflow),当结果超出该类型可表示的范围时,例如int或unsigned int。溢出时的行为特性受语言标准的约束。
带符号类型的溢出(signed overflow)会导致未定义行为,即编译器可以执行任何操作:忽略错误、生成异常或返回不可预测的结果。对于无符号类型(unsigned),根据C标准,溢出的行为是确定的:按类型大小进行模运算重置(wraparound)。
对于无符号数,溢出结果可以轻松预测,例如UINT_MAX + 1 == 0。对于带符号数,建议在运算之前使用<limits.h>中的宏检查类型的边界,或使用静态分析工具。现代编译器和工具可以检测潜在的溢出情况。
代码示例:
#include <stdio.h> #include <limits.h> int add_with_check(int a, int b) { if (a > 0 && b > INT_MAX - a) { printf("会发生溢出!\n"); return -1; } return a + b; } int main() { int x = INT_MAX, y = 1; printf("结果: %d\n", add_with_check(x, y)); unsigned int ux = UINT_MAX; printf("无符号溢出: %u\n", ux + 1); return 0; }
关键特点:
<limits.h>获取类型的大小无符号类型的溢出算不算错误?
不是,这种行为是标准定义的,相当于模运算重置。例如,(unsigned int)UINT_MAX + 1 == 0始终为真。
可以依赖于当溢出时int的结果会简单地"翻转"至INT_MIN吗?
不,这种行为并不被保证,也没有标准化,这是未定义行为。可能会崩溃,返回不正确(跨平台不同)的值,或被编译器以不可预测的方式优化。
可以依赖于int始终是双补数表示吗?
尽管现代硬件几乎总是使用“二补数”表示带符号int,C语言标准并不要求这样,因此涉及溢出的代码不具可移植性。
在没有边界检查的情况下对int进行加法运算——在大数据情况下的溢出导致无效的计算。
优点:
缺点:
在所有算术操作之前使用宏和函数进行溢出检查。在可接受包围的情况下使用无符号类型。
优点:
缺点: