C++内存模型
C++内存模型
计算机程序内存模型
高地址
栈 (Stack) | ← 局部变量、函数参数、返回地址(自动分配、函数退出时自动释放) |
---|---|
内存映射区 | ← 动态库映射、mmap 分配的内存 |
堆 (Heap) | ← new / malloc 分配(需要手动释放) |
BSS 段 | ← 未初始化的全局/静态变量 int g1; // 放 BSS |
数据段 (Data) | ← 已初始化的全局/静态变量 int g2=10; // 放 Data |
代码段 (Text) | ← 程序的机器指令,常量字符串 const char* s = “abc”; |
低地址
然后翻译自cpp官方网站的内存模型介绍:
内存模型为 C++ 抽象机器定义了计算机内存存储语义。
C++ 程序可用的内存是一个或多个连续的字节序列。每个字节有自己独有的内存地址。
字节(Byte)
字节是内存中的最小可寻址单元,由连续的多个比特组成。C++ 中,char/unsigned char/signed char 的对象存储和值表示均使用恰好 1 字节。于是,字节中有多少比特,可以通过 std::numeric_limits::digits取得。
内存位置(Memory Location)
内存位置是标量类型(算数类型、指针类型、枚举类型或是 std::nullptr_t)的对象;或是,长度不为零的位域组成的最长连续序列。
struct S {char a; // memory location #1int b : 5; // memory location #2int c : 11, // memory location #2 (continued): 0, // zero-length, 强制对齐到下一个int边界d : 8; // memory location #3struct {int ee : 8; // memory location #4} e;
} obj; // The object 'obj' consists of 4 separate memory locations
这段代码在32位机器上的内存分布是:
Offset 内容
0x00 a (1 byte)
0x01 padding (3 bytes)
0x04 b:5, c:11, unused:16
0x08 d:8, unused:24
0x0C e.ee:8, unused:24
所以为了节省空间,可以采用以下措施:
- 同类型放一起 → 减少不同类型对齐带来的空洞
- 大类型放前,小类型放后 → 利用剩余空间
- 位域合理使用 → 多个位域尽量放同一个存储单元
- 对齐指令 / pragma → 可以压缩结构体,但可能牺牲性能
注意:语言中的许多特性会引入额外的内存位置。这些内存位置程序无法访问,而是为编译器实现自行管理。这些特性例如:引用和虚函数。
大小端访问问题
假设我们有一个 32-bit 整数:
uint32_t x = 0x12345678; // 十六进制表示
小端存储(Intel x86 常见)
内存地址从低到高依次存储:
地址: 0x1000 0x1001 0x1002 0x1003
内容: 0x78 0x56 0x34 0x12
低地址存最低有效字节(0x78),高地址存最高有效字节(0x12)。
大端存储(某些 ARM 或网络协议)
内存地址从低到高依次存储:
地址: 0x1000 0x1001 0x1002 0x1003
内容: 0x12 0x34 0x56 0x78
低地址存最高有效字节(0x12),高地址存最低有效字节(0x78)。
32 位机器的影响
内存访问:如果 CPU 是小端,访问 (uint16_t)(&x) 取低 16 位 → 0x5678
网络传输:网络协议一般使用大端(Network Byte Order)
在小端机器上需要 htonl/ntohl 转换
**注意:**这里两位两位的读取存储,只是因为0X情况下是16位存储,也就是一个数字是2个字节(16位),当32位存储读取的时候,肯定是4个字节,也就是32位,所以会两个两个的存储读取;另外大小端是规定了存储读取的顺序,所以其实当读取出来的时候会自动根据大小端的定义重新排列数据。