📜 C/C++ 程序内存分区详解:从实验到工具验证
1. 内存分区概述
程序运行时,内存被划分为多个逻辑区,各司其职:
- 代码区(Text):存储可执行指令,只读。
- .rodata 段:存储只读数据(如字符串字面量 “Hello” 和 const 全局变量)。
- Data段:存储已初始化的全局变量和静态变量(如
int global = 42;
)。 - BSS段:存储未初始化的全局变量和静态变量(默认初始化为
0
)。 - 堆区(Heap):动态分配的内存(
malloc
/new
),需手动管理。 - 栈区(Stack):存储局部变量和函数参数,自动管理。
2. 实验验证内存布局
通过代码打印变量地址,结合工具验证存储位置:
//basestore.c
#include <stdio.h>
#include <stdlib.h>
// 已初始化的全局变量 → Data段
int global_init = 42;
// 未初始化的全局变量 → BSS段
int global_uninit;
// const常量 → .rodata(只读)
const int global_const = 100;
int main() {
// 局部变量 → 栈区
int local_stack = 5;
// 动态分配内存 → 堆区
int *heap_var = (int*)malloc(sizeof(int));
printf("全局初始化变量地址: %p\n", &global_init);
printf("全局未初始化变量地址: %p\n", &global_uninit);
printf("全局常量地址: %p\n", &global_const);
printf("栈变量地址: %p\n", &local_stack);
printf("堆变量地址: %p\n", heap_var);
free(heap_var);
return 0;
}
运行结果:
全局常量地址: 0x5561d8ffc008 (.rodata)
Data段地址: 0x5561d8ffe010 (Data段)
BSS段地址: 0x5561d8ffe018 (BSS段)
栈变量地址: 0x7ffcd9f56dac (栈区)
堆变量地址: 0x55620cf8b2a0 (堆区)
3. 内存布局示意图
高地址
┌───────────────────┐
│ 栈区 │ ← 向下增长(局部变量)
├───────────────────┤
│ 堆区 │ ← 向上增长(动态分配)
├───────────────────┤
│ BSS段 │ ← 未初始化全局变量(默认0)
├───────────────────┤
│ Data段 │ ← 已初始化全局变量
├───────────────────┤
│ .rodata段 │ ← 全局常量(只读)
├───────────────────┤
│ 代码区 │ ← 可执行指令
低地址
4. 工具验证:nm、size、objdump
(1) 使用 nm
查看符号表
nm -n basestore | grep "global_"
输出:
0000000000002008 R global_const # .rodata
0000000000004010 D global_init # Data段
0000000000004018 B global_uninit # BSS段
- 符号类型:
R
→ 只读数据段(.rodata)。D
→ 已初始化数据段(Data)。B
→ 未初始化数据段(BSS)。
(2) 使用 size
查看段大小
size basestore
输出:
text data bss dec hex filename
2186 628 12 2826 b0a basestore
- Data段:628 字节(包含用户变量和编译器内部数据)。
- BSS段:12 字节(仅用户变量占 4 字节)。
(3) 使用 objdump
查看Data段内容
objdump -s -j .data basestore
输出:
basestore: file format elf64-x86-64
Contents of section .data:
4000 00000000 00000000 08400000 00000000 .........@......
4010 2a000000 *...
十六进制值 0x2a → 十进制 42(global_init的值)
5. 关键结论
- 内存分区顺序:代码区 → .rodata → Data → BSS → 堆 → 栈。
- Data与BSS地址接近:用户变量少,段总大小由编译器内部数据主导。
- 只读段保护:尝试修改
.rodata
会触发段错误。 - 工具验证:
nm
、size
、objdump
是分析内存布局的利器。
6. 一句话总结
内存分区各司其职:代码只读,Data存初值,BSS省空间,堆手动管,栈自动清!
动手实验:尝试修改代码,添加更多变量,观察地址变化,加深理解! 🔍