数据在内存中的存储

数据在内存中的存储是编程的核心基础,本质是二进制位(bit)的有序排列------ 内存可视为 "字节数组",每个字节(8 bit)对应唯一的内存地址(如 0x00001000),数据存储本质是将其编码为二进制后,写入指定地址的字节中。

以下从「内存区域划分」「基本数据类型存储」「复合数据类型存储」「关键存储规则」四个维度,系统讲解数据在内存中的存储逻辑:

一、先明确:数据存在内存的哪个区域?

不同生命周期、不同类型的数据,会存储在内存的不同区域(以 C/C++ 为例,其他语言底层逻辑相通),核心区域划分如下:

内存区域 存储内容 生命周期 特点
栈(Stack) 局部变量、函数参数、返回值 函数调用时创建,返回时销毁 自动分配释放,速度快,空间小(MB 级)
堆(Heap) 动态分配的数据(如 new/malloc 手动分配(new)/ 释放(delete 空间大(GB 级),需手动管理,易内存泄漏
静态区(全局区) 全局变量、静态变量(static 程序启动时创建,退出时销毁 全局可见,默认初始化为 0
常量区(.rodata) 字符串常量、const 常量 程序生命周期全程存在 只读(修改会触发崩溃)
代码区(.text) 程序指令(编译后的机器码) 程序启动时加载 只读,按地址顺序执行

关键结论:数据的存储位置,直接决定其生命周期和访问权限(如常量区不可修改,栈数据自动释放)。

二、基本数据类型的存储(最基础,必须掌握)

基本数据类型(整型、浮点型、字符型)是 "值存储"------ 直接将数据的二进制编码写入内存,无需额外间接地址。

1. 整型存储:补码是核心(解决负数运算问题)

整型包括 char(1 字节)、short(2 字节)、int(4 字节)、long((32位)4/8(64位) 字节),分为有符号(signed)无符号(unsigned) 两类。

(1)无符号整型:直接存储二进制原码

无符号整型只有非负值,存储时直接将十进制数转为二进制原码,填充到对应字节中。

例:unsigned int a = 10(4 字节):

  • 10 的二进制原码:00000000 00000000 00000000 00001010
  • 直接写入内存(地址从低到高或高到低,取决于大小端,后续详细解释大小端)。
(2)有符号整型:存储补码(核心!)

有符号整型需表示负数(最高位为符号位:0 = 正,1 = 负),为解决 "负数加法运算" 矛盾(如 1 + (-1) = 0),采用补码存储:

  • 正数的补码 = 原码(与无符号一致);
  • 负数的补码 = 反码 + 1(反码 = 原码符号位不变,其余位取反)。

例:int b = -10(4 字节):

  1. 原码:10000000 00000000 00000000 00001010(符号位 1 表示负);
  2. 反码:11111111 11111111 11111111 11110101(符号位不变,其余位取反);
  3. 补码:11111111 11111111 11111111 11110110(反码 + 1);
  • 最终内存中存储的是补码。(原因:在计算机系统中,数据一律用补码来表示和存储,原因在于,使用补码,可以将符号位和数值域统一处理,同时加法和减法也可以统一处理(cpu只有加法器),此外,补码与原码相互转换,其运算过程是相同的(解释:原码取反+1得到补码,补码取反+1得到原码),不需要额外的硬件电路)
(3)大小端问题:多字节数据的存储顺序

当数据占多个字节时(如 int 占 4 字节),会涉及 "字节在内存中的排列顺序",即大小端字节序

  • 大端(Big-endian):高位字节存低地址,低位字节存高地址(符合人类阅读习惯);
  • 小端(Little-endian):低位字节存低地址,高位字节存高地址(x86 架构 CPU 主流)。

例:int c = 0x12345678(十六进制,对应 4 字节:0x12 高位,0x78 低位):

内存地址(低→高) 小端存储(x86) 大端存储(如 ARM 某些模式)
0x00001000 0x78(低位) 0x12(高位)
0x00001001 0x56 0x34
0x00001002 0x34 0x56
0x00001003 0x12(高位) 0x78(低位)

判断大小端的代码技巧:用 union(共用体)或指针访问低地址字节,若为低位则是小端。

判断大小端的小程序:

cs 复制代码
int s = 1;
if (*(char*)&s == 1) {
    printf("小端\n");
}
else {
    printf("大端\n");
}

代码解读:(我们暂时统一认为地址由低到高)

我们定义的s是int类型占据4个字节,我们知道1的16进制是0x00000001,其中高位是0x00,低位是0x01,设想一下:

小端架构:0x01,0x00,0x00,0x00

地址:低<------------------->高

大端架构:0x00,0x00,0x00,0x01

通过上述分析,我们就能知道在最第一个地址处大小端是不同的,所以只需要判断第一个地址是不是1就可以判断你的电脑中字节如何在内存中的排列顺序。

  • &s 取的是低地址(第一个字节的地址);
  • (char*)&s 指向这个低地址,解引用后获取的是第一个字节 ;
  • 条件 *(char*)&s == 1 成立 → 输出「小端」否则为「大端」。

这段代码的本质是:通过char*指针精准访问int变量的「首地址 1 字节」,判断该字节是否为int值的「低位字节」

它的优势在于:

  1. 高效:只涉及地址操作和 1 字节读取,无额外内存开销;
  2. 简洁:无需定义结构体 / 共用体(union),代码行数少;
  3. 通用:基于 C 语言的指针和强制类型转换特性,跨平台兼容(只要支持标准 C)。

补充:与共用体(union)判断法的对比

"用 union 判断大小端",原理和这段代码完全一致(都是访问首地址 1 字节),只是实现方式不同:

cs 复制代码
// 共用体判断法(等价逻辑)
union EndianTest {
    int i;
    char c;  // 共用体所有成员共享首地址,c对应首地址1字节
};

union EndianTest et;
et.i = 1;
if (et.c == 1) printf("小端\n");
else printf("大端\n");

两段代码的核心逻辑完全相同,只是前者用「指针强制转换」,后者用「共用体成员共享地址」,最终都是为了访问int变量的首地址 1 字节。


相关推荐
BBB努力学习程序设计33 分钟前
Java:理解数据类型和变量
java
古城小栈34 分钟前
SpringBoot:声明式事务 和 编程式事务 的擂台霸业
java·spring boot·后端
小二·37 分钟前
Java基础教程之网络编程
java·开发语言·网络
乾元37 分钟前
多厂商配置对齐器:AI 如何在 Cisco / Huawei / Juniper 间做语义映射
运维·开发语言·网络·人工智能·网络协议·华为·智能路由器
熊文豪37 分钟前
使用Python快速开发一个MCP服务器
服务器·开发语言·python·mcp
泥嚎泥嚎38 分钟前
【Android】RecyclerView 刷新方式全解析:从 notifyDataSetChanged 到 DiffUtil
android·java
leo_23238 分钟前
SMP(软件制作平台)语言基础知识简介之一
开发语言·smp(软件制作平台)·软件开发工具
努力学算法的蒟蒻39 分钟前
day23(12.3)——leetcode面试经典150
java
言言的底层世界39 分钟前
c/c++基础知识点
开发语言·c++·经验分享·笔记