
3.5 各种数据类型的转换
在C语言中整型数据(包括int、short和long)和实型数据(包括float和double)都是数值型数据,字符型数据可以与整型数据通用,因此整型、实型、字符型数据之间可以执行混合运算。例如,62+′a′-15*2.4是一个合法的混合运算表达式。
在运算时不同类型的数据要先转换成同一类型。
C语言的数据类型转换可以归纳成3种转换方式,即自动转换、赋值转换和强制转换。
3.5.1 自动转换
数据类型的自动转换规则如图3-7所示。

图3-7 数据类型的自动转换规则
说明如下。
(1)自动转换按数据长度增加的方向进行,以保证精度不降低。
若两种类型的字节数不同,转换成字节数高的类型;若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型。
(2)图3-7中横向向左的箭头表示必须完成的转换,即表达式中的char型或short型数据一律先自动转换成 int型;float型数据一律先自动转换为double型。
(3)图3-7中纵向向上的箭头表示当运算对象为不同类型时的转换方向遵循由低级转换向高级转换的原则,即在任何涉及两种数据类型的操作中等级较低的类型会被转换成等级较高的类型。
注意:不要理解成int型先转换成unsigned型,再转换成long型,最后转换成double型。
如果一个int型数据和一个double型数据一起运算,则直接将 int型转换成double型计算,结果为double型;同理,一个int型与一个long型数据一起运算,先将int型数据直接转换成long型后再计算,结果为long型。
这里介绍的是一般算术转换,这种类型转换是系统自动进行的。
例如,有如下算术表达式:

其中u为unsigned型,f为float型,s为short型表达式的处理步骤如下。
(1)将′a′和s转换成int型,将f转换为double型。
(2)计算f *s,因f已经转为double型,所以将s转换为double型,结果也为double型。
(3)计算100+′a′,因′a′已转换为int型,所以结果为197(int型)。
(4)计算197+u,将197转换为unsigned型,结果为unsigned型。
(5)计算(197+u)-(f *s),由于f *s为double型,所以将上一步结果转换为double型,因而整个表达式的计算结果为double型。
由此可知算术表达式中只要存在实型数据,则表达式的计算结果一定为double型。
3.5.2 赋值转换
在赋值运算中当赋值运算符两边的操作数类型不同时,将发生类型转换。转换的规则是把赋值运算符右侧表达式的类型转换为左侧变量的类型,赋值时的类型转换也是系统自动进行的。具体的转换如下。
(1)实型与整型之间的赋值转换。
· 将实型数据赋值给整型变量时将舍弃实型数据的小数部分,只保留整数部分。
例如,已知a是int型变量。若有a=3.54,则a中存储的数据将是3,小数部分被舍弃。
注意:如果实型数据的整数部分超过了整型变量的取值范围,则会发生溢出。
· 将整型数据赋值给实型变量时数值不变,但是会将该数值以浮点数形式存储到变量中。例如,已知b是float型变量,若有b=78,则先将78转换为78.000 000,再存储到变量b中。
(2)float型与double型之间的赋值转换。
· float型数据赋值给double型变量时数值不变,只是在float型数据尾部加0延长为double型数值参加运算,然后直接赋值。有效数字从7位扩展到16位,占用内存从4个字节扩展到8个字节。
· double型数据赋值给float型变量时,截取其前面7位有效数字并存放到float型变量的存储单元(4个字节)中,截断前要进行四舍五入操作。
例如,已知f是float型变量。若有f=8.2 345 678 234 598,则f中实际存放的数据是8.23 457。需要注意的是如果要赋值的 double 型数据超出了float型变量的范围,则会发生溢出,造成错误的结果。
(3)char型数据赋值给int型变量时将字符的ASCII码赋值给整型变量。
例如,已知x是int型变量。若x=′a′,则赋值后x的值为97。
(4)截断赋值。
将一个占字节多的整型数据赋值给一个占字节少的整型变量或字符变量,以及将int型数值赋值给 char型变量或将 long型数据赋值给 short型变量时,只将其低字节原封不动地送到该变量(即发生截断),高字节部分被舍去。
(5)无符号整数与有符号整数之间的赋值转换。
· 将一个无符号整数赋值给长度相同的有符号整型变量时(如unsigned->int、unsigned long->long、unsigned short->short),按字节原样赋值,内部的存储方式不变。但外部值却可能改变,因此要注意不要超出有符号数整型变量的数值范围;否则会出错。
· 将一个有符号整数赋值给长度相同的无符号整型变量时按字节原样赋值,即内部存储形式不变,但外部表示时总是无符号的(原有的符号位也作为数值位)。
计算机中的数据用补码表示,int型数据的最高位是符号位,为1时表示负值;为0时表示正值。如果一个无符号数的值小于32 768,则最高位为0,赋给int型变量后得到正值。如果无符号数大于等于32 768,则最高位为1,赋给整型变量后就得到一个负整数值;反之,当一个负整数赋给unsigned型变量时,得到的无符号值是一个大于32 768的值。
以上赋值规则看起来比较复杂,其实不同类型的整型数据间的赋值归根到底就是按存储单元中的存储形式直接传送。
从上面可以看出将一个低类型的数据存放到高类型的变量中时,数据不会发生变化。而将高类型的数据存放到低类型变量中时数据的精度有可能降低;同时也可能导致整个运算结果出错,对于这一类转换在程序设计时一定要注意。
C语言这种赋值时的类型转换形式可能会使开发人员认为不精密和不严格,因为系统自动将表达式的值转为赋值运算符左部变量的类型。而转变后数据可能有所不同,不注意时可能产生错误。这确实是个问题,但不应该忘记的是C语言最初是为了替代汇编语言而设计的,所以类型转换比较随意。当然用强制类型转换是一个好习惯,这样至少从程序上可以看出转换的目的。
3.5.3 强制类型转换
C语言也提供了强制类型转换的机制,可以利用其转换运算符将一个表达式转换成所需的类型,其一般格式为:

功能:把表达式的运算结果强制转换成类型说明符所表示的类型。
例如:

注意:无论是自动转换还是强制转换,都不会改变变量原来的类型和值,转换时只不过是得到了某种类型的中间变量。例如,(int)x。如果变量x原来定义为float型,那么执行强制类型运算后得到一个int型的中间变量,其值等于x的整数部分,而变量x本身的类型和值都未改变。