C语言零基础第17讲:数据在内存中的存储

目录

1.整数在内存中的存储

2.大小端字节序

[2.1 什么是大小端?](#2.1 什么是大小端?)

[2.2 为什么会有大小端之分?](#2.2 为什么会有大小端之分?)

[2.3 练习1](#2.3 练习1)

[2.4 练习2](#2.4 练习2)

[2.5 练习3](#2.5 练习3)

[2.6 练习4](#2.6 练习4)

[2.7 练习5](#2.7 练习5)

[2.8 练习6](#2.8 练习6)

3.浮点数在内存中的存储

[3.1 浮点数的存储](#3.1 浮点数的存储)

[3.2 浮点数存的过程](#3.2 浮点数存的过程)

[3.3 浮点数取的过程](#3.3 浮点数取的过程)

[3.4 题目解析](#3.4 题目解析)


正文开始

1.整数在内存中的存储

我们先来回顾一下,整数的表示方法:

  1. 整数的二进制表示方法有3种:原码、反码、补码。
  2. 有符号整数的最高一位为符号位, 0表示正,1表示负,其余位为数值位。
  3. 无符号整数没有符号位,所有的位都用来表示数值。

我们再来回顾一下,原码、反码和补码的概念:

  1. 原码:真值的符号部分用0或1来表示,真值的数值部分用二进制来表示。
  2. 反码:原码的符号位不变, 其他位取反。
  3. 补码:反码 + 1。
  4. 原码→补码:符号位不变,取反+1。
  5. 补码→原码:符号位不变,取反+1。
  6. 正整数的原、反、补都相同。
  7. 负整数的原、反、补各不相同。

我们还需要知道的是:

  1. 在计算机系统中,数值一律用补码来表示和存储。
  2. 原因在于,使用补码,可以将符号位和数值域统一处理。
  3. 同时,加法和减法也可以统一处理(CPU只有加法器)。
  4. 此外,补码和原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

2.大小端字节序

请看代码:

我们发现:0x11223344是以字节为单位,倒着存储的,即44332211。这是为什么呢?

2.1 什么是大小端?

刚刚的变量a,是int类型,大小为4个字节。像这样,超过1个字节的数据,在内存中存储的时候,就有存储顺序的问题了。按照不同的存储顺序,分为大端字节序存储和小端字节序存储,概念如下:

  1. 大端模式:数据的低位放在高地址处,高位放在低地址处。
  2. 小端模式:数据的低位放在低地址处,高位放在高地址处。

简而言之:

  1. 大端模式:正着存,如0x11223344→11 22 33 44。
  2. 小端模式:倒着存,如0x11223344→44 33 22 11。

2.2 为什么会有大小端之分?

2.3 练习1

  1. 在计算机系统中,数据的存储是以字节为单位的。
  2. 每个地址单元都对应着1个字节,1个字节有8个比特位。
  3. char是1个字节,但short是2个字节,int是4个字节......
  4. 对于超过1个字节的数据,它就需要分为多个字节来存储了,也就出现了存储顺序问题。
  5. 对于32位和64位这些处理器,由于寄存器的宽度大于1个字节,必然存在着顺序问题。
  6. 我们常用的x86结构是小端模式,而KEIL C51则为大端模式。
  7. 很多的ARM、DSP都为小端模式。
  8. 有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)------百度笔试题

我们可以以1为例,来进行大小端的判断。

如果是大端模式,即00 00 00 01,我们取出第一个字节,应该得到00。

如果是小端模式,即01 00 00 00,我们取出第一个字节,应该得到01。

2.4 练习2

请看代码:

这道题与整数在内存中的存储有关。

我们来看一下结果:

我们再看另一种情况,如果是以%u的形式打印呢?

2.5 练习3

我们来看一下分析及结果:

我们再看一道类似的题目:

2.6 练习4

2.7 练习5

再看一道类似的题目:

2.8 练习6

对于ptr[-1],我们可以看看图解:

我们再来看一下*ptr2的图解:

运行结果如我们所见:

上述代码运行的环境为32位环境,如果是64位环境,a是8字节的地址,强制转换为4字节的int类型,就会破坏地址的完整性了。

3.浮点数在内存中的存储

请看代码:

如上,我们可以发现,浮点数的存储方式,和整型是有差异的。

3.1 浮点数的存储

在上面的代码中,n和*pFloat在内存中明明是同一个数,为什么以浮点数和整数的视角解读,结果会有这么大的差异呢?

要理解这个结果,一定要搞明白浮点数在计算机内部的表示方法。

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

  1. S也叫数符,0表示整数, 1表示负数,用来决定浮点数的符号。
  2. M也叫尾数,是一个二进制的定点小数,一般用原码表示。
  3. E也叫阶码,是一个二进制的定点整数,一般用移码来表示。

我们来看一个例子:

IEEE754规定:

  1. 对于32位的浮点数 (float),最高1位 存储的是符号位S,后面8位 存储的是指数E,剩下23位存储的是有效数字M。
  2. 对于64位的浮点数 (double),最高1位 存储的是符号位S,后面11位 存储的是指数E,剩下52位存储的是有效数字M。

3.2 浮点数存的过程

前面提到过,根据IEEE 754标准,M都是写成1.xxxxxx的形式。可以发现,任何一个浮点数,第一位都是1。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以不用存这个1,只存后面的xxxxxx部分即可

比如,在存1.01的时候,只存01,等到读取的时候,再把第一位加上去。

这样做的目的是,节省1位有效数字。以32位浮点数为例,留给M的只有23位,将第一位舍去后,相当于可以存24位有效数字了。

至于指数E,它是一个无符号整数(unsigned int)

这意味着,如果E为8位,它的取值范围就是0~255;如果E为11位,它的取值范围就是0~2047。但是,我们知道,有时候E是会出现负数的,比如:0.5为(-1)^0*1.0*2^(-1),这里的E就是-1。所以IEEE 754规定,存入内存时,E的真实值必须再加上一个偏置值。对于8位的E,偏置值为127;对于11位的E,偏置值为1023。也就是,根据IEEE 754,对于n位的E,偏置值为2^(n-1) - 1。

比如,2^10的E是10,所以保存为32位浮点数时,必须保存为10+127=137,即10001001。

3.3 浮点数取的过程

指数E从内存中取出来,还可以再分为3种情况:

1.E不全为0或不全为1(常规情况):

符号位S不用处理。

指数E减去偏置值127(或1023),得到真实值。

有效数字M的第一位加上原来舍去的1。

2.E全为0:

这时,IEEE 754规定:

指数E的真实值为1-127(或1-1023)。

有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。

这样做是为了表示±0,以及表示接近于0的很小的数字。

3.E全为1:

这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位S)。

3.4 题目解析

对于开头提到的代码,我们就进行一个解析:


完结