数据在内存中的存储(C语言)

在这里插入图片描述

😇 😇hello,我是bug。今天我们来学习一下数据是如何在内存中进行存储的

🌟 🌟 一、数据类型

🌳 🌳一、介绍

数据类型可以大致分为内置类型和自定义类型,内置类型又分为三个大类型:整型、浮点数、指针类型。 ⚽️⚽️


在这里插入图片描述

🌳 🌳二、类型的意义

🌵🌵1、判断使用该类型数据开辟的空间大小。
🌵🌵2、如何看待内存空间的视角。

🍏 🍏注意❗️ ❗️

字符类型也是整型家族之中的一员,因为存储字符类型的本质是存储其ASCII码值。 ⚽️⚽️


🌟 🌟二、整型在内存中的存储

原码、反码、补码

🌳 🌳编码方式: 通过一定手段将数据转化成二进制,而这种手段就叫做编码方式。

🎈🎈1、原码

🌈🌈因为计算机中只能够识别0和1,所以存储数据只能使用二进制进行存储。那么我们就要将数据转化成二进制的形式。对于正数而言,其二进制数可以直接转化。那么负数的二进制又要如何实现呢?为了表示负数,人们在二进制数前增加了一位符号位。这种编码方式被称为原码,原码的出现可以解决计算机的存储问题,但随之而来的问题是,如何对其进行计算呢?计算机中算术逻辑单元不能够直接计算减法,计算减法我们就要将其转换成加法运算。 ⚽️⚽️
🌈🌈当我们使用原码进行加法运算时,如图⬇️ ⬇️:

在这里插入图片描述

🌈🌈加法计算的结果没有问题,那我们再来进行减法运算,如图⬇️ ⬇️:

在这里插入图片描述
🌈🌈对于减法运算,我们要先将其转换成加法运算。当我们考虑符号位的时候,计算结果明显是错误的!所以计算机存储数据不能采用原码。


🎈🎈2、反码

🌈🌈因为减法运算中原码的不适用,人们发明出了反码来解决这个问题。

反码的计算方法是:正数的反码就是其本身,负数的反码就是符号位不变,其他位按位取反。

🍏 🍏对反码进行减法运算时,要注意以下几点:
🌵🌵1、符号位和数据一起参加计算。
🌵🌵2、符号位相加后如果出现进位,那么就要把它送到最低为去进位。
🌵🌵3、反码运算的结果仍为反码。要获得原码时,关键看符号位,符号位为0,那么反码和原码相同;符号位为1,那么要对反码按位取反。

🌈🌈反码的加法运算和原码没有区别,这里我们直接看减法运算,如图⬇️ ⬇️:
在这里插入图片描述
🌈🌈计算结果没有问题,所以反码可以很好的解决减法运算。但是在某些特定的场景下,其还是存在一定的小问题,如图⬇️ ⬇️:
在这里插入图片描述
🌈🌈计算结果取反后,出现了-0这种情况。0怎么还分正负呢?对于我们而言,正负0可能没有什么区别。但是对计算机而言。补码为全0和全1的时候分别表示+0和-0,对于这种没有意义的事情,还是要进行解决。所以就补码就出现了。


🎈🎈3、补码

补码的计算方法是:正数的补码和原码一致,负数通过对反码加一获得补码。

🍏 🍏对补码进行减法运算时,要注意以下几点:
🌵🌵1、符号位和数据一起参加计算。
🌵🌵2、符号位相加后如果出现进位,那么就要把它直接舍弃。
🌵🌵3、反码运算的结果也是补码。要获得原码时,关键看符号位,符号位为0,那么补码和原码相同;符号位为1,那么补码的补码就是原码。

🌈🌈补码的加法运算和原码没有区别,这里我们直接看减法运算,如图⬇️ ⬇️:
在这里插入图片描述
🌈🌈计算的结果没有问题,再看关于0符号问题,如图⬇️ ⬇️:
在这里插入图片描述
🌈🌈计算结果中0的正负号问题也被解决了。

补码的优点:
🌵🌵1、采用补码,可以解决反码表示中0的正负表示方式。
🌵🌵2、补码表示的数字范围要比原码和反码大。

🍕总结:
在内存中,存储整型数据都是存储的补码。存储整型数据使用补码可以统一处理符号域和数值域;同时加法和减法也可以统一处理,因为cpu只有加法器,所以要直接规避减法。而且原码和补码之间的转换过程也是一致的,所以不需要额外的硬件电路。


