类型转换过程中存在大量潜在的危险需要加以避免:
1) 数值的丢失:转化后的类型其数值量级不能被体现
2) 符号的丢失:从有符号类型转换为无符号类型会导致符号的丢失
3) 精度的丢失:从浮点类型转换为整型会导致精度的丢失
对于所有数据和所有可能的兼容性实现来说,唯一可以确保为安全的类型转换是:
1) 整数值进行带符号的转换到更宽类型
2) 浮点类型转换到更宽的浮点类型
当然,在实践中,如果假定了典型类型的大小,也能够把其他类型转换归类为安全的。
普遍来说,采取的原则是,利用显式的转换来辨识潜藏的危险类型转换。
类型转换中还有其他的一些危险需要认清。这些问题产生于C 语言的难度和误解,而不
是由于数据值不能保留。
1. 整数提升中的类型放宽:整数表达式运算的类型依赖于经过整数提升后的操作数的类型。总是能够把两个8 位数据相乘并在有量级需要时访问 16 位的结果。有时而不总是能够把两个16 位数相乘并得到一个 32 位结果。这是 C 语言中比较危险的不一致性,为了避免混淆,安全的做法是不要依赖由整数提升所提供的类型放宽。
考虑如下例子:
INT16U u16a = 40000; /* unsigned short / unsigned int */
INT16U u16b = 30000; /* unsignedshort / unsigned int */
INT32U u32x; /* unsigned int / unsigned long */
u32x = u16a + u16b; /*u32x = 70000 or 4464 ? */
期望的结果是70000,但是赋给 u 的值在实际中依赖于 int 实现的大小。如果 int 实现的大小是32 位,那么加法就会在有符号的 32 位数值上运算并且保存下正确的值。如果 int 实现的大小仅是16 位,那么加法会在无符号的 16 位数值上进行,于是会发生折叠(wraparound )现象并产生值4464(70000%65536 )。无符号数值的折叠(wraparound)是经过良好定义的甚至是有意的;但也会存在潜藏的混淆。
2. 类型计算的混淆:程序员中常见的概念混乱也会产生类似的问题,人们经常会以为参与运算的类型在某种方式上受到被赋值或转换的结果类型的影响。例如,在下面的代码中,两个16 位对象进行 16 位的加法运算(除非被提升为 32 位int),其结果在赋值时被转换为INT32U 类型。
u32x = u16a + u16b;
并非少见的是,程序员会认为此表达式执行的是32 位加法——因为 u32x 的类型。
对这种特性的混淆不只局限于整数运算或隐式转换,下面的例子描述了在某些语句中,
结果是良好定义的但运算并不会按照程序员设想的那样进行。
u32a = (INT32U_t) (u16a * u16b);
f64a = u16a / u16b ;
f32a = (float32_t) (u16a / u16b) ;
f64a = f32a + f32b ;
f64a = (float64_t) (f32a + f32b) ;
3. 数学运算中符号的改变:整数提升经常会导致两个无符号的操作数产生一个(signed )int类型的结果。比如,如果 int 是32位的,那么两个 16 位无符号数的加法将产生一个有符号的32 位结果;而如果 int 是16 位的,那么同样运算会产生一个无符号的 16 位结果。
4. 位运算中符号的改变:当位运算符应用在无符号短整型时,整数提升会有某些特别不利
的反响。比如,在一个unsigned char 类型的操作数上做位补运算通常会产生其值为负的(signed )int 类型结果。在运算之前,操作数被提升为 int 类型,并且多出来的那些高位被补运算置1。那些多余位的个数,若有的话,依赖于int 的大小,而且在补运算后接右移运算是危险的。
为了避免上述问题产生的危险,重要的是要建立一些准则以限制构建表达式的方式。这里首先给出某些概念的定义。