关于整形在内存中的存储
在创建变量的时候,是需要在内存中开辟空间的。空间的大小也是根据所创建的类型而定。
比如一个char类型的数据,就是一个字节,int类型的就是4个字节。
这里的一个空格表示一个字节
但是因为在内存中是用二进制来进行储存的,所以在内存中,整数都是换算成二进制来进行计算的
二进制又是0和1来组成的,计算方式是逢二进一。
原码、反码、补码
在计算机中又有三种二进制的表示方法,即原码、反码、补码。
这三种表现方法均有符号位,和数值位两个部分。
符号位是在一个二进制数的最左边那一个数,那一位是0表示正数,是1表示负数。
比如:
10在二进制中是1010,如果是char类型的(char大小是八个比特位)00001010,最左边的是0,所以是正数。
-10在二进制中也是1010,但是最左边的那个数是1,char类型的话就是,10001010。
正数的原码、反码和补码是一样。
比如:
15的原码:00001111
15的反码:00001111
15的补码:00001111
负数的原码
关于负数的原码、反码和补码就不一样了。
如果我们用负数的原码来进行计算,比如-1+1,这里的-1+1我们就用char八个bit位来计算把,int的比较大。
-1 的原码是:10000001
我们来进行加1,二进制逢二进一嘛,那就变成了:10000010
这 10000010 在二进制中因为有符号位的存在所以它是-2,但是-1+1不应该是0嘛,为什么会这样呢?
因为不能解决原码计算负数的问题,所以出现了反码。
负数的反码
反码的计算规则是:负数的反码是在原码的基础上,符号位不变,其他位数值按位取反,0变1,1变0。
然后我们再用负数的反码来计算。比如-5
-5的原码是10000101,转换成反码,符号位不变,其它位按位取反,就得到了:11111010
我们就用-5的反码来加2(正数的原、反、补相同),11111010 + 00000010 = 11111100 这串数字咱们在按位取反一下就得到了:10000011
10000011 计算下是:-3。这样就可以很好的计算负数了。
但是我们如果一直加,反码的-1+1再来计算11111110 + 00000001(正数的原码、反码、补码一样)
反码-1+1就得到了:11111111 再取反一下,符号位不变,其它位按位取反。就得到了:10000000 = “-0”?
咱们再对“-0”的反码进行加1,11111111 + 00000001 就得到了100000000,因为咱们是char类型的8bit位,这个有9个bit位,
多得咱们就丢掉了。所以最终就得到了00000000。
但是这样不行啊,有-0 和 0 如果计算起来不就会多一个,计算的结果不就有1的误差了嘛。
因为有反码有-0 和 0 之间的误差,所以出现了补码。
负数的补码
补码:正数的补码不变,负数的补码是在反码的基础上加1。
在计算机中的存储和计算都是用补码来进行的。
咱们就拿补码来计算一下吧。
比如-5 + 10
-5 的原码:10000101 反码:11111010 补码(反码加1等于补码):11111011
正数的原码、反码、补码相同所以
10 的原码:00001010 反码:00001010 补码:00001010
-5 的补码和 10 的补码相加就等于:100000101 因为是9个比特位(bit)所以多的就丢掉。
就得到了 00000101 这个数换算成十进制就是 5 。正数的原、反、补相同所以这个二进制位也是补码
这样补码就很好的解决了,反码计算有误差的问题了。
计算机是用补码来进行储存
在计算机系统中,数值一律用补码来表示和储存。原因在于,使用补码,可以将符号位和数值域统一进行处理。
这里我写一段代码,进行调试,来演示计算机为什么用补码来表示和储存
int main()
{
int a = 20;
int b = -10;
return 0;
}
int a = 20;
00000000000000000000000000010100 - 20原码
00000000000000000000000000010100 - 20反码
00000000000000000000000000010100 - 20补码
在调试的内存窗口中,地址输入:&a,再把列改为4,可以看见 14 00 00 00 ,但是为什么是这样显示的呢?
因为内存显示的是16进制位,那就把20的补码拿来进行计算吧。
4个比特位(bit)就是一个16进制位,在二进制位中4个1就是15,十六进制,又是逢十六进一。
所以就4个比特位就是一个16进制位咯。
这样计算下来就是:00 00 00 14
int b = -10;
10000000000000000000000000001010 - -10原码
11111111111111111111111111110101 - -10反码
11111111111111111111111111110110 - -10补码
在调试的内存窗口中,地址输入:&b。也可以看见-10的十六进制位
十六进制的10 11 12 13 14 15 等于 a b c d e f
4个1就是二进制的15,所以就等于一个f
用-10的补码来计算就是:FF FF FF F6
这样就可以说明在计算机中是使用补码来进行表示和储存的了。
大小端字节序储存
在上边清楚了,关于计算机是拿补码来储存和计算的。
关于上边的内存窗口中显示的数据为什么是倒着放。
这里就可以讲讲大小端字节序储存了。
大端字节序储存:
就是把一个数据的低位字节的数据,存放在高地址处,
把高位字节的数据,存放在低地址处。
小端字节序储存:
就是把一个数据的低位字节的数据,存放在低地址处
把高位字节的数据,存放在高地址处。
什么是高位字节的数据和低位字节的数据呢?
比如:
一个123,其中的1是百位,2是十位,3是个位。这里的1就是高位字节的数据,3就是低位字节的数据。
关于大端字节序储存和小端字节序储存,在内存中又是怎么显示的呢?
这是一段代码,里面的变量a存储的是十六进制的数据。其中的11是高位字节序,44是低位字节序。
int main()
{
int a = 0x11223344;
return 0;
}
如果内存是左边是低地址,右边是高地址。11 在左边低地址处,44在右边高地址处。那么11 22 33 44,就是大端字节序储存。
如果11 是在右边高地址处,44是在左边低地址处,那么44 33 22 11,这样存就是小端字节序储存
咱们再来看一下,调试中的内存窗口是怎么显示的。
这里我用的是Visual Studio 2019来进行调试,当调试创建好了a的变量时,再到内存窗口的地址中&a,右边列里的自动改为4,就可以看见a地址里是如何储存的。
0x004FFCD8是a的地址。
可以看到这和我们变量a存的数据相反,11是高位字节序,但是在右边。44是低位字节序,现在在左边。
就可以知道原来这个数据是用小端字节序进行储存的。
刚才咱们明白了大小端是怎么存储的
那下面就来写一个判断大小端的代码吧。
判断大小端
这里先创建一个int类型的a变量,给个1的值。为什么是1呢?
在十六进制下的1是0x00000001, 00 是高位字节序,01 是低位字节序。
在大端存储下就是 00 00 00 01 ,左边是低地址,右边是高地址。
在小端存储下就是 01 00 00 00 。
如果是大端字节,前面就是一个字节00 ,小端字节就是01,那么这样就好分辨了。
因为a是int 4个字节类型的,所以需要强制转换为char 的1个字节类型给它的第一个字节,来判断。
就可以写一个char*来强制转换,用一个char* p来存储。然后if来判断是否为1就行了。
int main()
{
int a = 1;
char* p = (char*)&a;
if (*p == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
可以看见是最后输出的是小端
当然这个也可以封装成函数类型
这边如果是小端,就让他返回1,是大端就返回0。
这样的话就可以这样写
创建check_sys函数,在里边创建int a = 1;然后直接return *(char*)&a,如果*(char*)&a得到的是1,那么就直接返回1,如果是0就直接返回0。
int check_sys()
{
int a = 1;
return *(char*)&a;
}
int main()
{
if (check_sys() == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
最后谢谢观看,如果那里写得不对的地方,欢迎提出,探讨。白白