数据存储(一)

495 阅读8分钟

整型数据存储

int main()
{
    int a = 20;
    int b = -10;
    return 0;
}

image.png

image.png

第一张图片是内存中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展示中是这样的 image.png。两个完全是反过来的,这是因为数据的存储有大端字节序存储模式和小端字节序存储模式。

大端字节序存储模式: 数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中。
小端字节序存储模式: 数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中。

就以00 00 00 14 为例

image.png image.png

//写一段代码,告诉我们机器是使用的大端字节序还是小端字节序
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;
}

这段程序最后的输出结果是什么呢?

image.png

这个结果是怎么来的呢?

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型要在前面补240/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;
}

程序的输出结果是

image.png

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的范围

image.png

有符号char范围:-128~127 10000000->-128 10000000通过减一取反得到的原码不能转换为-128,但计算机直接规定10000000就是-128

image.png

无符号char范围:0~255