有关数据位的提升的隐式转换从下面的这三道道笔试题开始:
signed char a = 0xe0; unsigned int b = a; unsigned char c = a;
signed char a = 0xe0; if(a == 0xe0) { printf("haha"); } else { printf("nono"); }
unsigned int a = 6; int b = -20; (a + b > 6) ? printf(">6") : printf("<=6");
首先明确一点,计算机内存内部位的运算操作都是很简单的小学生运算,硬件的设计是越简单越好,具体这个数是什么,要看你解读这个数的方式。一个很好的例子就是位操作数据溢出的结果这篇博文中的介绍。
首先来看第一道题目:
首先任何的常量整数都是int类型的,这是编译器默认的,所以0xe0会被表示为0x00 00 00 e0(32位),赋值给signed char a时,因为char是8位的,所以只取低8位e0赋值过去,这时的赋值仅仅是位数据的拷贝,没有任何的符号等考量在内。所以a得到的数据是e0(1110 0000),这是a把这些二进制当做signed char解释,所以得到的结果是一个负值。
unsigned int b = a这句话是把a的值负值给一个int,那么要扩展,扩展的方式是跟最高位的位相同,所以b被扩展为0xff ff ff e0,但是b的解读方式是unsigned int,所以会得到一个很大的数字。
注意:0xe0这个常量整数直接写,编译器把他认为是0x00 00 00 e0,这里不是扩展,不涉及类型的提升,就是默认的位int类型。
unsigned char c = a把a赋值给一个char类型的变量,这里就是直接的把8位二进制原封不动的拷贝过去,但是c得到这些数据不再按signed char解释,而是按照unsigned char类型解释,所以c是一个很大的整数。
再来看第二道题目:
0xe0直接被默认是0x00 00 00 e0,默认为一个正的int类型。赋值给a的时候,截取最后面的8位0xe0赋值给a,但是a拿到这8位数据以后当做signed char来解释。所以是一个负数,所以a是不等于0xe0的。在真正比较的时候a会做位的提升,因为它在和一个int类型的值比较,所以a会提升为0xff ff ff e0,当然不等于0x00 00 00 e0了。
最后来看第三道题目:
有符号数和无符号数在一起运算,不要以为遵循的仍然是短的向长的靠齐。以前我们知道,如果一个short和一个int在一起运算,首先编译器会将short自动转换为一个int类型中间变量,然后进行运算。但是有符号数和无符号数在一起运算的话,并不是这样。
首先说说符号数和无符号数之间的转换。无符号数可以转换为有符号数,有符号数也可以转化为无符号整形,这之间的转换一般编译器也不会给出警告,因为这种转换不涉及到bit丢失的情况,内存内容不会发生任何改变。仅仅是对这块内存的解释不一样而已。
int a = -3; unsigned int b = a; // b= 65533 //反之一样
编译器里面有标准的转换,这个是在整形运算的时候出现。
标准转换的规则是:短的的向长的转;有符号的向无符号的转。如果被转换的数据比转换后的数据要长的话,转换可能会丢失bit数据。通常,编译器会给出警告。
无符号数据和有符号整数进行运算,是有符号整数向无符号整数靠齐。这里,我们可以总结一条规则:整形数运算,总是遵循一个原则:小的往大的扩展。
从小到达:short -> unsigned short -> int -> unsigned int。
所以题目中会首先把b转换成一个很大的是,因为负数的最高位是1,转换成无符号以后,这个1很值钱,所以a+b是一个很大的数,所以最后结果:>6。
总结一下:
在数据进行位扩展的时候,首先要确定这个要扩展的数是有符号还是无符号的,然后决定扩展的高位是符号位还是0。
char ch=-1; printf(“%02x,%02x”,ch,(unsigned char)ch);
打印的结果是:ffffffff,ff。
因为%x是十六进制的方式输出整型数据,也就是int,所以两个都要对ch进行高位扩展,但是扩展的方式是不一样的,ch如果是有符号就在高位扩展这个数的符号位,如果ch是无符号的,就直接在高位扩展0。