news 2026/2/15 2:09:08

C语言之数据在内存中的存储4000字超详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言之数据在内存中的存储4000字超详解

目录

一、整数在内存中的存储

1.存储形式

2.原码、反码和补码

二、大小端字节序和字节序判断

1.什么是大小端?

2.为什么有大小端?

3.判断大小端模式

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

1.奇怪的现象

2.浮点数内存存储

2.1指数E的特殊情况

3.回到例题

往期内容:


一、整数在内存中的存储

1.存储形式

整型数据在内存中存储的是二进制的补码形式,这是计算机中表示整数的标准方式

2.原码、反码和补码

整数的2进制表⽰⽅法有三种,即原码、反码和补码

有符号整数的三种表⽰⽅法均有符号位数值位两部分,2进制序列中,最⾼位的1位是被当做符号 位,剩余的都是数值位。

符号位都是⽤0表⽰“正”,⽤1表⽰“负”

int 类型来说,占 4 个字节,32 个 bit 位

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

负整数的三种表示方法各不相同。

原码直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码反码+1就得到补码。 补码得到原码也是可以使⽤:取反,+1的操作。

⽆符号整数的三种 2 进制表示相同(直接将其数值转换为二进制存储),没有符号位,每⼀位都是数值位。

举个栗子

对于正数,它的原码、反码和补码是完全相同的。所以,+25的二进制直接表示即可。

