14.3.5 编译器如何处理类型转换
以上几节介绍了在哪些情况下会发生类型转换,并且明确了每种情况下会把什么类型转成什么类型,本节介绍编译器如何处理任意两种类型之间的转换。现在要把一个M位的类型(值为X)转换成一个N位的类型,所有可能的情况如表14.3所示。
表14.3 如何做类型转换
|
待转换的类型< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> |
M > N的情况 |
M == N的情况 |
M < N 的情况 |
|
signed integer to signed integer |
如果X在目标类型的取值范围内则值不变,否则Implementation-defined |
值不变 |
值不变 |
|
unsigned integer to signed integer |
如果X在目标类型的取值范围内则值不变,否则Implementation-defined |
如果X在目标类型的取值范围内则值不变,否则Implementation-defined |
值不变 |
|
signed integer to unsigned integer |
X % 2N |
X % 2N |
X % 2N |
|
unsigned integer to unsigned integer |
X % 2N |
值不变 |
值不变 |
|
floating-point to signed or unsigned integer |
Truncate toward Zero,如果X的整数部分超出目标类型的取值范围则Undefined |
|
signed or unsigned integer to floating- point |
如果X在目标类型的取值范围内则值不变,但有可能损失精度;如果X超出目标类型的取值范围则Undefined |
|
floating-point to floating-point |
如果X在目标类型的取值范围内则值不变,但有可能损失精度;如果X超出目标类型的取值范围则Undefined |
值不变 |
值不变 |
注意表14.3中的"X % 2N",我想表达的意思是"把X加上或者减去2N的整数倍,使结果落入[0, 2N-1]的范围内",当X是负数时运算结果也是正数,即运算结果和除数同号而不是和被除数同号,这不同于C语言中%运算的定义。写程序时不要故意用表14.3中的规则,尤其不要触碰Implementation-defined和Undefined的情况,但程序出错时可以借助表14.3分析错误原因。
下面举几个例子说明表14.3的用法。比如把double型转换成short型,对应表中的"floating-point to signed or unsigned integer",如果原值在(-32769.0, 32768.0)之间,则截掉小数部分得到转换结果,否则产生溢出,结果是Undefined,例如对于short s = 32768.4;这个语句gcc会报警。
比如把int型转换成unsigned short型,对应表中的"signed integer to unsigned integer"。如果原值是正的,则把它除以216取模,其实就是取它的低16位;如果原值是负的,则加上216的整数倍,使结果落在[0, 65535]之间。
比如把int类型转换成short类型,对应表中的"signed integer to signed integer"。如果原值在[-32768, 32767]之间则值不变,否则产生溢出,结果是Implementation-defined,例如对于short s = -32769;这个语句gcc会报警。
最后一个例子,把short型转换成int型,对应表中的"signed integer to signed integer",转换之后应该值不变。那怎么维持值不变呢?是不是在高位补16个0就行了呢?如果原值是-1,用十六进制数表示就是ffff,要转成int型的-1需要变成ffffffff,因此需要在高位补16个1而不是16个0。换句话说,要维持值不变,在高位补1还是补0取决于原来的符号位,这称为符号扩展(Sign Extension)。