🌟 🌟三、大小端字节序

🌈🌈大小端字节序:(在C++中有提到,这里我们简单梳理一下)
计算机中我们以字节为单位,每一个地址单元都对应了一个字节。对于char这种只占一个字节的类型,不需要考虑存储数据时字节在内存中的顺序。但是对于short、int等占多个字节的类型,如何安排数据字节的顺序就是一个必然问题了。

🍕大端存储:即数据的高位存储在内存中的低位,数据的低位存储在内存中的高位。
🍕小端存储:即数据的高位存储在内存中的高位,数据的低位存储在内存中的低位。

🌈🌈如图⬇️ ⬇️:
在这里插入图片描述
在这里插入图片描述

🌈🌈通过内存窗口我们可以看到,从左往右地址逐渐增大,低位上的44存储在地址中的低位,所以当前为小端存储。

🍏 🍏注意❗️ ❗️

🌈🌈我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

🌈🌈那我们如何来设计一个程序判断当前机器是大端还是小端呢? ⬇️ ⬇️

char Check()
{
	int a = 1;
	return *(char*)&a;
}

int main()
{

	if (Check())//为真就是小端机
	{
		printf("小端\n");
	}
	else//大端机
		printf("大端\n");

	return 0;
}

🌈🌈当我们以int的形式存储1时,那么一共使用了4个字节来进行存储。我们再将其地址强制转换成*char,我们强转之后解引用就可以获得最低位上的数据,那么如果是大端机,那么低位上存储的是00,如果是小端机,那么低位上存储的就是01,这样就可以加以区分了。


🌟 🌟四、浮点数在内存中的存储

🌈🌈浮点数的存储规则:
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式⬇️ ⬇️:
在这里插入图片描述

🍒 🍒以6.0为例,写成二进制为110.0,即1.1X2^2 。那么S = 0,M = 1.1,E = 2 。
🍒 🍒以-6.0为例,写成二进制为-110.0,即-1.1X2^2 。那么S = 1,M = -1.1,E = 2 。

🌻🌻那么我们要如何将浮点数存入内存中呢?
IEEE 754规定:⬇️ ⬇️
🌈🌈对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
在这里插入图片描述

🌈🌈对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
在这里插入图片描述
🍕除上述之外,IEEE 754对有效数字M和指数E,还有一些特别规定。

🌵🌵1、M:1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

🌵🌵2、E:首先,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。

🍒 🍒以6.5f为例,将其转换成二进制后用科学计数法表示为1.101X2^2,那么S = 0,M = 1.101,E = 2 + 127 = 129。将其存入后,如图⬇️ ⬇️:
在这里插入图片描述

🌈🌈我们再到编译器中的内存进行观察,如图⬇️ ⬇️:

在这里插入图片描述

在这里插入图片描述
🌈🌈由于当前机器是小端机,6.5f的十六进制表示为0x40d00000,存入内存中要按照小端来存储。

🍕然后,将E取出来还有如下规则:

🌵🌵 (1)E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
🌵🌵 (2)E为全1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s).
🌵🌵 (3)E为全0
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。


🍕当我们了解了整型和浮点数在内存中的存储之后,试试下面这道题目。
🌈🌈请写出下列程序的输出值: ⬇️ ⬇️

int main()
{
 int n = 9;
 float *pFloat = (float *)&n;
 printf("n的值为:%d\n",n);
 printf("*pFloat的值为:%f\n",*pFloat);
 *pFloat = 9.0f;//我们直接加上f,就表示为float
 // *pFloat = 9.0;因为当前编译器下默认的浮点数类型为double,所以这里会发生隐式类型转化,即将double转化成float
 printf("num的值为:%d\n",n);
 printf("*pFloat的值为:%f\n",*pFloat);
 return 0; }
输出的结果为:90.0000001,091,567,6169.000000

🍕分析:
🌈🌈n为正数,那么n的原码、补码、反码一致。n的补码为:1001 (前面的0省略掉了)。这时,我们将n的地址强制转化float的指针,那么我们将n的数值放入*pfloat中,此时E为全0,那么按照浮点数的形式取出时,该数字无限趋近于0,所以结果为0.000000 。
当我们给*pfloat存入9.0f时,其二进制为:0 10000010 00100000000000000000000,十六进制为:0x41100000,按照整型取出,那么就是1,091,567,616 。

🍕验证:⬇️ ⬇️
在这里插入图片描述


😎 😎今天的内容到这里就结束了,希望各位能够有所收获。
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天也要写bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值