进程内存布局:你的数据住在哪

栈、堆、全局区……各放什么、谁负责回收?

商用重叠

◎学完你会

1内存布局分区图:点开看每段放什么、谁回收

程序运行时内存大致分五段(地址从高到低)。点每一段看它装什么、由谁负责回收。

栈 地址较高 · 向下生长
堆 地址较低 · 向上生长
全局 / 静态区
常量区(只读)
代码段(只读) 地址最低
口诀:栈高堆低;局部变量在栈、new 的在堆;全局/静态在中间;常量、代码只读。
点左边任一段。
👉 谁负责回收:栈——函数返回自动回收;堆——必须手动 delete;全局/静态、常量、代码段——程序结束才回收。

2关键命令

写法含义
&局部变量打印栈上地址(通常较高,如 0x7ff...c)
new int在堆上申请内存(地址通常较低),返回指针
sizeof(结构体)看结构体实际占多少字节(含对齐填充)
✅ 想亲眼验证:打印 &局部变量 和 new int 的地址,能直观看到"栈高堆低"。

3栈自动回收,堆必须手动

这是 C++ 内存管理最核心的区别。

void f(){ int a = 5; // 栈上:函数返回,a 自动消失 int* p = new int(5); // 堆上:不 delete,就一直占着 delete p; // 必须手动还回去 }
为什么堆要小心:new 拿到的内存不会自己消失。函数返回时,指针变量 p 没了(栈回收),但它指向的堆内存仍占着——谁都不管,就是内存泄漏。这就是后面 delete 和智能指针要解决的事。

4内存对齐与 sizeof:成员之和 ≠ sizeof

为了 CPU 高效读取,结构体成员之间可能塞入"填充字节",所以 sizeof 常大于成员大小之和。

struct S { char c; int i; }; // sizeof(S) 往往是 8,不是 1+4=5 // c 占 1 字节,后补 3 字节对齐到 4,i 占 4 字节 → 共 8
易错:以为 sizeof(结构体) 等于成员大小之和。实际要算对齐填充。想少占内存,把大的成员排前面、小的排后面,可减少填充。

!易错点

① 返回局部变量的地址/引用——局部变量在栈上,函数一返回就没了,返回它的地址得到悬垂指针(指向已回收的内存)。
② 大数组放栈导致栈溢出——栈空间有限(通常几 MB),几百万元素的数组应放堆或全局。
③ 以为 sizeof 等于成员之和——忽略了对齐填充,算出的字节数不对。
④ 深递归栈溢出——每层递归都在栈上压帧,递归太深把栈撑爆 → 栈溢出(Segmentation fault)。

?跨学科:内存五段,就像一栋楼的五层

栈像"临时休息区":人来(进函数)占个位置,人走(函数返回)自动腾空——不需要你收拾。堆像"自助储物柜":你自己打开柜门存东西(new),离开前必须自己锁好柜门(delete),忘了锁就是泄漏。

全局/静态区像"公司档案室":程序一开工就建好,一直放到下班才归档。常量区、代码段像"消防手册":只读,贴墙上不许改。分清"谁收拾",就不会把堆上的东西丢在栈上乱放。

✎练一练

函数里的普通局部变量(如 int a = 5;)存在哪、由谁负责回收?
局部变量在栈上,函数返回时由栈机制自动回收,不需要也不允许手动 delete。
在函数里声明一个超大数组(如几百万个 int)会发生什么?
栈空间有限(通常几 MB),超大局部数组瞬间把栈撑爆 → 栈溢出。应放堆(new/vector)或全局。
struct S { char c; int i; }; 的 sizeof(S) 通常是?
c 占 1 字节后,为让 i 按 4 对齐会补 3 字节填充 → 8。sizeof 常大于成员之和,别忽略对齐填充。