算术转换与整形提升
就在前天,我做了一道这样的题,它使我发现我对数值类型转换这里的知识很肤浅。所以我整理了一下这方面的知识。
下面我做测试的平台是VS 2013 32位环境(小端字节序)。
这是原题:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = s + u;
printf("us = 0x%x\n", us);
us = (char)s + u;
printf("us = 0x%x\n", us);
us = (unsigned char)u + s;
printf("us = 0x%x\n", us);
return 0;
}
算术转换
C语言在处理不同类型数值之间的运算时是有一定的规律的。需要先将两个操作数转换为相同的类型才能进行运算。而类型的转换的规律如下图:
int
unsigned int
long int
unsigned long int
float
double
long double
这里的转换是自动由低向高发生的(当然,强制类型转换是根据用户意愿进行的),例如:int和unsigned int进行运算,编译器会将低级别的int转换为unsigned int再进行计算。而这个转换编译器并没有对它在内存中的值做出改变,而是使用了不同的读取方式,编译器将原本的int的符号位也当成数值大小进行读取。。
而这里没有提到的char和unsigned char,short和unsigned short类型在运算时都会自动为‘升级’为int型再进行运算。也就是我们所说的整形提升
整形提升
相应的,不同的低级别的类型向高级别类型整形提升的过程也是不太一样的。
这里的转换的规律是:
unsigned char在转化时,编译器会将他们当成正数进行转化,在高位补0,补全后就是它的补码
signed char转化时,编译器则会根据他们的正负将他们的高位全部补0或补1,补全后就是它的补码
我们再返回来看开头我遇到的那个题:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = s + u;
printf("us = 0x%x\n", us);
return 0;
}
u是signed char型的,在与类型为unsigned char型的s进行加法运算时需要进行类型提升,提升为int型,
因为在u进行初始化时,将128赋值给了u,而signed char的取值范围是-128 ~ 127,这里的127的原码是0111 1111,加一就是1000 0000,也就是-128,所以其实在u中存放的是-128这个数。-128的补码也是1000 0000
那么在类型提升时,编译器会将它当作负数进行补全,高位补1,它在内存中即是0xffff80
而s是unsigned char型的,在提升时,编译器认为它是正数,所以高位补0,它在内存中为0x000080。
00000000 00000000 00000000 10000000 //s的补码 无符号相当于正数,原反补一样,提升为整型时,高位补0,
11111111 11111111 11111111 10000000 //u提升为整型的补码 u是signed char
+ -----------------------------------
10000000 00000000 00000000 00000000
它俩相加后,结果为:0x80000000,在赋值unsigned short的us时将结果的低位两个字节(16位)截断赋值给us,所以us中保存的是0x0000
再看下面两个语句:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = (char)s + u;
printf("us = 0x%x\n", us);
return 0;
}
这里将s先强制类型转换为signed char型,再进行运算。
而s一旦转换为signed char型,它的最高位就会表示符号位,其表示的值就会变为-128,在提升为整型时与u就有了相同的待遇。
所以这里u和s的补码都为0xffff80,它俩的相加结果如下:
11111111 11111111 11111111 10000000 //u提升为整型的补码 u是signed char
11111111 11111111 11111111 10000000 //s被强制类型转换后,与u有了相同的待遇
+ -----------------------------------
11111111 11111111 11111111 00000000
最终编译器将相加结果截取低位的16位(两个字节)存放到了unsigned short型的us中。这时us中存放的值是0xff00
最后我们再看剩下两条语句:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = (unsigned char)u + s;
printf("us = 0x%x\n", us);
return 0;
}
先将u强制类型转换为unsigned char,此时u的最高位符号位变为数值大小,u的值变为128,进行整型提升时,u被编译器当作正数,高位补0,提升后,u在内存中表示为0x000080
同样的s类型为unsigned char,整型提升后在内存中表示为0x000080,两者相加:
00000000 00000000 00000000 10000000 //u强制类型转换为unsigned char类型后整型提升
00000000 00000000 00000000 10000000 //s作为unsigned char类型的整型提升
+ -----------------------------------
00000000 00000000 00000001 00000000
最终编译器将相加结果截取低位的16位(两个字节)存放到了unsigned short型的us中。这时us中存放的值是0x0100
我们通过在vs2013下运行这段代码查看最终结果如下:
如果我的文章里的解答有问题,希望小伙伴们积极指出