2.8 混合运算

字符型(char)、整型(包括int、short、long)、浮点型(包括float、double)数据可以混合运算。运算时,不同类型的数据首先要转换为同一类型,然后进行运算。不同类型数据的转换级别如图2.8.1所示,从短字节到长字节的类型转换是由系统自动进行的,编译时不会给出警告;若反向进行,则编译时编译器会给出警告。

img

图2.8.1 不同类型数据的转换级别

2.8.1 数值按int型运算

C语言中的整型数算术运算总是以默认整型类型的精度进行的。为了获得这个精度,表达式中的字符型和短整型操作数在使用之前会被转换为基本整型(int型)操作数,这种转换被称为整型提升(Integral Promotion)。例如,

img

其中,b和c的值首先被提升为基本整型数,然后执行加法运算。加法运算的结果将被截短,然后存放到a中。这个例子的结果和使用8位算术运算的结果相同,但在下面的例2.8.1中,结果不再相同。

【例2.8.1】整型常量默认按int型运算实例。

img

执行结果如图2.8.2所示。

img

图2.8.2 例2.8.1中程序的执行结果

为什么采用十六进制数打印呢?这是因为输出时%x是取4字节进行输出的;b中存储的只有93,为什么前面却打印出了3字节的ff也就是6个ff呢?如果用%d输出,那么可以得到一个负值,当我们用%x输出一个少于4字节的数时,前面补的字节是按照对应数据的最高位来看的,因为字符b的最高位为1,所以其他3字节补的都是1。

为什么把操作分成两步后,b的值就为13呢?因为0x93左移一位时,虽然按4字节进行,但是最低一字节的值为0x26,赋值给b后,b内存储的就是0x26,这时再对b进行右移时,单字节拿到寄存器中是按4字节运算的,但是因为b的最高位为零,因此拿到寄存器中按4字节运算,前面补的都是零,再右移一位表示除以2,因此得到的值是13。

另外一种场景是我们对两个整型常量做乘法,并赋值给一个长整型变量,对编译器来讲这是按照int型进行的。如例2.8.2所示,打印结果为0,无论是在VS中新建Win32控制台应用程序并在32位下执行,还是在Linux下将其编译为64位的可执行程序,执行结果均为0。那么怎么解决这种类型问题呢?我们来看例2.8.2。

【例2.8.2】两个较大常量相乘溢出实例。

img

我们可以在做乘法前,将整型数强制转换为long型,32位控制台应用程序代码如例2.8.3所示,在32位操作系统下,longlong型只占8字节而long型占4字节。

【例2.8.3】32位程序两个较大的常量相乘不会溢出实例。

img

上例代码的输出结果如图2.8.3所示,如果是64位程序,如例2.8.4所示,那么转化为long型即可。

img

图2.8.3 例2.8.3程序的输出结果

【例2.8.4】64位程序两个较大的常量相乘不会溢出实例。

img

2.8.2 浮点型常量默认按double型运算

浮点型常量默认按8字节运算,如例2.8.5所示。

【例2.8.5】浮点型常量运算实例。

img

输出结果如图2.8.4所示。

img

图2.8.4 例2.8.5程序的输出结果

第一个打印的值只有7位精度,原因是单精度浮点数f只有4字节的存储空间,能够表示的精度是6~7位,所以只保证1~7位是正确的,后面的都是近似值。第二个打印的值是正确的浮点型常量,它是按8字节即double型进行运算的,同时%f会访问寄存器8字节的空间进行浮点运算,因此可以正常输出。

思考题:在代码中我们定义了double d=f,请问d输出会是12345678901吗?

2.8.3 类型强制转换场景

整型数进行除法运算时,如果运算结果为小数,那么存储浮点数时一定要进行强制类型转换,否则会出现如图2.8.5所示的结果,f得到的值为2,g得到的值为2.5。

img

图2.8.5 未进行强制类型转换的程序

图2.8.5中程序的编译结果如2.8.6所示,第11行因为将一个整型表达式赋值给浮点数,有丢失精度的可能,所以产生警告。第13行因为未把变量l强制转换为short,在做加法产生警告,如果增加了short强制转换,那么就可以去除第13行的警告。

img

图2.8.6 未进行强制类型转换的程序的编译结果

代码执行结果如图2.8.7所示。

img

图2.8.7 代码执行结果

思考题:

1.假定编写一个程序,把一个long型变量赋值给一个short型变量。编译程序时会发生什么情况?运行程序时会发生什么情况?其他编译器的结果是否也是如此?

2.假定编写一个程序,把一个double型变量赋值给一个float型变量。编译程序时会发生什么情况?运行程序时会发生什么情况?