C语言——数据在内存中的存储

本文详细讲解了整数和浮点数在内存中的存储原理,包括补码的使用,以及大端和小端字节序的区别。还通过示例分析了浮点数存储和取值过程中的潜在问题,如精度损失和比较浮点数的技巧。
摘要由CSDN通过智能技术生成

hello大家好,我是柚子,今天给大家分享的内容是整数、浮点数在内存中的存储,以及牵扯到的存储顺序的问题(大端和小端)

一、整数在内存中的存储

①整数的二进制表示有三种,即原码、反码和补码。有符号的整数,三种表示方法均由符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,最高的一位是被当做符号位,剩余的都是数值位;

②正整数的原码、反码、补码都相同;

③负整数的三种表示方法各不相同:原码:按照正负数值直接转化为二进制即可得到

                                                          反码:原码符号位不变,数值位依次按位取反

                                                          补码:反码+1

例如下图:

对于整型来说:数据存放内存中其实就是存放的补码。

为什么?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码可以将符号位和数值域统一处理,同时加减法也可以统一处理(CUP只有加法器,-1==+(-1),+(-1)这种运算只有补码才能正确计算)此外,补码与原码相互转换(取反+1),其运算过程是相同的,不需要额外的硬件电路。

大端/小端字节序存储

什么是大端、小端?

其实超过一个字节的数据在内存中存储的时候就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储:①大端存储模式:数据的低位字节内容保存在内存的高地址处,高位字节内容保存在内存的低地址处;②小端存储模式:数据的低位字节内容保存在内存的低地址处,高位字节内容保存在内存的高地址处。

如何判断?我们来用代码实现一下:

二、浮点数在内存中的存储

我们先来 看一道例题,先别看答案,自己判断一下预期的输出结果;

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);	 
	printf("*pFloat的值为:%f\n", *pFloat);  

	*pFloat = 9.0;
	printf("n的值为:%d\n", n);	 
	printf("*pFloat的值为:%f\n", *pFloat);	 
	return 0;
}

 好,我们接下来来看一下实际输出结果:

按照注释,是否能理解了一丢丢,这么看来整数的存储方式和浮点数的存储方式是不一样滴。

具体情况我们一起来看一下!

根据国际标准 IEEE (电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的这种形式:

例如:①十进制的5.0,写成二进制是101.0,相当于1*1.01*2^2;

             S=0,M=1.01,E=2 。

           ②十进制的5.0,写成二进制是-101.0,相当于(-1)*1.01*2^2;

             S=1,M=1.01,E=2 。

IEEE 745规定:

对于32位浮点数,最高位1位是存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

对于64位浮点数,最高位1位是存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

a.浮点数的过程

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的XXXXXX部分,比如保存1.01的时候,只保存01,等到读取的时候再把第一位加上去。这样做的目的是节省一个有效数位,以32位为例,留给M的只有23位,将第一位的1舍去以后,等于后边可以多保存一位,这样更精确。

但是如上图所展示的黄色部分的E,规定成无符号的整数,但是E它也有可能是负数,(比如0.5,E为-1),那怎么办呢,这下没办法存进去了,出现bug了,不要着急,这时我们可以看看那个IEEE 754是怎么规定的,它说存入内存的E的真实值必须再加上一个中间数,8位的E的取值范围是0~255,11位的E的取值范围是0~2047;所以,对于8位的E,这个中间数是127,对于11位的E,这个中间数是1023。

如果理解起来太抽象的话,我们看例题:

到此我们可以调试来验证一下:

 

b.浮点数的过程

上述三种情况的后两种简单了解一下就可以了,重点是第一种常规复原的方法!!

到这儿之后我们再回过头去看这个例题:

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	//9的二进制1001
	//补码:000000000000000000001001
	//0 00000000 00000000000000000001001
	//S=0,E=-126,M=0.0000000000000000001001
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000

	*pFloat = 9.0;
	//9.0 二进制1001.0
	//S=0,E=3,M=1.001
	//0 10000010(127+E) 00100000000000000000000
	//01000001000100000000000000000000 整型去取的时候会认为这32位是补码
	printf("n的值为:%d\n", n); //1091567616
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

 这么解释是不是清楚了很多,恍然大明白的感觉。

我们来总结一下:①有些浮点数在内存中可能无法精确保存(例如3.14,99.7)

                             ②double类型的精度比float类型更高

                             ③两个浮点数在比较大小的时候,直接使用“==”可能会存在问题!

如果非要比较的话,就添加一个精度,例如:if(abs(f-5.6)<= 0.000001),推荐一本书《C语言深度解剖》,这里边有详细记载如何比较两个浮点数。

ok,今天的分享就到这里,我们下次不见不散,,关注柚子不迷路哦!!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我为IT舔砖Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值