整型与浮点型数据在内存中的存储
前言
在数据类型中,除去构造类型、指针类型和空类型,剩下的基本上都是属于整型和浮点型了,而整型和浮点型又是我们使用较为频繁的数据类型,因此,掌握整型和浮点型数据在内存中的存储方式较为重要。
1. 整型数据在内存中的存储
计算机中整型有三种二进制表示方法:原码、反码、补码。这三种表示方法都有符号位和数值位,0表示正,1表示负
1.1 原码、反码、补码
要说原码、反码、补码,还得先分两个大类,一是无符号数,二是有符号数:
对于无符号数:原码、反码、补码是相同的。
对于有符号数:
又分为两类,一是正数,二是负数:
正数:原码、反码、补码是相同的。
负数:
原码——根据一个数的二进制得到
反码——原码取反
补码——反码加上1
参考下面的图:
下面举个例子来具体解释以下原码、反码、补码:
#include<stdio.h>
int main()
{
int a = 20;
//20
//求原码:它的二进制序列为10100,因为是正数,符号位是0,则原码的最前面是0,其余位置补上0
//00000000 00000000 00000000 00010100 - 原码
//00000000 00000000 00000000 00010100 - 反码
//00000000 00000000 00000000 00010100 - 补码
//a在内存中存的是0x00 00 00 14
//这是把二进制的补码转换成了十六进制
int b = -10;
//-10
//求原码:10的二进制序列为1010,而-10是负数,符号位是1,则原码最前面是1,其余位置补上0
//10000000 00000000 00000000 00001010 - 原码
//11111111 11111111 11111111 11110101 - 反码(原码取反)
//11111111 11111111 11111111 11110110 - 补码(反码+1)
//b在内存中存的是0xff ff ff f6
return 0;
}
a和b在内存中的存储如图:
我们得到的 00000014 是十六进制的数,把它转换成二进制,结果为:
00000000 00000000 00000000 00010100 ---- 这就是20的补码!
由此我们可以发现,在计算机系统中,数值一律用补码来表示和存储。
原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的(都是取反后+1),不需要额外的硬件电路。
关于原到补、补到原都是取反+1,可以参考下面分析:
比如-1
原码 10000000 00000000 00000000 00000001 - 原码
取反 11111111 11111111 11111111 11111110 - 反码
加一 11111111 11111111 11111111 11111111 - 补码
补码 11111111 11111111 11111111 11111111 - 补码
取反 10000000 00000000 00000000 00000000 - 注意这不是反码!!!
加一 10000000 00000000 00000000 00000001 - 原码
2. 大、小端字节序存储
什么是大小端字节序存储?
比如数字0x11223344
在计算机存储的时候,在内存的低地址到高地址中,是先存11呢,还是先存44呢,还是乱着存呢?
其实都可以,但是我们要存储方便,取出数据也要方便、精确,所以一般不考虑乱着存,于是就有两种存放数据的顺序。
大端字节序存储:
是指数据的低位字节的内容保存在内存的高地址中,而数据的高位字节的内容,保存在内存的低地址中。
小端字节序存储:
是指数据的低位字节的内容保存在内存的低地址中,而数据的高位字节的内容,保存在内存的高地址中。
比如0x11223344,其中11是高位,44是低位
-------------->
低地址 高地址
大端 11 22 33 44
小端 44 33 22 11
注意:我们讨论的高位、低位是以字节为单位的,因为16进制两个数字为一个字节,所以我们两个两个看。
大小端在vs中的例子,还是以0x11223344为例:
可以看到,存在内存中的0x11223344变成了44332211,这就说明了博主的电脑中的vs是小端存储的。
大家可以用下面的代码试试自己的机器是什么样的存储方式:
#include<stdio.h>
int check_sys()
{
//小端:返回1
//大端:返回0
int a = 1;
return *(char*)&a;
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端");
}
else
{
printf("大端");
}
return 0;
}
3. 浮点型数据在内存中的存储
首先我们要记住一点:浮点型数据和整型数据的存储方式是完全不同的!两者不可混淆!
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
1. (-1)^S * M * 2^E
2. (-1)^S表示符号位,当S=0,为正数;当S=1,为负数。
3. M表示有效数字,1<=M<2。
4. 2^E表示指数位。
例如:
float a = 5.5;
它的二进制为:101.1 从小数点右边是2的-1次,2的-2次…
根据(-1)^S * M * 2 ^ E
a = (-1)^0 * 1.011 * 2 ^ 2
又比如 0.5 = (-1)0*1.0*2(-1)
对于浮点数,只有S,M,E不一样,所以只需存储这三个数就行
对于S,无非是0或1
对于M,实际上只存小数点后面的数,因为1<=M<2 ,M=1.xxxxxx,M必然是1点几,所以这个1省略不存进去
对于E,存入时,先加上127(float)或者1023(double),再转换成二进制存入。
取出数据时,分为三种情况:
1. E全为0
这时,浮点数的指数E等于-126(或者-1022)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。
这样做是为了表示±0(正负取决于符号位s),以及接近于0的很小的数字。
2. E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)
3. E不全为0或不全为1(就是有0有1)
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),
得到真实值,再将有效数字M前加上第一位的1。
举例:
比如float类型的0.5:
0.5的二进制形式为0.1,即1.02(-1),即(-1)01.0*2^(-1)
S=0,M=0,E=-1+127=126
存入内存的形式为:
0 01111110 00000000000000000000000
再比如float类型的6.5:
6.5的二进制形式为110.1,即1.10122,即(-1)01.101*2^2
S=0,M=1.101,E=129
存入内存的形式为:
0 10000001 10100000000000000000000
将0 10000001 10100000000000000000000转化为16进制为40d00000,去vs中验证一下是否正确:
至此整型与浮点型在内存中的存储梳理完成了,学会这些或许无法帮助你很明显地提高编程能力,但是却能够有效的拓宽思路,扩大视野,相信在未来一定会有某一刻是需要这些知识的!