《C语言程序设计:问题与求解方法》——3.8节不同类型数据之间的类型转换

简介:

本节书摘来自华章社区《C语言程序设计:问题与求解方法》一书中的第3章,第3.8节不同类型数据之间的类型转换,作者:何 勤,更多章节内容可以访问云栖社区“华章社区”公众号查看

3.8 不同类型数据之间的类型转换
机器语言的算术运算指令比C语言算术表达式的限制更多。为了让计算机执行机器指令中的算术运算,通常不仅要求两个操作数有相同的长度(字节数),而且还要求数据的存储方式也相同。比如同是单精度浮点型数。
在C语言中,最好把同类型的常量值赋给同一类型的变量,或者使用同类型的常量和变量进行算术运算或关系运算。
然而在C语言程序中,允许在表达式中混合使用各种不同类型的数据。在一个表达式中,可以同时出现整型、浮点型、字符型的常量和变量。在这种情况下,C语言编译程序通常需要生成一些附加的指令,在执行一条运算类指令之前,将其中一个操作数的类型转换成另一个操作数的类型。
下面讨论的类型转换都是有符号型的变量和常量之间的类型转换,不涉及无符号的变量(和常量)。这适用于常规的编程应用范围。一些高级语言(比如Java语言),就只有有符号型的常量和变量。
类型转换分为自动类型转换和强制类型转换两种。自动类型转换指的是编译器在将语句(或表达时)翻译成指令时,由编译程序自动进行的类型转换。在以下四种情况中会出现自动类型转换:
1)二元算术运算符或关系运算符两边的操作数类型不一致。
2)赋值运算符两边的操作数类型不一致。
3)函数调用时实际参数与形式参数要求的类型不一致。
4)函数调用结束时返回值的类型与要求的类型不一致。
其中,第3种和第4种类型转换讨论,请参见第7章。
本章以下仅讨论前两种情况下的自动类型转换。
(1)算术或关系表达式中的类型转换
在算术(或者关系)表达式中,出现在二元运算符两侧的运算量可以是不同类型的,这时就涉及类型之间的转换问题。如果没有出现强制类型转换,那么简单的自动类型转换规则分为三种:
1)只要两个操作数中出现了char类型或 short 型,首先都将无条件地自动提升为int型。
2)在两个操作数的类型都不是浮点型的情况下,自动进行了第一种规定的无条件的类型提升之后,按照以下方式对操作数的类型再次进行有条件的提升。这个条件是两个操作数之中有一个数是long型,即将int提升为long型。
举例来说,对于“short n1 ; long num;”,要求计算表达式n1 + num。编译器先将变量 n1自动提升为int型。由于两个操作数中有一个变量num是long型,因此,刚刚提升为int 型的变量n1的值将再次提升为long型。
延伸与拓展:整型类型提升的技术内幕
增加临时存放此数的字节和对这些字节进行符号位的扩展,这是由于计算机是用补码来表示有符号整数的。只有对补码进行符号位的扩展,补码表示的值才不会改变。参见本章提高部分有关补码的讨论。
3)两个操作数都是浮点数或者其中有一个是浮点数的情况下,提升规则如下:
long double (这是C99标准规定的类型)

double

