堆、栈、静态区、常量区、自由存储区一次讲清
很多人第一次在面试里听到'内存模型'会懵——以为在问大小端,或者把'堆/栈/全局区/常量区/代码区'背一遍就完了。 真正卡人的点在于:你看到的'5 个区域'并不是 C++ 标准定义的唯一划分;网络上常额外引入'自由存储区',于是概念开始互相打架
0. 为什么越看越乱?
你可能见过类似下面两段(很多博客/笔记都会这么写):
- 'C++ 内存分 5 个区域:堆、栈、全局/静态区(.data/.bss)、常量区(.rodata)、代码区(.text)'
- '根据对象生命周期,内存模型有三种:自由存储区/动态区/静态区……(还有'局部非静态变量 (栈)'塞进同一条)'
问题在于:它们混用了两个维度:
- OS / 可执行文件 / 进程地址空间的'段与区域'(
.text/.data/.bss/.rodata/stack/heap/...) - C++ 语言层面对象的存储期(lifetime / storage duration)(自动、静态、动态、线程)
这两个维度是'映射关系',不是'包含关系'。
所以:不存在'代码区里还有静态区/动态区';.text就是放指令的,静态/动态讲的是对象生命周期或分配方式。
1. 面试建议答案
C++ 里常用两种视角来描述内存:
- OS/二进制段视角(实现相关):程序加载到内存后通常包含
.text:代码段(通常只读 + 可执行).rodata:只读数据(字符串字面量、部分常量等,具体看编译器/平台).data:已初始化的全局/静态数据.bss:零初始化/未显式初始化的全局/静态数据- 运行时还有
stack(栈)和heap(堆)等
- C++ 对象存储期视角(标准更关心这个):
- 自动存储期:局部变量/参数(通常在栈)
- 静态存储期:全局变量、
static变量(常在.data/.bss,只读的可能在.rodata) - 动态存储期:
new/delete管理(自由存储区 free store,通常由堆/内存池等实现) - 线程存储期:
thread_local(TLS)
补一句区分点:heap 常指 malloc/free 的分配来源;free store 指 operator new/delete 管的内存,两者常重合但不必须相同。
2. 看到的'5 类 + 自由存储区'到底该怎么理解?
你列的这几类:
- 堆:
malloc/free - 栈:局部变量
- 全局/静态存储区:全局变量、静态变量
- 常量存储区:常量
- 自由存储区:
new/delete
正确解读方式是:它们不是严格互斥的五块地,而是不同层级的叫法。
2.1 栈(stack)
- 是什么:线程调用栈,保存调用帧(返回地址、保存寄存器、局部变量等)
- C++ 对应:自动存储期对象(局部非
static) - 特点:分配释放极快(移动栈指针),容量相对有限
2.2 全局/静态存储区(通常 .data/.bss)
- 是什么:承载静态存储期对象的区域
.data:已初始化的全局/静态变量.bss:零初始化/未显式初始化的全局/静态变量(加载时清零)- C++ 对应:全局变量、命名空间作用域变量、
static变量、函数内static
常见误区:有人说'C++ 里不区分
.data/.bss'。更准确的说法是:C++ 标准不关心这些段名,但主流平台实现依然会区分。
2.3 常量存储区(常说 .rodata)
- 是什么:实现层面的'只读数据页/只读段'
- 常见内容:字符串字面量、只读表、部分常量对象
- 关键补刀:
- '常量一定在
.rodata'不是标准保证 - 编译器可能把常量直接内联进指令、寄存器或做常量折叠;因此'常量区'更多是工程上的经验总结
- '常量一定在
2.4 堆(heap)
- 是什么:常指 C 运行库/系统提供的动态分配区域(
malloc/free) - 特点:容量大但分配/释放相对慢,可能碎片化
- 实现现实:现代分配器往往用分桶、arena,底层可能来自
mmap/VirtualAlloc等'映射区'
2.5 自由存储区(free store)
- 是什么:
operator new/delete所管理的动态内存来源 - 与 heap 的关系(面试高频点):
- 很多实现里
operator new最终调用malloc⇒ 看起来 free store ≈ heap - 但也可以被替换/重载,走内存池、引擎分配器等 ⇒ free store 不等于 heap(不必须同源)
- 很多实现里
一句话总结:
heap 是实现层常见的动态分配地盘;free store 是 C++
new/delete的语义归属。
3. '代码区里还有静态区/动态区'为什么是错的?
.text(代码段)存的是指令;'静态/动态'描述的是对象的存储期或分配方式- 你可以说'静态存储期对象通常位于
.data/.bss(只读的可能在.rodata)',但不能说'静态区在.text里' - 现实里确实有'和代码强相关的数据'(如 vtable、RTTI),但它们仍是数据,常见落在
.rodata(具体实现相关),不是.text
4. 追问速答
Q1:new/delete 和 malloc/free 区别?
new:分配内存 + 构造对象delete:析构对象 + 释放内存malloc/free:只管理原始字节,不调用构造/析构
Q2:函数内 static 变量在哪里?什么时候初始化?
- 存储期:静态存储期(通常在
.data/.bss) - 初始化时机:第一次执行到定义处初始化;C++11 起要求线程安全
Q3:const/constexpr 一定在 .rodata 吗?
不一定。可能在 .rodata,也可能被常量传播/折叠,直接嵌入指令或优化掉(实现与优化决定)。
Q4:thread_local 呢?
线程存储期(TLS),每个线程一份;段名/布局是实现相关(常见 .tdata/.tbss 等概念)。
Q5:栈上的东西'只在当前函数及下一层有效'对吗?
不严谨。更准确是:自动对象在其作用域结束时销毁。是否'下一层函数可用'取决于是否通过引用/指针传递,以及是否越界/悬空。
5. 收口
'
.text/.data/.bss/.rodata/stack/heap是 OS 与编译链接实现层面的划分;C++ 标准更关心对象存储期:自动、静态、动态、线程。new/delete的 free store 通常由堆或内存池实现,和malloc/free的 heap 经常重合但不必相同。'
6. 如何自证(不只会背)
如果面试官继续深挖'你怎么证明变量在哪个段',你可以说:
- Windows/MSVC:
- 用
dumpbin /headers xxx.exe看段信息 - 或在 VS 里看反汇编/内存窗口
- 类布局可用编译选项:
/d1 reportAllClassLayout(或reportSingleClassLayoutXXX)输出对象内存布局
- 用
- Linux/Clang/GCC:
readelf -S a.out看段表nm -n a.out/objdump -t看符号与地址objdump -d看.text反汇编
对象'落在哪个段'很多时候属于实现细节;你验证的是'当前平台/当前编译选项下的实际结果'。

