数据在内存中的存储
整数在内存中的存储
整数的二进制表示形式有三种:
原码、补码、反码
原码:将数值根据正负翻译成二进制的形式转换得到的就是原码
反码:原码除符号位不变,其余按位取反
补码:反码+1
对于有符号整数,二进制数分为两部分:符号位、数值位
符号位:最高位为符号位,其余位为数值位,0表正数,1表示负数。
对于正数:原反补相同
对于负数:
反码:原码除符号位按位取反
补码:反码最低位加1
在计算机中,数值一律按补码的形式存储,为什么?
原因:因为cpu只做加法处理,使用补码可以将符号位与数值位同一处理,如若遇上减法运算,也能转换成加法运算。
比如:
9的原反补码:0000 1001、
-6的原码:1000 0110、反码:1111 1001、补码:1111 1010
如果用原码来存储,进行运算时:9+(-6)=3
若用反码来存储,进行运算时:答案不对
若用补码来存储,进行运算时:答案正确
那为什么不这样子:用原码或者反码来存储,运算的时候在转换成补码呢?
因为如果直接以补码的形式进行存储,就可以在运算的时候省略掉原反补之间转换的过程,这样就不要额外的硬件电路了
⼤⼩端字节序和字节序判断
例子:
int main()
{
int a=0x11223344;
return 0;
}
调试这段代码,观察他在内存中的存储顺序我们会发现:它是以44 33 22 11的顺序存储的
至于原因,那就要讨论一下”大小端“的问题了
什么是大小端
当一个数据的大小超过一个字节时,在内存中就存在存储顺序的问题,我们根据数据在内存中的存储顺序,分为了”大端字节序存储“和”小端字节序存储 “
大端字节序:
低位字节的内容存在内存的高地址处,高位存低地址处
小端字节序:
低位字节的内容存在内存的低地址处,高位存在高地址处
为什么有大小端
一个地址单元对于一个字节,一个字节等于8bit,但是在c语言中除了8Bit,还有16bit(short),32bit(int),另外,对于位数大于8的处理器,,比如16位处理器,32位,64位处理器,寄存器的宽度大于一字节,那么必然存在如何安排多个字节的问题,由此就产生了大小端的存储方式
设计一个程序来判断大小端:
int check()
{
int a = 1;
return *(char*)&a;
}
int main()
{
int ret = check();
if (ret==1)
{
printf("小端");
}
else
{
printf("大端");
}
return 0;
}
浮点数在内存中的存储
浮点数存的过程:
浮点数在内存中是按照国际标准存储的:IEEE754
任何一个浮点数V可以表示成下面的形式:
V=(-1)S*M*2E
(-1)^S表示符号位,S=0为整数,S=1为负数
M表示有效数字,是大于等于1小于2的
2^E表示指数
(M*2^E这个表达式与科学计数法很相似)
比如:
5.0
按IEEE754的方式转换得到:1.01*2^2
S=0
M=1.01
E=2
-5.0
转换得到-1.01*2^2
S=1
M=1.01
E=2
浮点数的存储过程:
在上面说过:M是大于等于1,小于2的,所以 M可以写成1.xxxxxx,在IEEE754标准中规定,M只存储小数部位xxxxxx,默认M第一位的1是一直存在的,等到取值时,再把1加上,这样做是为了节省1位有效数字,比如在32位浮点型,M的有效位是23,因为1是默认存在的,所以M的有效位就是24了
⾄于指数E,情况就⽐较复杂
⾸先,E为⼀个⽆符号整数(unsignedint)
这意味着E在8位时的取值范围 是:0~255,在11位时取值范围是:0 ~ 2047。但是我们知道科学计数法中E是可能出现负数的,所以IEEE754标准规定,在E存入内存时要加上一个中间值,8位E中间值是127,11位E中间值是 1023。
比如:2^5,如果是8位E就要加上127,等于132,也就是10000100
浮点数取出的过程
浮点数取出的时候分为三种情况:E全为0,全为1,不全1或不全为
当E全为0时
(假设时32位浮点数)说明在E加上中间值之前是-127,因为只有这样,E加上中间值后才会等于0
此时V=(-1)^S* M * 2 ^-127,这是一个很无限接近于0的数字,并且当E小于0时,说明M是一个小于0的数字,在还原M时不用加上1,直接还原成0.xxxx的形式就行
当E位全1时
(假设时32位浮点数)说明E在加上中间值之前,值为128,此时V=(-1)^SM2 ^ 128,此时v是一个 正负无穷大的数,