整数溢出在C++中引发未定义行为,需通过编译期检查(如-fsanitize=undefined)、运行时防护(如std::add_overflow)和编码习惯(如预校验输入、避免无符号减法)协同防范。

整数溢出在C++中不会自动报错,而是产生未定义行为(UB),轻则逻辑错误,重则被利用触发远程代码执行。检测和避免的关键在于:编译期检查 + 运行时防护 + 编码习惯约束。
编译期开启溢出检测(Clang/GCC)
现代编译器支持内置的整数溢出检查机制,无需修改源码即可捕获大部分常见溢出:
- Clang 加 -fsanitize=integer 或更全的 -fsanitize=undefined,运行时会打印溢出位置并终止程序
- GCC 11+ 支持 -fanalyzer 静态分析,部分溢出路径可提前预警;配合 -Woverflow 和 -Wsign-conversion 捕获隐式转换风险
- 启用 -ftrapv(GCC/Clang)让有符号溢出直接触发 SIGABRT,但注意它不覆盖无符号类型(C++标准规定无符号溢出是回绕,非UB)
用安全算术库替代裸运算
手动检查每个 +、*、- 是否溢出既繁琐又易漏。推荐使用经过验证的安全封装:
-
std::add_overflow / std::mul_overflow / std::sub_overflow(C++23 标准库新增),返回
bool表示是否成功,并通过输出参数写入结果 - 第三方库如 absl::int128(带溢出检查的宽整型)、SafeInt(微软开源,模板化强类型检查)
- 自定义宏或内联函数(仅限简单场景):
#define SAFE_ADD(a, b, res) (__builtin_add_overflow((a), (b), (res)))(GCC/Clang 内建函数)
设计阶段规避溢出风险
很多溢出其实在逻辑层就可避免,不依赖运行时检查:
立即学习“C++免费学习笔记(深入)”;
- 对用户输入或外部数据做范围预校验,例如分配内存前先判断
size * elem_size是否超过SIZE_MAX - 用更大类型承载中间结果:如用
size_t计算数组索引,避免int累加后溢出导致负索引 - 禁用无符号类型做减法(如
size_t i = 0; i--会绕成极大值),改用有符号类型或显式边界判断 - 容器操作优先用
.at()而非[],配合.size()检查而非手算下标
静态分析与 fuzzing 辅助发现
人工审查难以覆盖所有路径,需工具补位:
- Clang Static Analyzer(
clang++ -O2 --analyze)能识别部分溢出模式,尤其配合注解如__attribute__((warn_unused_result)) - 用 libFuzzer 或 AFL++ 对数值处理函数做模糊测试,随机喂入极大/极小值,触发崩溃即可能暴露溢出
- 代码扫描工具如 Cppcheck(启用
--inconclusive --enable=warning,style)可标记可疑表达式,如a * b > INT_MAX类比较
基本上就这些。溢出不是“会不会发生”的问题,而是“何时被发现”的问题。把编译器警告当错误、把 sanitizer 当日常、把安全算术当默认,比事后修 bug 省力得多。









