问题现象
先看一段看似人畜无害的代码:
cpp
#include <iostream>
int main() {
unsigned a{ 10 };
int b{ -1 };
auto res = a * b;
std::cout << "a*b=" << res << std::endl;
return 0;
}
输出不是 -10,而是 4294967286。是不是开始怀疑编译器了?别急,这不是 Bug,而是 C++ 类型转换规则在你眼皮底下悄悄偷换了概念。
原因剖析
当有符号和无符号整数参与同一次算术运算时,C++ 会执行算术转换:
- 若两者的秩(rank)相同 ,且其中一个为无符号、另一个为有符号,则有符号类型会转换为无符号类型。
回到上面的例子:b = -1 先被转换为 unsigned int。在 32 位补码体系下,-1 的二进制为 11111111 11111111 11111111 11111111,解释成无符号整数就是 4294967295 。这个数乘以 10 必然溢出,最终结果按模2322^{32}232取余,得到 4294967286。
v.size() - 1 的经典翻车现场
更隐蔽的坑藏在日常代码里。C++ 标准库容器的 size() 返回的是无符号整数 。如果容器为空,v.size() 返回 0,而 0 - 1 会按照上述规则得到一个巨大的正整数(例如 SIZE_MAX),导致循环执行约 2^64 次,直接卡死程序。
cpp
for (size_t i = 0; i < v.size() - 1; i++)
// 实际业务
正确的做法是提前判断容器是否为空,或者直接把 v.size() 转为有符号类型(在确保不会溢出的前提下)。
总结规律:记住这两条,避开所有同类坑
两个不同类型的操作数做算术运算时,编译器会先执行类型转换,再计算:
- 秩不相等:低秩类型 → 高秩类型。
- 秩相等且符号不同:无符号类型若能容纳有符号类型的全部取值,则有符号 → 无符号。
附常见整型的秩排序(由高到低):
bash
long long == unsigned long long > long == unsigned long > int == unsigned int > short == unsigned short > char == unsigned char
下次看到 -1 与无符号数一起出现时,记得多留个心眼。