2.1.2 类型转换
对象的类型定义了对象能包含的数据和能参与的运算,其中一种运算被大多数类型支持,就是将对象从一种给定的类型转换(convert)为另一种相关类型。
当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换,在4.11节(第141页)中我们将对类型转换做更详细的介绍。此处,有必要说明给当某种类型的对象强行赋了另一种类型的值时,到底会发生什么。
当我们像下面这样把一种算术类型的值赋给另外一种类型时:
bool b = 42; // b为真
int i = b; // i的值为1
i = 3.14 // i的值为3
double pi = i // pi的值为3.0
unsigned char c = -1; // 假设char占8比特,c的值为255
signed char c2 = 256; // 假设char占8比特,c2的值是未定义的
类型所能表示的值的范围觉得了转换的过程:
●当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true。
●当我们把一个布尔值赋给非布尔类型时,初始值为false则结果为0,初始值为true则结果为1.
●当我们把一个浮点数赋给整数类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
●当我们把一个整数值赋给浮点类型时,小数部分记为0.如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
●当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后所得的余数。例如,8比特大小的unsigned char可以表示0至255区间内的值,如果我们赋了一个区间外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255。
●当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
建议:避免无法预知和依赖于现实环境的行为
无法预知的行为源于编译器无须(有时是不能)检测的错误。即使代码编译通过了,如果程序执行了一条未定义的表达式,仍有可能产生错误。
不幸的是,在某些情况和/或某些编译器下,含有无法预知行为的程序也能正确执行。但是我们却无法保证同样一个程序在别的编译器下能正常工作,甚至已经编译通过的代码再次执行也可能会出错。此外,也不能认为这样的程序对一组输入有效,对另一组输入就一定有效。
程序也应该尽量避免依赖于实现环境的行为。如果我们把int的尺寸看成是一个确定不变的已知值,那么这样的程序就称作不可移植的(nonportable)。当程序移植到别的机器上后,依赖于实现环境的程序就可能发生错误。要从过去的代码中定位这类错误可不是一件轻松愉快的工作。
当在程序的某处使用了一种算术类型的值而其实所需的是另一种类型的值时,编译器同样会执行上述的类型转换。例如,如果我们使用了一个非布尔值作为条件(参见1.4.1节,第10页),那么它会被自动地转换成布尔值,这一做法和把非布尔值赋给布尔变量时的操作完全一样:
int i = 42;
if (i) // if条件的值将为true
i =0;
如果i的值为0,则条件为false;i的所有其他取值(非0)都将使条件为true。
以此类推,如果我们把一个布尔值用在算术表达式里,则它的取值非0即1,所以一般不宜在算术表达式里使用布尔值。
含有无符号类型的表达式
尽管我们不会故意给无符号对象赋一个负值,却可能(特别容易)写出这么做的代码。例如,当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。把int转换成无符号数的过程和吧int直接赋给无符号变量一样:
unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // 输出-84
std::cout << u + i << std::endl; // 如果int占32位,输出4294967264
在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值:
unsigned u1 = 42 u2 = 10;
std::cout << u1 - u2 << std::endl; // 正确:输出32
std::cout << u2 - u1 << std::endl; // 正确:不过,结果是取模后的值
无符号数不会小于0这一事实同样关系到循环的写法。例如,在1.4.1节的练习(第11页)中需要写一个循环,通过控制变量递减的方式把从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所占位数而定。在我们的环境里,结果是4294967895。
普通的冒险故事提示您:看后求收藏(卧龙小说网http://www.wolongxs.com),接着再看更方便。
好书推荐:《我的剧本世界在自主运行》、《我是舰娘》、《交错世界之学院都市》、《认清现实后,她们开始追夫火葬场》、《好徒儿你就饶了为师伐》、《剑来》、《带着修真界仙子们天下无敌》、《修炼成仙的我只想养成女徒弟》、《足控勇者的目标是魔王的丝袜》、《被触手怪养大的少女》、