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类型的变量。
强制类型转换如果使用不当,出现的问题与赋值语句中自动类型转换的类似。
注意:无论是强制类型转换或是自动类型转换,都只是为了本次运算的需要而对从变量中取出的值进行的临时性转换,不会改变变量定义时对该变量指定的类型,也不会改变变量所对应内存单元中存放的值。
无符号的变量(和常量)之间的类型转换,与本节讨论的情况完全类似。强烈建议读者在通常应用中不要在表达式中使用无符号的常量与变量。无符号的常量和变量最好局限于应用在系统编程和嵌入式编程方面。所以,读者应尽量避免在同一个表达式中,同时使用有符号和无符号的量。无符号与有符号量之间的类型转换的讨论,请读者参考相关的教科书。
《C语言程序设计:问题与求解方法》——3.8节不同类型数据之间的类型转换
时间: 2025-01-19 20:17:09
《C语言程序设计:问题与求解方法》——3.8节不同类型数据之间的类型转换的相关文章
《C语言程序设计:问题与求解方法》——导读
目 录 第0章 "理想厨房"的工作原理0.1 理想厨房系统0.2 理想厨房系统的一个炒菜实例0.3 "理想厨房"工作的重要特点0.4 理想厨房系统与计算机系统术语对照表本章习题第1章 计算机的基本工作原理1.1 二进制简介1.2 计算机系统1.3 提高部分本章习题第2章 C语言程序结构和基本语法要素2.1 高级程序设计语言和编译程序简介2.2 C语言历史概述2.3 C语言源程序的主要构成成分:函数定义2.4 C语言源程序的次要组成成分:编译预处理命令.注释和声明2.
《C语言程序设计:问题与求解方法》——3.3节逐步求精的伪代码
3.3 逐步求精的伪代码 表示问题求解的算法有多种方法,其中最常用的是伪代码和流程图.流程图只适合解决规模较小的.比较简单的问题,虽然初学者易于理解和掌握,表达算法也比较清晰,但程序员和真正会编程者并不常使用这种方法-因为它不利于对算法的构思.修改和调整(仅适用于表达比较简单的算法),所以本书不作介绍. 本书将使用逐步求精的伪代码来表示算法.所谓伪代码没有严格的规范(所以也比较灵活),但其中有一些结构要素,可以用一些比较规范的方式来表述对问题的计算和处理流程. 一级算法只是解决问题的一个轮廓.对
《C语言程序设计:问题与求解方法》——2.10节变量
2.10 变量一些动态变化的量(比如车速.温度.股票价格等)称为变量,这些量在源程序中用常量无法表示.用C语言进行编程,要使用数据区(数据值可变)而不是代码区(数据值不允许变)的内存单元来存放数据,都必须向编译程序提出申请.在C语言源程序中,向编译程序申请一个(或几个)存放某种类型数据的.值的大小可以变化的内存单元称为定义变量. 2.10.1 变量的定义定义一个简单变量的格式为:类型名 变量名;类型名要使用关键字(比如int.float.char等,参见2.7节),变量名必须使用标识符.通过这种
《C语言程序设计:问题与求解方法》——3.11节本章习题
本章习题 一.选择题 1. ( )是C语言提供的合法的数据类型关键字. A. Float B. signed C. integer D. Char 2. 属于合法的C语言长整型常量的是( ). A.5876273 B. 0L C.2E10 D.(long)5876273 3. 判断int x = 0xaffbc:x的结果是( ). A.赋值非法 B.不确定 C.affb D.ffbc 4. 下面选项中,均是合法浮点数的是( ). A.+1e+1 B.-.60 C.123e D.-e3 5e-9.
C++实现二叉树遍历序列的求解方法_C 语言
本文详细讲述了C++实现二叉树遍历序列的求解方法,对于数据结构与算法的学习有着很好的参考借鉴价值.具体分析如下: 一.由遍历序列构造二叉树 如上图所示为一个二叉树,可知它的遍历序列分别为: 先序遍历:ABDECFG 中序遍历:DBEAFCG 后序遍历:DEBFGCA 我们需要知道的是,由二叉树的先序序列和中序序列可以唯一地确定一棵二叉树:由
c语言-请问怎么学好《C语言程序设计教程》
问题描述 请问怎么学好<C语言程序设计教程> 这本书下学期就要学了,可是看着迷迷糊糊,看不懂,有没有什么学习的捷径?什么配套的辅导书比较好?谢谢! 解决方案 简单来说,就是Reading+Coding. 阅读入门可以从谭浩强的<C程序设计>开始,国人所写比较符合我们的思维习惯,但是过于基础,许多细节问题没有讲到,所以看完那本可以接着看看<C语言深度解剖>. 另外还有两本国外的C语言经典著作<C程序设计语言><数据结构与算法分析--C语言描述>,可
C语言学习教程第三章-C语言程序设计初步(1)
C语言程序设计 本课介绍C语言程序设计的基本方法和基本的程序语句.从程序流程的角度来看,程序可以分为三种基本结构, 即顺序结构.分支结构.循环结构. 这三种基本结构可以组成所有的各种复杂程序.C语言提供了多种语句来实现这些程序结构. 本章介绍这些基本语句及其应用,使读者对C程序有一个初步的认识, 为后面各章的学习打下基础. C程序的语句 C程序的执行部分是由语句组成的. 程序的功能也是由执行语句实现的.C语句可分为以下五类:1.表达式语句2.函数调用语句3.控制语句4.复合语句5.空语句 1.表
《C语言程序设计与实践(第2版)》——导读
前言 C语言程序设计是一门理论与工程实践密切相关的专业基础课程,在计算机学科教学中具有十分重要的地位.大力加强该课程的建设,提高该课程的教学质量,有利于教学改革和教育创新,有利于创新人才的培养.通过本课程的学习,学生应培养良好的编程风格,掌握常见的算法思路,真正提高运用C语言编写程序解决实际问题的综合能力,为后续课程的实践环节打好基础. 目前国内关于C语言的教材较多,有些教材语法知识介绍细致,较适合作为非专业的等级考试类教学用书:有些教材起点较高,内容深奥,不适于初学者.为了帮助广大学生更好地掌
《C语言程序设计》一 2.3 运算符和表达式
2.3 运算符和表达式 在前面的例子程序中已经多次用到基本的运算.运算符是表示某种操作的符号,操作的对象叫操作数,用运算符把操作数连接起来形成一个有意义的式子叫表达式.C语言为了加强对数据的表达.处理和操作能力,提供了大量的运算符和丰富的表达式类型,其中最常用的是算术运算符和赋值运算符. 2.3.1 算术运算符与算术表达式 算术运算符分为一元算术运算符和二元算术运算符两类,一元算术运算符只需要一个操作数,放在运算符的后面,二元算术运算符需要两个操作数,操作数写在运算符两边. 1.C语言提供的算术