要理解整形提升和类型转换的话首先要理解计算机的原码、反码、补码
1.原码
计算机存储数据是用二进制数存储的。
把一个整数转换成二进制形式就是原码,原码最左边的一位数表示符号位,0为正,1为负
例如:int a = 4
; 那么a的原码就是00000000 00000000 00000000 00000100
例如:int b = -4
; 那么b的原码就是10000000 00000000 00000000 00000100
2.反码
正数的原码、反码、补码相同,负数的补码就是除了符号位
,其他所有位取反
例如:int a = 4
; 那么a的反码就是00000000 00000000 00000000 00000100
例如:int b = -4
; 那么b的反码就是11111111 11111111 11111111 11111011
3.补码
正数的原码、反码、补码相同, 负数的补码就是反码+1
例如:int a = 4
; 那么a的补码就是00000000 00000000 00000000 00000100
例如:int b = -4
; 那么b的补码就是11111111 11111111 11111111 11111100
计算机中的数据都是以补码存储的,如果是正数的话,原码就是其补码,如果是负数的话,就要转换成补码
- 正数的原、反、补码都相同
- 负数的原、反、补码各不相同
4.整型提升和类型转换
整型提升是什么?
在进行表达式运算时
,所有值都要先提升成int类型,因为运算会在CPU相应的运算器件内执行,而CPU的整型运算器的操作数的字节长度一般是4个字节,也就是int类型的字节长度。
所以如果是char或者short类型的变量,在CPU的运算器件中是很难计算的,所以会先整型提升到4个字节再进计算
整型提升是按照数据的符号位来提升的,正数的符号位为0,所以高位补0,负数的符号位为1,所以高位补1
分析以下代码在底层是怎么运行的
例1:
int main(){
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n",c);
return 0;
}
首先把3这个整型赋值给char类型的a
3这个整型的原码是:
00000000 00000000 00000000 00000011
因为正数的原、反、补相同,所以补码也是:
00000000 00000000 00000000 00000011
因为整型变量是4个字节,而字符型变量只有1个字节,所以把整型值赋值给a会发生截断
所以a变量里面存储的就是3的补码的最后八位:
00000011
127这个整型的补码是:
00000000 00000000 00000000 01111111
赋值给b变量后,b变量里存储的就是:
01111111
把 a+b 的值赋值给c,这里在进行表达式运算,而且a和b都是char类型的,都没有达到一个int大小,所以会发生整形提升,a和b都为正数,高位补0
所以就是:
00000000 00000000 00000000 00000011
加上00000000 00000000 00000000 01111111
结果是:00000000 00000000 00000000 10000010
然后赋值给char类型的c会发生截断,只取最后八位,那么c里存储的就是:
10000010
printf("%d\n",c);
%d就是以有符号整型的形式打印
以整型的形式打印,c就会先提升成4个字节,高位补符号位,所以c是:
11111111 11111111 11111111 10000010
- 补码
又因为计算机底层存储的是补码,而我们看到的应该是原码,所以这里输出就要转换成原码输出
反码:
11111111 11111111 11111111 10000001
- 反码
原码:
10000000 00000000 00000000 01111110
- 原码
所以输出的结果就是:-126
例2:
int main(){
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a == 0xb6){
printf("a");
}
if(b == 0xb600){
printf("b");
}
if(c == 0xb6000000){
printf("c");
}
return 0;
}
0x或者0X开头的数据是十六进制的数,b6(16)转换成十进制数就是 11 * 161 + 6 * 160 = 182(10)
182这个整型的补码是:
00000000 00000000 00000000 10110110
赋值给a发生截断,所以a里存储的就是:
10110110
if(a == 0xb6)
,这里在进行表达式运算,会发生整型提升,a的符号位为1,高位补1
所以a是:
11111111 11111111 11111111 10110110
- 补码
转换成原码:
11111111 11111111 11111111 10010101
- 反码
10000000 00000000 00000000 01101010
- 原码
所以if(a == 0xb6)
这里的a就是106,0xb6转换成十进制是182
106 == 182
是错的,所以不会输出a
因为char类型的a不足4个字节,运算时会发生整型提升,值就有可能被改变
b同理,所以b也不会输出
可以看到,a和b都发生了整型提升,但是b6000000一共占用4个字节刚好可以存储在c里,并且在
if(c == 0xb6000000)
比较时,c就是4个字节,所以c不会发生整型提升,所以c里的值就不会改变,所以这里是对的,就会输出c
总结:
有符号char类型的a变量占用1个字节,不足4个字节,在进行运算时会发生整形提升
,值就有可能被改变
有符号short类型的b变量占用2个字节,不足4个字节,在在进行运算时会发生整形提升
,值就有可能被改变
有符号int类型的c变量本来就是一个整型,占用4个字节,所以在进行运算时不会发生整形提升
,值就不会被改变
例3:
int main(){
char c = 1;
printf("%d\n",sizeof(c));
printf("%d\n",sizeof(+c));
printf("%d\n",sizeof(-c));
printf("%d\n",sizeof(!c));
return 0;
}
先看结果
第一个printf没有进行计算,就没有发生整型提升,所以还是1个字节
第二个printf有计算,发生整型提升,所以是4个字节
第三个printf有计算,发生整型提升,所以是4个字节
第四个printf有计算,发生整型提升,所以应该是4个字节,但是因为编译器不同,这里vs编译器输出的是1,gcc编译器会输出4。
以上都是隐式类型转换