## 内存分配方式 ## 判别规则(核心,唯一要记的) - 看声明方式定区:**全局/static → 静态区**(程序期,系统收);**函数内局部(无 static)→ 栈**(函数期,系统收);**malloc/free → 堆**(程序员管) - 谁管释放是关键:**只有堆要程序员负责**;栈和静态区都系统自动回收 一句话:**"全局住酒店(静态区),局部住民宿(栈),malloc 自己买房(堆)——前两个店家收房,最后一个自己管。"** ## 抽象描述(一句话本质) C 内存分三区——静态区(程序期)、栈(函数调用期)、堆(malloc 到 free)。每个变量必答三连问:住哪?活多久?谁收? ## 正反例(建立直觉) - **正例1(静态区)**:`int g;` / `static int s;` → 程序启动分配、结束回收,自动清零,活整个程序期 - **正例2(栈)**:`void f(){ int x = 5; }` → 调用时分配、返回自动销毁,快但量小(KB 级) - **正例3(堆)**:`int *p = malloc(100);` → malloc 分配、free 手动释放,不 free 就泄漏 - **反例1**:返回栈地址 → 悬垂指针(函数返回栈销毁,地址变废纸) - **反例2**:malloc 后忘 free → 内存泄漏 ## 易混对比 | | 静态区 | 栈 | 堆 | | ---------- | ---------- | ----------------- | ---------------------- | | 谁分配 | 程序启动 | 函数调用时 | `malloc` | | 生命周期 | 整个程序期 | 函数调用期间 | malloc → free | | 谁管释放 | 系统自动 | 系统自动 | **程序员** | | 大小/速度 | 固定,快 | 小(KB 级),很快 | 大,慢(要查找空闲块) | | 自动初始化 | ✅ 清零 | ❌ 垃圾值 | ❌ 垃圾值 | 陷阱:函数内 `static int x;` 住**静态区**不是栈(static 改的是存储期)。 ## 变体验证(3 题,全过=学会) 1. `const char *s = "hello";` → 字符串字面量在**只读数据区**(静态区一族),**不能修改** 2. 陷阱题:全局 `int g[10];` 元素**默认全是 0**;函数内 `int l[10];` 是**垃圾值**(静态区自动清零 vs 栈不初始化) 3. 陷阱题:`void f(){ int *p = malloc(100); }` 函数返回后 → 指针没了,**堆内存还占着没人能 free → 内存泄漏** ## 口述要点(面试怎么讲) - **结论先行**:三区(静态区/栈/堆)+ 谁管释放(只有堆归程序员) - **为什么栈快堆慢**:栈分配 = 移动栈指针 SP,一条指令完成;堆分配 = 遍历空闲块链表找合适块,可能要分裂/合并,**慢且时间不确定** - **嵌入式为什么不推荐 malloc**:① 内存小 + 碎片化(反复 malloc/free 后大块申请失败)② 分配时间不确定,破坏实时性 ③ 泄漏后果重、重启代价高。替代:静态分配、内存池、FreeRTOS 的 heap_1~heap_5 - **栈溢出**:栈大小有限。每调用一层函数就压一帧(局部变量+参数+返回地址),递归/深嵌套层层压 → 栈耗尽 → 覆盖相邻内存 → HardFault。RTOS 里用 HighWaterMark 检测剩余栈 - **易错点**:函数内 static 变量住静态区不是栈;栈不碎片、堆碎片 ## 关系网络 | 相邻概念 | 和本主题的关系 | 孤立理解会犯的错 | | -------- | --------------------------------------------------------- | ------------------------------------ | | 程序分段 | 三区放一起讲:代码段/数据段/BSS/堆/栈——本题是三区的微观版 | 以为堆栈是"段"(实际是动态区) | | 堆 vs 栈 | 谁分配、谁回收、速度、碎片——三区里最常单独拷的对比 | 以为栈也像堆一样会碎片化 | | static | static 局部 = 静态区,不是栈 | 以为函数内声明就住栈 | | 内存碎片 | malloc/free 反复 → 堆碎片化 | 以为碎片只有大小问题,不关心分配失败 | ## 学习日期 2026-08-03 --- _每主题一页,复习时只翻本目录。_