尽管我们不会故意给无符号对象赋一个负值,却可能写出这样的代码。例如,当一个算术表达式既有无符号数又有int
值时,那个int
值就会转换成无符号数。把int
转换成无符号数的过程和把int
直接赋给无符号变量一样:
#include <iostream>int main() {unsigned u = 10, u2 = 42;std::cout << "u2 - u =" << u2 - u << std::endl;std::cout << "u - u2 =" << u - u2 << std::endl;int i = 10, i2 = 42;std::cout << "i2 - i =" << i2 - i << std::endl;std::cout << "i - i2 =" << i - i2 << std::endl;u = 42;i = 10;std::cout << "i - u =" << i - u << std::endl;std::cout << "u - i =" << u - i << std::endl;u = 10;i = -42;std::cout << "i - i =" << i + i << std::endl; // prints -84std::cout << "u - i =" << u + i << std::endl; // if 32-bit ints, prints 4294967264i = 10;std::cout << "good" << std::endl;while (i >= 0) {std::cout << i << " ";--i;}std::cout << std::endl;for (int i = 10; i >= 0; --i)std::cout << i << " ";std::cout << std::endl;for (unsigned u = 0; u <= 10; ++u)std::cout << u << " "; // prints 0 . . . 10std::cout << std::endl;/* NOTE: the condition in the following loopwill run indefinitely// WRONG: u can never be less than 0; the condition will always succeedfor (unsigned u = 10; u >= 0; --u)std::cout << u << std::endl;
*/u = 11; // start the loop one past the first element we want to printwhile (u > 0) {--u; // decrement first, so that the last iteration will print 0std::cout << u << " ";}std::cout << std::endl;// be wary of comparing ints and unsignedu = 10;i = -42;if (i < u) // false: i is converted to unsignedstd::cout << i << std::endl;elsestd::cout << u << std::endl; // prints 10u = 42;u2 = 10;std::cout << "u - u2 =" << u - u2 << std::endl; // ok: result is 32std::cout << "u2 - u =" << u2 - u << std::endl; // ok: but the result will wrap around
}
输出:
u2 - u =32
u - u2 =4294967264
i2 - i =32
i - i2 =-32
i - u =4294967264
u - i =32
i - i =-84
u - i =4294967264
good
10 9 8 7 6 5 4 3 2 1 0
10 9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1 0
10
u - u2 =32
u2 - u =4294967264
有符号数与无符号数的转换规则
-
隐式转换:当有符号数与无符号数一起使用时,有符号数会被隐式转换为无符号数。这可能导致负数转换为一个很大的正数。
int a = -1; unsigned b = 1; if (a < b) {// 实际上a被转换为无符号数,变成了一个很大的正数 }
-
显式转换:可以使用类型转换运算符显式地将一个有符号数转换为无符号数,或反之。需要注意的是,这样的转换可能会导致意料之外的结果。
int a = -42; unsigned b = static_cast<unsigned>(a); // b现在是一个很大的正数
无符号数的特性
- 零或正数:无符号数总是表示零或正数,没有负数。
- 溢出行为:当无符号数的值超出其表示范围时,它会绕回到零。例如,假设
unsigned int
的最大值为4294967295
,如果再加1,就会变成0。unsigned int max = 4294967295; max += 1; // 结果是0
有符号数与无符号数运算中的常见问题
-
混合运算:在有符号数和无符号数混合运算时,结果可能不符合预期。
unsigned u = 10; int i = -3; std::cout << u + i << std::endl; // i被转换为无符号数,导致结果出乎意料
-
循环控制:在控制循环的条件中,如果使用无符号数作为循环变量,要特别小心,因为它永远不会小于0。
for (unsigned u = 10; u >= 0; --u) {std::cout << u << std::endl; // 无限循环 }
C++标准中的相关定义
-
整数提升:当执行二元运算时,C++标准要求对操作数进行整数提升。例如,有符号
char
、short
等会被提升为int
,无符号的会被提升为unsigned int
。char c = 'a'; int result = c + 1; // 'a'被提升为int,然后再进行加法运算
-
算术转换:在进行二元运算时,如果两个操作数类型不同,C++会进行算术转换,将较低等级的类型转换为较高等级的类型。
unsigned u = 42; int i = -42; std::cout << i + u << std::endl; // i被转换为无符号数
安全使用建议
- 尽量避免混合使用:尽量避免将有符号数和无符号数混合使用,以减少潜在的错误。
- 检查范围:在进行转换或运算前,检查数值是否在目标类型的表示范围内。
- 使用静态分析工具:使用静态分析工具检查代码中的潜在问题,例如混合运算带来的隐患。