C++23中推荐使用std::add_overflow和std::sub_overflow进行安全加减,语义清晰、无副作用、支持所有整型;若不支持C++23,可用GCC/Clang的__builtin_add_overflow等内置函数;手动检查易出错,需运算前判断且注意类型提升与符号边界。

用 std::add_overflow 和 std::sub_overflow 做安全加减(C++23)
这是目前最直接、标准且无副作用的方案,但仅限 C++23 起可用。它不依赖编译器内置函数或手动位运算,语义清晰,返回 bool 表示是否溢出,并通过输出参数写入结果。
- 必须包含
- 函数签名是
std::add_overflow(a, b, &result),返回true表示溢出,此时result值未定义 - 支持所有有符号/无符号整型,但两个操作数类型需相同,且
result类型必须匹配 - 不改变原值,不引发未定义行为,比手写条件判断更可靠
int a = INT_MAX;
int b = 1;
int result;
if (std::add_overflow(a, b, &result)) {
// 溢出处理:例如抛异常、返回错误码、截断等
} else {
// result == INT_MIN(补码下溢出结果),但这是由函数保证的合法值,非未定义行为
}用编译器内置函数 __builtin_add_overflow(GCC/Clang)
在 C++23 不可用的项目中,这是最常用、高效且被广泛验证的方式。它本质是编译器生成的单条带进位指令检查,零运行时开销。
- 仅 GCC 5+ / Clang 3.4+ 支持,MSVC 不支持(需换用
_addcarry_u32等内联汇编或SafeInt库) - 三个参数:
__builtin_add_overflow(a, b, &result),语义与std::add_overflow一致 - 注意:
a、b、result类型必须完全一致(如全为int),否则编译失败或行为未定义 - 不能用于浮点数,也不能用于类类型;对
char、short会先整型提升,需显式转成int*接收结果
long x = LONG_MAX;
long y = 1L;
long sum;
if (__builtin_add_overflow(x, y, &sum)) {
// 处理溢出
}手动检查加法溢出的边界条件(可移植但易错)
对有符号整数,不能只看结果值——因为溢出后行为是未定义的,任何基于 a + b 的比较都可能被编译器优化掉。正确做法是**在运算前判断**。
- 对于
int加法:a > 0 && b > 0 && a > INT_MAX - b表示正溢出;a 表示负溢出 - 无符号加法更简单:
a + b (利用模运算特性),但前提是a、b是无符号类型,否则隐式转换可能掩盖问题 - 常见错误:写成
if (a + b > INT_MAX)—— 这段代码本身触发未定义行为,编译器可能直接删掉整个分支 - 模板封装时要注意类型推导:用
std::numeric_limits替代硬编码常量,但需确保::max() T是整型且非bool
减法和乘法的溢出检查要点
减法可统一转为加法检查(a - b → a + (-b)),但要注意 INT_MIN - (-1) 这类情况:对有符号数,-INT_MIN 本身溢出,所以不能直接算 -b 再传给加法检查函数。
立即学习“C++免费学习笔记(深入)”;
- 推荐仍用
std::sub_overflow或__builtin_sub_overflow,它们内部已处理该 corner case - 乘法没有标准库函数(C++23 也未加入),
__builtin_mul_overflow是 GCC/Clang 提供的对应版本,用法一致 - 若只能手写乘法检查,需分四象限讨论符号,并用除法反向验证(如
b != 0 && a > INT_MAX / b),但要小心除零和舍入误差 - 所有检查都应在同一类型宽度下进行,避免中间计算因类型提升引入新溢出(例如
int8_t * int8_t结果应存入int16_t再判断)
实际工程中,最容易被忽略的是:溢出检查本身不能成为性能瓶颈,也不应掩盖真正的逻辑错误。比如在循环计数器中频繁调用检查函数,不如改用更大类型(int64_t)或静态断言约束输入范围。而一旦选择检查,就必须覆盖所有路径——尤其是混合类型运算、模板实例化、以及从用户输入直接参与算术的边界点。











