整型在内存中的存储
什么是原码反码补码
整型有三种二进制表示方法,分别为原码,反码,补码。
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位就直接把数值转化成二进制。
正数的原、反、补码都相同。
负整数的三种表示方法如下:
-
原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码。
-
反码:原码的符号位不变,其他位依次按位取反。
-
补码:反码+1。
比如10的二进制表示法,先看是正是负:正数,所以原反补相同
- 原码:00000000 00000000 00000000 00001010
- 反码:00000000 00000000 00000000 00001010
- 补码:00000000 00000000 00000000 00001010
-10是负数,所以原码符号位为1:
- 原码:10000000 00000000 00000000 00001010
- 反码: 11111111 11111111 11111111 11110101 (符号位不变,其余按位取反)
- 补码: 11111111 11111111 11111111 11110110 (反码+1)
什么是大小端
小端存储:是指把数据的低位字节的内容,保存在内存的低地址处;
高位字节的内容,保存在内存的高地址处。
大端存储:是指把数据的高位字节的内容,保存在内存的低地址处;
低位字节的内容,保存在内存的高地址处。
我是怎么记忆的呢:把什么放在低地址处就是什么端存储,低放低就是小端;高放低就是大端。
把数据的低(小)位字节放到低地址处就是小端;
把数据的高(大)位字节放到低地址处就是大端。
写一份代码来判断我的机器是大端存储还是小端存储:
#include<stdio.h>
int main()
{
int a = 1;
char* p = &a;
if (*p == 1)
printf("当前机器为小端");
else
printf("当前机器为大端");
return 0;
}
运行结果如图,这是怎么判断的呢?
int a = 1;在内存中开辟了一块空间
因为VS上内存中数据显示的是十六进制,所以我们把二进制转化为十六进制,四个二进制位转化为一个十六进制位
a在内存中存储的形式如图
我们取出a的地址,由此可见低地址处存放的是01,所以就是把低字节的数据存放在低地址处,所以我的机器为小端存储。
整型和浮点数变量在内存中存储是否不同
我们来看一份代码:
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
先猜测一下四个printf分别打印出了什么内容?
第一个printf,肯定输出的是9,这毫无疑问。
第二个printf,已知存进去的是整型9,%f打印的难道是9.000000?
第三个printf,存进去9.0,%d打印的是不是9呢?
第四个printf,存进取9.0,%f打印的应该是9.000000。
我们先做出上述猜测,那我们来看一下程序运行的结果。
上述代码的结果如下:
为什么是这样呢?我们来分析一下。
我们首先创建了一个int n整型变量,存入了一个9,这个9是以整型的形式存储到内存中的,第一个printf输出的是%d,所以n的值依然为9,这里从内存中拿取数据的方式,也是以整型的形式拿出来的,当&n并且强制类型转换成float*时,再解引用,我们发现他打印%f,并不是我们想象的9.000000,这一次我们从内存中拿取数据的方式则是以浮点数的形式拿出来,他的结果就发生了变化,
根据上述结果我们可以得知,内存中整型数据的存入和取出和浮点数数据的存入和取出是有所差异的。
浮点数的存储规则
任意一个二进制浮点数 V,根据国际标准IEEE(电气和电子工程协会) 754,可以表示成下面的形式:
简单的讲就是 (-1)^S 来表示浮点数的符号,M就是浮点数的二进制表示形式 ,
比如9.0可以表示为
第一部分:
(-1^0) —— S = 0 因为9.0是正数 —— 1001 是9的二进制表示形式,
第二部分:
1.001 —— 因为M大于等于1,小于2,所以小数点向左移三位
第三部分:
2^3 —— 2的E次方,小数点右边有几位E就等于几
根据上文我们可以知道,当知道S,M,E的值分别为多少时,我们就可以知道在内存中存储了一个什么数,那么既然是这样,当存储一个浮点数的时候,只需要存储S,M,E就可以了呢?
事实确实是这样的
那么浮点数数据是怎么存放到内存中去的呢?
IEEE754规定:
对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
通过对9.0浮点数V的表示形式的分析,我们可以发现:
-1 ^ S表示符号,M表示有效数字,2 ^ E表示指数位,在这其中
S前的-1,M前的1,E前的2都是永远固定不变的数字,那么这些数字存储在内存中是不是浪费了一定的空间呢。
于是IEEE 754规定,当存入浮点数到内存中时,是不存入这些固定的数字的。当读取内存中的数据的时候,如果读取的是浮点数,只需要把这些固定的数字还原出来就好了。
在计算机内部保存M时:
默认这个数的第一位总是1,因此可以被舍去,只保存小数点后面的部分。比如保存1.01的时,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
在计算机内部保存M时:
E为一个无符号整数(unsigned int)
如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。
但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
加上中间数之后又给E节省以为有效数字的空间,不然第一位当成符号位时,存储的有效数字就会减少一位。
指数E从内存中取出还可以再分成三种情况:
1.E有1有0:
指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
2.E全为0:
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
3.E全为1:
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
阅读到这里,相信你也对数据在内存中的存储有了一定的了解,感谢你的阅读,下期再见!