乘法溢出在运算时即发生,而非赋值阶段;应提前将操作数提升至足够宽类型(如long long)或用__builtin_mul_overflow检测,避免先算后判导致未定义行为。

因为乘法运算本身发生在原始类型范围内,溢出在*执行时就已发生,不是赋值阶段才“产生”的错误结果。
为什么a * b这一步就已经错了
当两个int相乘时,CPU按32位有符号整数规则执行IMUL指令,结果也截断为32位。即使你马上把它赋给size_t或long long,装进去的已经是被截断、错误的值——就像往大桶里倒一杯洒了一半的水,桶再大也补不回漏掉的部分。
- 编译器不会自动把
a * b提升到更宽类型再算;它严格按操作数类型决定运算宽度 -
INT_MAX * 2这种表达式在编译期可能被常量折叠,但若涉及变量,运行时溢出就是未定义行为(UB),优化器甚至可能删掉后续判断逻辑 - Visual Studio 的 C26451 警告正是在提醒:别等赋值后才发现,问题出在乘号那一下
怎么写才不会在*那步就崩
关键是在乘法发生前,就把至少一个操作数提升到足够宽的类型,让中间结果不丢失精度。
- 显式转成
long long:static_cast<long long>(a) * b</long>,再检查是否超出目标类型范围 - 用
__builtin_mul_overflow(GCC/Clang)直接捕获溢出:bool overflowed = __builtin_mul_overflow(a, b, &result) - 对
size_t分配场景,优先用size_t类型接收输入,避免int→size_t隐式转换带来的符号扩展风险 - 别依赖
if (a * b > SOME_LIMIT)做防护——这个a * b本身可能已 UB,条件判断不可靠
常见误判:以为“赋值给大类型=安全”
很多人写size_t total = count * sizeof(int),觉得size_t是64位就万事大吉。但只要count和sizeof(int)都是int,乘法就在32位内完成,溢出早已发生。
- 错误示范:
int count = 3000000000; size_t s = count * 4;→count * 4先溢出,再转size_t - 正确做法:
size_t s = static_cast<size_t>(count) * 4;</size_t>或更稳妥地:if (__builtin_mul_overflow(count, 4, &s)) { /* handle */ } - 注意:无符号乘法虽不触发 UB,但回绕结果(如
UINT_MAX + 1 → 0)在内存分配中等于申请 0 字节,极易导致后续malloc返回nullptr或崩溃
最易被忽略的一点:溢出检测必须落在乘法运算符之前或之内,而不是之后。任何“先算再判断”的逻辑,在有符号场景下都可能被编译器优化掉,或根本得不到预期值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