对于负数,转换则遵循特定的步骤(以 -25 为例):

  1. 原码:最高位(符号位)为1,表示负数。其余位是该数绝对值(25)的二进制表示。所以,-25 的原码是10000000 00000000 00000000 00011001

  2. 反码:保持符号位1不变,将原码的数值位(0000000 00000000 00000000 0011001按位取反(0变1,1变0),因此,-25 的反码是11111111 11111111 11111111 11100110

  3. 补码:在反码的基础上,最低位加1。反码11111111 11111111 11111111 11100110加 1 后得到11111111 11111111 11111111 11100111

数值原码反码补码
+2500000000 00000000 00000000 0001100100000000 00000000 00000000 00011001

00000000 00000000 00000000 00011001

-2510000000 00000000 00000000 0001100111111111 11111111 11111111 1110011011111111 11111111 11111111 11100111

对于整形来说:数据存放内存中其实存放的是⼆进制的补码,那这是为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。

原因在于,使⽤补码,可以将符号位和数值域统⼀处理;

同时,加法和减法也可以统⼀处理(CPU只有加法器)

此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

例如,计算1 - 1相当于计算1 + (-1)

1的补码00000000 00000000 00000000 00000001

-1的原码10000000 00000000 00000000 00000001

反码11111111 11111111 11111111 11111110

补码11111111 11111111 11111111 11111111

两者补码相加:

结果是:1 00000000 00000000 00000000 00000000

最前面的进位溢出舍弃,最终得到0,结果正确。

二、大小端字节序和字节序判断

当我们了解了整数在内存中存储后,我们调试看⼀个细节:

int main() { int a = 0x11223344; return 0; }

调试的时候,我们可以看到在a中的 0x11223344 这个数字是按照字节为单位,倒着存储的。这是为什么呢?

1.什么是大小端?

其实超过⼀个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:

大端(存储)模式: 是指数据的低位字节内容保存在内存的⾼地址处,而数据的高位字节内容,保存在内存的低地址处。

小端(存储)模式: 是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。

2.为什么有大小端?

这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8 bit 位,但是在C语⾔中除了8 bit 的 char 之外,还有16 bit 的 short 型,32 bit 的 long 型(要看 具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于⼀个字节,那么必然存在着⼀个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

3.判断大小端模式

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

#include <stdio.h> 因为int a = 1 ;中的 a 储存的是0x 00 00 00 01 所以可以将 a 内存中第一个字节取出来,判断是 1 还是 0 是 1 就是小端模式,否则是大端模式 int check_sys() { int i = 1; return (*(char *)&i); } int main() { int ret = check_sys(); if(ret == 1) { printf("⼩端\n"); } else { printf("⼤端\n"); } return 0; }

还有一种联合体的写法

int check_sys() { union { int i; char c; }un; un.i = 1; return un.c; }

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

常⻅的浮点数:3.14159、1E10(1E10的完整数学表达式是 1 × 10¹⁰)等,浮点数家族包括: float、double、long double 类型。 浮点数表示的范围: float.h 中定义。

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; }

是不是很出乎意料,上⾯的代码中, n 和 *pFloat 在内存中明明是同⼀个数,为什么浮点数和整数的解读结果会差别这么⼤?

在这个过程中,n就相当于一块空间,可以以整数的视角来理解这4个字节,也可以以浮点数的视角来理解这4个字节,以整数的形式存放,然后以整数的形式拿出,以浮点数的形式存放,然后以浮点数的形式拿出,但是我以整数的形式存放,以浮点型的方式拿出,以浮点数的形式存放,以整型的方式拿出,结果就不对,由此说明,整数和浮点数在内存中的存放方式不同

2.浮点数内存存储

要理解这个结果,⼀定要搞懂浮点数在计算机内部的表示⽅法。

根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:

V = (-1)^S * M * 2^E

S 表示符号位,当S=0,V为正数;当S=1,V为负数

M 表示有效数字,是一个大于等于1且小于2的二进制小数。例如,二进制下的1.011

E 表示2的幂次,决定小数点的位置

举例来说:

⼗进制的5.0,写成⼆进制是 101.0 ,相当于1.01* 2^2 。 那么,按照上⾯ V 的格式,可以得出S=0,M=1.01,E=2。

IEEE 754标准主要定义了两种精度的浮点数格式,它们在内存中的位分配如下

精度总位数符号位 (S)指数位 (E)尾数位 (M)
单精度 (float)32位1位8位23位
双精度 (double)64位1位11位52位

具体规则如下:

符号位 (S):直接存储,0代表正,1代表负

指数 (E):E在存储时是一个无符号整数。为了能表示负指数,实际存储的是E + 偏移值,单精度的偏移值是127,双精度是1023

例如,单精度下真实指数E=2,存储时就是2+127=129,二进制为10000001

有效数字 (M):由于M总是1.XXXXXX的形式,整数部分的1是固定的,因此存储时只保存小数部分XXXXXX,这样可以多出一位有效数字,提高精度,读取时再将这个1加上

我们以单精度浮点数存储17.625为例

转换二进制:整数部分17转二进制为10001;小数部分0.625转二进制为0.101(因为0.625 =0.5+0.125,即2^(-1) + 2^(-3)),所以17.625的二进制是10001.101

规范化:将二进制数小数点左移4位,变为1.0001101 × 2^4。得到S=0,M=1.0001101,E=4

计算存储值符号位 S0,阶码 E4+127=131,二进制为10000011,尾数 M:存储M的小数部分0001101,后面用0补足23位,得到00011010000000000000000

组合:最终32位序列为0 10000011 00011010000000000000000。通常我们表示为十六进制0x41 8D 00 00

这样的浮点数存储⽅式很巧妙,但是我们也要注意到有的浮点数是⽆法精确保存的。⽐如:1.2,我们可以在VS上调试看⼀下,我们发现会有些许误差

2.1指数E的特殊情况

E全为0

这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字

0 00000000 00100000000000000000000

E全为1

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

0 11111111 00010000000000000000000

3.回到例题

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; }

我用excel表格做了个图解

1,4相信聪明的你肯定知道,我们重点讲一下2,3

第2题,取出n的地址再强制类型转换给指针pFloat,再以浮点数形式打印

第3题,取出 n 的地址再强制类型转换给指针 pFloat ,再通过解引用把 n 的值改为 9.0 (浮点数),最后以整型形式打印

往期内容:

C语言内存函数使用和模拟(memcpy,memmove,memset,memcmp)4000字超详解

C语言分支和循环语句保姆级讲解0基础也能看懂

C语言中传参的本质

OK,以上就是全部内容了

若内容对大家有所帮助,可以收藏慢慢看,感谢大家支持
本文有若有不足之处,希望各位兄弟们能给出宝贵的意见。谢谢大家!!
本期制作不易希望各位兄弟们能动动小手,三连走一走!!
支持一下(三连必回0.0)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!