整型数据存储
int main()
{
int a = 20;
int b = -10;
return 0;
}
第一张图片是内存中a的存储方式,第二张图片是内存中b的存储方式。计算机是如何存储的呢?它是按照什么样的规律来存储的呢?
原码 反码 补码
计算机中的整型数据有无符号整数和有符号整数。无符号整数:所有二进制位都用来表示数值。有符号整数:第一个二进制位用来表示符号,后面的二进制位表示数值。0表示正数,1表示负数。
无符号整数:原码,反码和补码都是相同的。
有符号整数:对于正数,原码,反码和补码都是相同的。对于负数,原码,反码和补码各自有计算的方式。
原码: 将十进制数正常转换为二进制,正数为0,负数为1的设置符号位,就是原码。
反码: 将原码,除符号位以为,其余全部取反。
补码: 将反码加1。
int a = 20; //4个字节
a的原码:0 0000000 00000000 00000000 00010100
a的反码:0 0000000 00000000 00000000 00010100
a的补码:0 0000000 00000000 00000000 00010100
a的补码转成十六进制:0X00000014
int b = -10; //4个字节
b的原码:1 0000000 00000000 00000000 00001010
b的反码:1 1111111 11111111 11111111 11110101
b的补码:1 1111111 11111111 11111111 11110110
b的补码转成十六进制:0XFFFFFFF6
总结: 我们可以发现内存是存储的计算机补码的转换形式,所以数值在计算机中以补码的方式存在。
使用补码的原因
为什么要使用补码?计算机只有加法器可以进行加法运算,补码可以使加法和减法都使用加法得出结果。
int sum = a+b; //20 - 10 = 20 + (-10) = 10
//假如使用原码进行运算
a的原码: 0 0000000 00000000 00000000 00010100
b的原码: 1 0000000 00000000 00000000 00001010
a+b的原码:1 0000000 00000000 00000000 00011110
转成十进制:-30 错误
a的补码: 0 0000000 00000000 00000000 00010100
b的补码: 1 1111111 11111111 11111111 11110110
a+b的补码:0 0000000 00000000 00000000 00001010
转成十进制:10 正确
大端小端
细心地网友会发现a的十六进制我们转换出来明明是 00 00 00 14 ,但在IDE展示中是这样的 。两个完全是反过来的,这是因为数据的存储有大端字节序存储模式和小端字节序存储模式。
大端字节序存储模式: 数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中。
小端字节序存储模式: 数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中。
就以00 00 00 14 为例
//写一段代码,告诉我们机器是使用的大端字节序还是小端字节序
int check_sys()
{
//a(0X00 00 00 01)存储到内存中,如果是大端应该是00 00 00 01,
//如果是小端则是01 00 00 00,我们只需要判断第一个字节是00还是01就可以确定
//机器是大端还是小端。
int a = 1;
return *(char *)&a;
}
int main()
{
int ret = check_sys();
if(ret)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
字符型数据存储
int main()
{
char a = -1;
signed char = -1;
unsigned char = -1;
printf("a = %d, b = %d, c = %d\n",a,b,c);
return 0;
}
这段程序最后的输出结果是什么呢?
这个结果是怎么来的呢?
char a = -1;
-1是一个整数,所以它应该是四个字节的,我们写出-1的二进制形式
-1的原码:10000000 00000000 00000000 00000001
-1的反码:11111111 11111111 11111111 11111110
-1的补码:11111111 11111111 11111111 11111111
char是一个字节八个比特位的,并且char是默认的有符号字符类型
把-1放到一个字符型变量里面,这个变量a只能存储一个字节,所以放到a里面的是
a的补码:11111111
%d是打印有符号十进制的整数,既然要打印整数就要将char型的a提升为int型,
8位的char型要在前面补24个0/1变成int型。char是有符号的,最高位的1是符号位,
其他的高位就补1
则a的补码就变成 11111111 11111111 11111111 11111111,倒回去计算出原码,
a的反码 11111111 11111111 11111111 11111110
a的原码 10000000 00000000 00000000 00000001
将a的原码转成十进制就是 -1,a的打印结果就是-1.
signed char b = -1;跟char a = -1;是一样的,计算过程相同
unsigned char c = -1;
这里同样是-1放进一个字符型变量,所以最后放进c的也是 11111111
打印成整型,所以将char型的c进行整型提升为int型,unsigned char是无符号的,
前面补0
c的补码:00000000 00000000 00000000 11111111
c的反码:00000000 00000000 00000000 11111111
c的原码:00000000 00000000 00000000 11111111
转成十进制255
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
程序的输出结果是
char a = -128;
-128的原码:10000000 00000000 00000000 10000000
-128的反码:11111111 11111111 11111111 01111111
-128的补码:11111111 11111111 11111111 10000000
把-128放到字符型变量a中,就只能存一个字节的数据,所以a中的数值是
a的补码:10000000
%u是打印无符号十进制整数,将a整型提升为int类型的,char是有符号的,
所以二进制为补1
a整型提升后补码:11111111 11111111 11111111 10000000
然后根据补码算出原码,进行转换就是最后的结果。
此时要注意程序让进行无符号打印,而无符号的原反补码是相同的,所以
11111111 11111111 11111111 10000000就是原码,将它转换为10进制
就是最终结果。
按照列举的方式来查看无符号char和有符号char的范围
有符号char范围:-128~127 10000000->-128 10000000通过减一取反得到的原码不能转换为-128,但计算机直接规定10000000就是-128
无符号char范围:0~255