数据在内存中的存储
一、整数在内存中的存储
整数二进制的表示方法有三种:原码、反码、补码三种表示方法均由符号位和数
值位组成符号位用 0 表示正数,用 1 表示负数最高位当作符号位,其余均为数值位
正整数的原码、反码、补码都相同
负整数的原码就是二进制表示形式,最高位是 1
负整数的反码是符号位不变,数值位全部取反(0变1,1变0)
负整数的补码是反码 + 1
对于整形来说:数据存在内存中存放的是补码
计算机硬件只有加法器,没有专门的减法器,补码可以把减法完全转换成加法运
算;同时补码解决了「数字 0 有两种编码」的歧义、最大化利用二进制编码空
间、让符号位直接参与运算,是兼顾硬件简单、运算高效、编码规范的最优方
案。
二、大小端字节序和字节序判断
大端字节序:数据的低位字节内容放在高地址处,高位字节内容保存在内存的低地址处
小端字节序:数据的低位字节内容放在低地址处,高位字节内容保存在内存的高地址处

在 visual studio 2026 中采用的是小端字节序存放数据。
2.1 设计程序判断当前环境的大小端字节序
对 1 取出来一个字节的地址,解应用是 1 就是小端字节序,是 0 就是大端字节序
1 | int n = 1; |

1 | char a = -1; |
-1 是一个负整数,他在内存中存储的是 11111111
11111111 11111111
11111111
存放在变量 a b c 中时,存放的是 11111111
对于变量 a b, 他们都是有符号的,在处理时可处理为-1
对于变量 c , 是无符号的,按正数的存储来,就是原码也是 11111111,即 255

1 | char a = -128; |

-128 在内存中存储的补码是 11111111 11111111 11111111 10000000
放在 a 中的就是 10000000
而 %u 是输出无符号十进制数,输出时发生整型提升,根据整型提升规则,提升为
11111111 11111111 11111111 10000000
因为是无符号,所以原码、反码、补码都相同,所以输出的值是 4294967168
1 | char a = 128; |
128 在内存中存放的是 00000000 00000000 00000000 10000000
放到变量 c 里面的就是 10000000,这里可以发现,和上面的代码中,存放
的是一样的补码,变量 c 的类型仍然是 char , 故这里实际上 c 中存放的是 -128
而 %u 是输出无符号十进制数,输出时发生整型提升,根据整型提升规则,提升为
11111111 11111111 11111111 10000000
所以输出结果为 4294967168

1 | char a[1000]; |

char 类型因为是一个字节的有符号数据类型,其中存放的数值的范围是 -128 ~
127
当 -1 - i 的结果是 -129 时,就会出现一个现象
-129 的二进制原码是 11111111 11111111 11111111 01111111,存放到 char
类型
中的是 01111111 ,而这个值是 127,
类似的 -130 的二进制原码是 11111111 11111111 11111111 01111110,存放到 char 类型
中的是 01111110 , 而这个值是 126
通过一个图来理解
可以看到对于一个字节来说,一直不停的 +1 ,这个字节的值会在 -128 ~ 127 之间循环,构成了一个圆
1 | unsigned char i = 0; |
这里的 i 是无符号 char 型,存储数据的范围是 0 ~ 255, 即 11111111
当 i 的值为 255 时, 再加 1 ,由于 char 型只能存放一个字节的数据, 而 256 是 1 00000000,存放到 i 中的是 00000000 , i 的值是 0, 会一直这么循环下去,所以成了死循环
1 | unsigned int i; |
首先这个循环一定是一个死循环,因为 i 的类型是 unsigned int , i >= 0 是恒成立的
再来看看 i 后续的值,那么当 i 的值为 0, 再 -1 时,往 i 中存放 -1 的补码 11111111
11111111 11111111 11111111 时,unsigned int 会认为这是一个非常大的正整数
所以就是死循环
1 | int a[4] = { 1, 2, 3, 4 }; |
先来看 ptr1,这是一个 int* 类型的指针,&a 取出的是整个数组的地址,是一个 int(*)[]
(数组指针),而 a + 1 就是跳过的整个数组,指向的就是数组后的那一块内存空间,
赋值给了 ptr1

