最稳的溢出防范是在运算前用std::numeric_limits预判边界,而非事后检查;配合__builtin_add_overflow等编译器内置函数运行时兜底,并优先考虑升级类型而非硬加检查。

用 std::numeric_limits 预判溢出边界
直接靠算完再检查结果是否“变小了”或“符号翻转”不可靠,尤其涉及负数或无符号类型时。最稳的方式是:在运算前,先确认操作数是否在安全范围内。
std::numeric_limits<int>::max()</int> 和 std::numeric_limits<int>::min()</int> 给出当前平台 int 的确切上下界,比硬写 2147483647 更可移植、更清晰。
- 加法前检查:
a > std::numeric_limits<int>::max() - b</int>→ 可能上溢;a ::min() - b→ 可能下溢 - 减法转加法处理(
a - b等价于a + (-b)),复用同一套逻辑 - 乘法要更谨慎:
abs(a) > 0 && abs(b) > std::numeric_limits<int>::max() / abs(a)</int>这类判断需额外处理 0 和INT_MIN(因为-INT_MIN本身溢出)
用 __builtin_add_overflow 做运行时兜底(GCC/Clang)
编译器内置函数比手写条件判断更高效,且能正确处理所有边缘情况(比如 INT_MIN - 1 这种未定义行为的检测)。
它不计算结果,只返回是否溢出,并把安全结果写入输出变量——适合对性能敏感又不想引入大依赖的场景。
立即学习“C++免费学习笔记(深入)”;
- 函数签名是:
__builtin_add_overflow(a, b, &result),返回true表示溢出 - 同样有
__builtin_sub_overflow和__builtin_mul_overflow - 注意:MSVC 不支持,跨平台项目需
#ifdef __GNUC__或改用std::add_overflow(C++23) - 别把它当普通函数调用——这些 builtin 是编译器特化指令,传非常量参数也可能被优化掉溢出检查
int 溢出本身就是未定义行为(UB),别依赖“看起来没崩”
C++ 标准规定,有符号整数溢出是未定义行为。这意味着:程序可能看似正常运行、可能崩溃、可能返回错误值、甚至影响前面的代码逻辑——取决于编译器优化级别和目标架构。
- 开启
-fsanitize=undefined编译时会报signed integer overflow,强烈建议在开发/测试阶段启用 -
int在不同平台可能是 16/32/64 位,sizeof(int)不等于 4 —— 别假设字长 - 隐式转换容易埋雷:比如
int a = 2000000000; long b = a * 2;,乘法先按int算,早已溢出,再转long也救不回来
什么时候该换类型,而不是硬防溢出
如果业务逻辑天然需要更大范围(比如累计用户点击量、时间戳差值、ID 运算),硬加一堆溢出检查反而增加复杂度和维护成本。
- 优先考虑
long long(至少 64 位)或int64_t(明确宽度) - 计数类场景用
size_t要小心:它是无符号的,下溢会绕回极大值,且和有符号类型混算易触发隐式转换警告或错误 - 涉及网络协议或文件格式时,必须严格匹配字段定义的宽度(如
int32_t),此时防溢出是刚需,不能换类型
溢出检查不是加个 if 就完事,关键在时机——算之前预判,比算之后补救可靠得多;而一旦发现频繁靠近边界,大概率是类型选小了,或者算法该重构了。










