复习C语言知识时发现了一个非常有趣的程序:
代码如下:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a=-8;
unsigned int b=7;
if(a+b>0){
printf("a+b>0\n");
}else{
printf("a+b<=0\n");
}
printf("%x\n",(a+b));
printf("%d\n",(a+b));
return 0;
}
程序输出结果如下:
看到这里,大家的第一反应肯定和我一样,-8加上7,应该等于-1,按道理输出的是a+b<=0。然而现实总是相反的,我决定花点时间折腾一下,了解清楚这个程序的来龙去脉。
原因如下:
漏了一个知识点:当表达式中存在 有符号数和无符号数时,有符号数会转换成无符号数参加运算,结果也是一个无符号数。程序中的a就是有符号整型,b是无符号整型。
原码:一个整数,按照绝对值的大小转换成二进制数,最高位为符号位。
反码:将原码除最高位(符号位)外,其余各位按位取反,所得到的二进制码。正数的反码和补码都为原码。
补码:反码最低位加1即为补码。
转换过程:由于计算机内部是以补码的形式进行表达的,先把4字节的a变量原码转换为补码
原码 | 二进制表示 | 反码 | 补码 |
---|---|---|---|
-8 | 1000 1000 | 1111 0111 | 1111 1000 |
7 | 0000 0111 | 0000 0111 | 0000 0111 |
unsigned int是32位的无符号整型,范围是0~232 -1,16进制表示为0xFFFF FFFF,所以int类型的a变量转换为unsigned int类型的16进制表示为0xFFFF FFF8,变量b本身就是unsigned int类型,所以表示为0x0000 0007。
a+b相当于 ————> 0xFFFF FFF8+0x0000 0007=0xFFFF FFFF
得到的0xFFFF FFFF是某个无符号的数的补码,反推回去就可以知道这个数(原码)是多少了
补码 | 反码 | 原码 | 十进制数 |
---|---|---|---|
1111 1111 | 0000 0000 | 1111 1111 | 232 -1 |
最终计算机得到的是一个非常大的数,232 -1,所以a+b的值远远大于0,程序输出了a+b>0的结果。
232 -1转换成16进制刚好就是0xFFFFFFFF,和计算机输出的16进制结果一样,至于最后为什么输出的是-1,原因是用%d输出的时候要再把它转换成int,也就是无符号整型转回原来的有符号整型。
0xFFFFFFFF-0x0000 0001=0xFFFFFFFE
符号位不变,取反之后得到原码:1000 0000 0000 0000 0000 0000 0000 0001,转换成十进制就是-1啦。
折腾了一下午,以上只是我个人的理解,如果有错的地方,还请指出!