假如现在有一个数组 arr , arr[1] 其实就是 *(arr + 1)
所以 ptr1[-1] 其实就是 *(ptr1 - 1)

再看 ptr2 , 数组名 a 被强转成 int 再 + 1,就是一个整数数值 + 1
这个操作可以理解成让这个地址向前移动了一个字节,再强转(int*)赋值给ptr2,而数
组 a 是一个整型数组,一个整型占四个字节。int* 类型解引用就是从地址起始位置
向后拿四个字节

而 %x 占位符是十六进制,1的十六进制是0x000001,2的十六进制是0x000002,由于
在 visual studio 2026 中采用的是小端字节序,内存中存放如图

三、浮点数在内存中的存储
浮点数表示的范围在 float.h 中定义
3.1 浮点数的存储与整型的存储不同
1 | int n = 9; |

通过运行结果可以看出浮点数在内存中的存储与整型不同
3.2 浮点数的存储
根据国际标准IEEE(电子和电气工程协会)754,任意一个二进制浮点数 V 可以表示成
下面的形式
比如5.5可以表示成二进制 101.1
按照 IEEE754,就是表示成1.01 * 2 ^ 1
其中 S = 0,M = 1.01, E = 1
相应的 -5.5 的表示就是-1 ^ 1 * 1.01 * 2 ^ 1
其中 S = 1,M = 1.01, E = 1
IEEE754规定:对于32位的浮点数,最高的1位存储符号位S,接着的8位存储指数
E,剩下的23位存储有效数字M
IEEE754规定:对于64位的浮点数,最高的1位存储符号位S,接着的11位存储指数
E,剩下的52位存储有效数字M
3.3 浮点数存储的过程
IEEE754规定 在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍
去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时
候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数
为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
而 E 是一个无符号整数,如果E为8位,它的取值范围为0~255;如果E为11位,
它的取值范围为0~2047。但是,科学计数法中的E是可以出现负数的,所以
IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个
中间数是127;对于11位的E,这个中间数是1023。
举个例子:
1 | // 5.5 |
调试查看
由于浮点数在内存中的保存方式,有部分浮点数在内存中无法精确保存时避免不了的
1 | float f = 3.14f; |

3.4 浮点数取的过程
E 从内存中取出的时候还可以再分成三种情况:
以 float 型举例
E 不全为 0 或 不全为 1
1 | 0 10000001 01100000000000000000000 |
这属于普遍情况,在取出 E 的时候 -127 就拿到真实的E
E 全为 0
1 | 0 00000000 01100000000000000000000 |
这属于特殊情况, E 是 +127 还能是 0, E的真实值被指定为 1 - 127 = -126,出现这种情况时,有效数字 M 不再加上1,而是直接还
原为 0.xxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E 全为 1
1 | 0 11111111 01100000000000000000000 |
这属于特殊情况, E 是 +127 得到255, E 是 128 ,表示存入的数字是 1.xxx
* 2 ^ 128 次方, 这是一个极大数,出现这种情况时,如果 M 全为 0, 表示±无穷大
3.5 回看代码
1 | int n = 9; |
n 在内存中存放的是
1 | 00000000 00000000 00000000 00001001 |
对 pFloat 解引用时拿到的也是这串补码
但是 pFloat 是 float* 类型,解引用时应用的是浮点数存储的方式算数值,应该这么
看
1 | 0 00000000 00000000000000000001001 |
E 对应的全是 0 ,特殊情况, E在取出来时被指定为 -126, M 取出来时不再 +1 ,这里取出来的值是
1 | 0.1001 * 2 ^ -126 |
这是一个无限接近于 0 的数
1 | *pFloat = 9.0; |
而这段代码, 是对 pFloat 解引用进行赋值,这是以浮点数在内存中的存储规则去进行的
9.0 的二进制表示形式是
1 | 1001.0 |
在存放时 E + 127 = 130 存放, 存的是 10000010
故存放的是
1 | 0 10000010 00100000000000000000000 |
而从 n 的角度去看,适用的是整数在内存中的存储的规则,看到的是
1 | 01000001 00010000 00000000 00000000 |
即
1 | 2 ^ 30 + 2 ^ 24 + 2 ^ 20 |