float
也就是说:
如果在一个表达式(或一个二元算数或关系运算符)中出现了long double 型的运算量,则此表达式计算出的最终值是long double型的。
如果一个表达式(或一个二元算数或关系运算符)中出现了double 型的运算量(但没有出现long double型量),则此表达式计算出的最终值是double型的。
如果一个表达式(或一个二元算数或关系运算符)中出现了float 型的运算量(但没有出现double型量或long double型量),则此表达式计算出的最终值是float型的。
如果两个操作数中还有一个int型或 long 型的整型量,那么此整型数就会根据另一个浮点操作数的类型进行转换。
读者还要注意的是:数据类型的转换,并不是在计算表达式的值之前一次性完成的,而是根据规定的运算顺序,逐步进行类型转换的。例如,对于如下表达式:
56.91+'A'*32
是先按照转换规则,将char型的量'A'转变为int型值(第一种类型提升)进行乘法运算;得到一个整数值以后,再按照转换规则转变成double型(这是由于前一个参与运算的常量59.61被系统默认为double型常量),然后再与56.91进行双精度浮点数加法运算。
(2)赋值语句(或赋值表达式)中的类型转换
在赋值语句(或赋值表达式)中,如果赋值运算符右边表达式计算结果的类型与左边变量的类型不一致,则会自动进行类型转换。类型转换的规则很简单:将表达式计算出的值转变成赋值号左边变量的类型。
如果赋值号右边的数据类型的取值范围宽于左边变量类型的取值范围,通常会发生精度的额外丢失或者错误。
例如,如果定义“char d_char ; int d_int ; float d_float ; double d_double ;”,那么以下赋值语句(或赋值表达式)是不会有任何问题的,因为右边的数据类型“不宽于”左边变量类型的取值范围。
d_int = d_char;
d_float = d_int;
d_double = d_float;
但是,如果将以上几条赋值语句(或赋值表达式)中的变量调换位置:
d_char= d_int;
d_int =d_float;
d_float= d_double;
通常都会出现问题。
对于第1条语句“d_char= d_int;”,在d_int的值超过255时,在char只占8位内存的计算机上必然出错(溢出)。即使d_int的取值在128~255之间,也不一定正确。这要看你正在使用的C编译器中,char类型本质上到底是有符号的整数(取值在–128~127之间)还是无符号的整数(取值在0~255之间)。如果是前者,就会出现把一个正整数变成了一个负整数并且存放到变量d_char中的错误。
对于第2条语句“d_int =d_float;”,必然会使d_float的小数部分丢失(不会四舍五入)。更为严重的是,如果d_float的值超出了int类型的取值范围(这或许是很常见的,因为有些C编译器中,int型的最大值是32767),那么变量d_int中的值也必然出错(溢出)。
第3条语句的情况,读者可自行分析。
(3)强制类型转换
如果自动类型转换满足不了需要,则可以要求编译程序进行强制类型转换。强制类型转换是通过使用强制类型转换运算符(由圆括号括住类型名构成)来实现的。其格式为:
(类型名)(被转换的表达式)
其功能是把表达式的运算结果,强制转换成类型名所给定的类型。例如,(float) m 把整型变量m的值强制转换为单精度浮点型。(int)(3.86x–y)把表达式3.86x–y的值强制转换为整型。
在进行强制类型转换时应注意:类型名必须用圆括号括住,不能省略;需进行类型转换的表达式也要加圆括号(单个变量或常量可以不加括号)。例如,如果错把(int)(x–y)写成(int)x–y,则会出现仅仅把x转换成int类型之后,再与y相加的问题。
以下两种情况下要使用强制类型转换:
1)如果担心两个float型量x,y的乘积超出float型的表示范围(即产生溢出),那就要这样书写语句:z=(double )xy;。其中,变量z是double类型,y的类型会自动提升为double型。注意,另一个很类似的语句:z=(double )(xy);是错误的,因为在进行强制类型转换前,x*y的乘积值很可能已经超出了float型的表示范围。
2)如果担心两个int型量i,j的乘积超出int型的表示范围,那就要这样书写语句: k=(long int )i*j;,其中k是long int类型的变量。
强制类型转换如果使用不当,出现的问题与赋值语句中自动类型转换的类似。
注意:无论是强制类型转换或是自动类型转换,都只是为了本次运算的需要而对从变量中取出的值进行的临时性转换,不会改变变量定义时对该变量指定的类型,也不会改变变量所对应内存单元中存放的值。
无符号的变量(和常量)之间的类型转换,与本节讨论的情况完全类似。强烈建议读者在通常应用中不要在表达式中使用无符号的常量与变量。无符号的常量和变量最好局限于应用在系统编程和嵌入式编程方面。所以,读者应尽量避免在同一个表达式中,同时使用有符号和无符号的量。无符号与有符号量之间的类型转换的讨论,请读者参考相关的教科书。

相关文章
|
1月前
|
存储 C语言
C语言变量类型
C语言变量类型
|
25天前
|
编译器 C语言
【C语言】字母转换大小写的三种方法
【C语言】字母转换大小写的三种方法
43 0
|
25天前
|
存储 编译器 程序员
【C语言】整形数据和浮点型数据在内存中的存储
【C语言】整形数据和浮点型数据在内存中的存储
15 0
|
1月前
|
存储 文件存储 C语言
《C语言程序设计》课程设计 -- 火车票票务管理系统
《C语言程序设计》课程设计 -- 火车票票务管理系统
23 1
|
1月前
|
存储 C语言
C语言顺序结构程序设计
C语言顺序结构程序设计
21 0
|
1月前
|
存储 小程序 C语言
【深度剖析数据在内存中的存储】C语言
【深度剖析数据在内存中的存储】C语言
|
1月前
|
存储 C语言
C语言指针类型和空类型详解
C语言指针类型和空类型详解
26 0
|
1月前
|
存储 C语言
C语言——数据输入和输出
C语言——数据输入和输出
12 0
|
1月前
|
存储 小程序 C语言
【C语言进阶】深度剖析数据在内存中的存储
【C语言进阶】深度剖析数据在内存中的存储
|
1月前
|
存储 编译器 C语言
C语言巧用联合体union判定数据的存储格式(大小端)
C语言巧用联合体union判定数据的存储格式(大小端)
12 1