2.1.2 类型转换(2)
在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值:
- unsigned u1 = 42, u2 = 10;
- std::cout << u1 - u2 << std::endl; // 正确:输出32
- std::cout << u2 - u1 << std::endl; // 正确:不过,结果是取模后的值
无符号数不会小于0这一事实同样关系到循环的写法。例如,在1.4.1节的练习(第13页)中需要写一个循环,通过控制变量递减的方式把从10到0的数字降序输出。这个循环可能类似于下面的形式:
- for (int i = 10; i >= 0; --i)
- std::cout << i << std::endl;
可能你会觉得反正也不打算输出负数,可以用无符号数来重写这个循环。然而,这个不经意的改变却意味着死循环:
- // 错误:变量u永远也不会小于0,循环条件一直成立
- for (unsigned u = 10; u >= 0; --u)
- std::cout << u << std::endl;
来看看当u等于0时发生了什么,这次迭代输出0,然后继续执行for语句里的表达式。表达式 u从u当中减去1,得到的结果 1并不满足无符号数的要求,此时像所有表示范围之外的其他数字一样, 1被自动地转换成一个合法的无符号数。假设int类型占32位,则当u等于0时,--u的结果将会是4294967295。
一种解决的办法是,用while语句来代替for语句,因为前者让我们能够在输出变量之前(而非之后)先减去1:
- unsigned u = 11; // 确定要输出的最大数,从比它大1的数开始
- while (u > 0){
- --u; // 先减1,这样最后一次迭代时就会输出0
- std::cout << u << std::endl;
- }
改写后的循环先执行对循环控制变量减1的操作,这样最后一次迭代时,进入循环的u值为1。此时将其减1,则这次迭代输出的数就是0;下一次再检验循环条件时,u的值等于0而无法再进入循环。因为我们要先做减1的操作,所以初始化u的值应该比要输出的最大值大1。这里,u初始化为11,输出的最大数是10。
提示:切勿混用带符号类型和无符号类型
如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动地转换成无符号数。例如,在一个形如a*b的式子中,如果a = 1,b = 1,而且a和b都是int,则表达式的值显然为 1。然而,如果a是int,而b是unsigned,则结果须视在当前机器上int所占位数而定。在我们的环境里,结果是4294967295。
2.1.2节练习
练习2.3:读程序写结果。
- unsigned u = 10, u2 = 42;
- std::cout << u2 - u << std::endl;
- std::cout << u - u2 << std::endl;
- int i = 10, i2 = 42;
- std::cout << i2 -i<< std::endl;
- std::cout << i - i2<< std::endl;
- std::cout << i - u<< std::endl;
- std::cout << u - i<< std::endl;
练习2.4:编写程序检查你的估计是否正确,如果不正确,请仔细研读本节直到弄明白问题所在。