CppCoreGuidelines ES.106 避免赋值负数给无符号类型
27 May 2023
“Don’t try to avoid negative values by using unsigned
”
理由
选择无符号类型意味着很多整型的行为将被改变,包括求模运算、抑制针对溢出的相关警告、产生有符号无符号类型混用的错误等。使用无符号类型并不能避免产生负数值的可能。
例子
unsigned int u1 = -2; // Valid: the value of u1 is 4294967294 cout << "u1 = " << u1 << "\n"; int i1 = -2; cout << "i1 = " << i1 << "\n"; unsigned int u2 = i1; // Valid: the value of u2 is 4294967294 cout << "u2 = " << u2 << "\n"; int i2 = u2; // Valid: the value of i2 is -2 cout << "i2 = " << i2 << "\n";
u1 = 4294967294 i1 = -2 u2 = 4294967294 i2 = -2
此类数值构造过程很合法,却很难发现实际代码中的问题。是现实世界大部分问题的来源。
unsigned area(unsigned height, unsigned width) { return height*width; } // ... int main() { int height; height = 2; auto a = area(height, 2); // if the input is -2 a becomes 4294967292 cout << a << '\n'; auto b = area(height, -2); cout << b << '\n'; }
4 4294967292
记住,-1 赋值给无符号整型时,变成了一个最大的无符号整型。并且,因为无符号计算是求模计算,乘法不会导致向上溢出,只会截去高位,变回小一些的数。
例子
unsigned max = 100000; // "accidental typo", I mean to say 10'000 unsigned short x = 100; while (x < max) x += 100; // infinite loop
如果 x 是一个有符号的 short 类型,编译器会产生关于未定义的溢出警告。
其他方案
- 使用有符号整型,并检查
x >= 0
- 使用一个正整数
- 使用整数类型的某个局部范围
assert(-1 < x)
例子
struct Positive { int val; Positive(int x) :val{x} { Assert(0 < x); } operator int() { return val; } }; int f(Positive arg) { return arg; } int r1 = f(2); int r2 = f(-2); // throws