【C语言】整数和浮点数在内存中的存储
个人主页
今天我们来讲一下整数和浮点数在内存中的存储方式以及大小端字节序的判断以及了解什么叫大小端,下面就一起来看看吧。
文章目录
- ⭐一、整数在内存中的存储
- 🚘二、大小端字节序和字节序的判断
- 🏝️三、浮点数在内存中的存储
⭐一、整数在内存中的存储
我们之前在学习操作符时,就知道整数的2进制表示方法有三种,即原码、反码和补码。这三种方式都有符号位和数值位两部分组成。符号位都是用0来表示“正”,用1来表示“负”,而数值位最高位的⼀位是被当做符号位来看待,剩余的都是数值位。
我们知道,正整数的原码,反码和补码均相同,而负整数的三种表示方法均不同:
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
例如:int a = 4时,由于其为正数,因此它的原码,反码和补码均相同,而int类型的大小为4个字节,因此在32位平台下存储的形式就为:
00000000 00000000 00000000 00000100
而当int a = -4时,我们知道负整数的原反补码均不相同,因此在32为平台下存储的形式为:
原码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
而对于整形数据来说,数据在内存中存放的其实是补码,而不是原码,这是为什么呢?这是因为补码可以将符号位和数值域统一进行处理
🚘二、大小端字节序和字节序的判断
我们先来了解一下什么叫做大小端?
大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容保存在内存的低地址处。
小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容保存在内存的⾼地址处。
我们来看一段代码演示一下:
我们通过调试,发现在变量a中的这个数字是倒着进行存储的,这就说明vs2022这个编辑器内部是使用小段存储的模式来进行存储数据的。
我们可以写一段代码来判断编译器是使用大端存储还是小端存储:
int main()
{
int i = 1;
//拿出第一个字节
int ret = *((char*)&i);
if (ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
那为什么会有大小端之分呢?
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,一个字节为bit位,但是在C语言中除了8bit的char之外,还有16bit的short型32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
🏝️三、浮点数在内存中的存储
我们先来看一段代码:
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;
}
问上述代码的输出结果是什么?
运行结果我们发现:*pfloat和num在内存中不是同一个数据吗?为什么运行结果显示差别这么大,这就需要考虑浮点数在内存中的存储方式了。
根据国际标准IEEE(电气和电子工程协会)754,任意⼀个二进制浮点数V可以表示成下面的形式:
例如:十进制的5.0,写成二进制的形式为101.0,相当于1.01*2^2。
因此根据上面V的格式我们可以得出:S = 1,M = 1.01,E = 2。
IEEE754规定:
• 对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
• 对于64位的浮点数,最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
浮点数存的过程:
IEEE754对有效数字M和指数E,还有⼀些特别规定。
数字M的规定:
由于M是大于等于1,小于2的,也就是说M可以写成1.xxxxx的形式,其中xxxxx表示小数部分。因此IEEE754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。 例如在保存1.01时,只保存01,等到读取的时候,再把第⼀位的1加上去,这样做的目的是为了节省1位有效数字。
指数E的规定:
首先我们要知道,E为一个无符号的整数。
这就意味着,如果E为8位,它的取值范围为0 ~ 255;如果E为11位,它的取值范围为0~2047。但我们知道,科学计数法中的E是可以出现负数的,因此IEEE754规定,存入内存时E的真实值必须再加上⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。 例如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
浮点数取的过程:
指数E从内存中取出还可以再分成三种情况:
E不全为0或不全为1:
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第⼀位的1。
例如:3.5, 用V的方式表示为:(-1)^0 * 1.11 * 2^1 其S = 0,M = 1.11,E = 1
根据上面的规定:1+127 = 128,即10000000;M去1为11
因此存储方式为:0 10000000 11000000000000000000000
E全为0:
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E全为1:
这时,如果有效数字M全为0,表示正负无穷大(正负取决于符号位S)
以上就是浮点数在内存中存储的规则。到这里,相信大家对整数和浮点数的存储规则应该都了解的差不多,现在我们就来回过头来看看一开始的代码题
根据运行结果,为什么9还原成浮点数为0.000000,我们先来看看9以整型的形式在内存中的存储:
00000000 00000000 00000000 00001001
根据浮点数拆分规则,我们可以将上述二进制序列拆分为:S = 0,E = 00000000,M为00000000000000000001001。
V = (-1) ^ 0 × 0.00000000000000000001001×2 ^ (-126)=1.001×2 ^ (-146)。显然,V是⼀个很小且接近于0的正数,因此用十进制的小数表示就为0.000000。
再看看浮点数9.0为什么整数打印时为1091567616
首先,浮点数9.0等于二进制的1001.0,即换算成科学计数法是:1.001×2^3
因此 V = (−1) ^ 0 * 1.001 * 2 ^ 3 ,即S = 0,M = 1.001,E = 3
因此写成二进制的形式应为:S + E + M
即:0 10000010 00100000000000000000000
而这个为内存中的补码,又因为其为正整数,其原反补码均相同,因此打印的数据就为1091567616。
今天的分享就到这里啦,如果感到不错,希望能给博主一键三连,感谢大家的支持!希望这篇文章可以帮到大家,我们下期再见!