## 野指针 ## 判别规则(核心,唯一要记的) - 指针指向的内存"**不确定**" → **未初始化** - "**曾经有效但已失效**" → **悬垂**(dangling) - "**在合法对象之外**" → **越界** - 三者都算野指针 防护三件套:**定义即初始化(NULL)+ free 后置 NULL + 边界检查。** 一句话:**"没定、失效、越界 = 野;初始化、置空、查界 = 防。"** ## 抽象描述(一句话本质) 野指针 = 指向"不确定/已失效/越界"内存的指针。解引用它 = 读垃圾值,或访问非法地址(段错误 / 嵌入式 HardFault 复位)。嵌入式裸机无 MMU 隔离,野指针直接打穿整个固件。 ## 正反例(建立直觉) - **正例1(未初始化)**:`int *p;` 后直接 `*p = 5;` → 往随机地址写。防:定义时初始化 `int *p = NULL;` 或 `= &某变量` - **正例2(悬垂)**:`int *p = malloc(10); free(p); *p = 5;` → 指向已归还的内存。防:free 后立刻 `p = NULL`。另一变体:`int *f(){ int x = 5; return &x; }` 返回栈变量地址,函数返回后失效 - **正例3(越界)**:`int a[10]; int *p = a; p[10] = 5;` → 走到数组外。防:边界检查 - **反例1**:指向静态/全局变量的指针不是野指针——生命周期有效,安全 - **反例2**:NULL 解引用 ≠ 野指针——NULL 是"显式初始化的空"(可控状态),野指针是"失控的指";都会崩但根因不同 ## 易混对比 | 类型 | 成因 | 例子 | 防 | | -------- | ------------------- | ---------------------------- | ----------------- | | 未初始化 | 声明没赋初值 | `int *p; *p = 5;` | 定义即初始化/NULL | | 悬垂 | 指向已释放/失效内存 | `free(p)` 后还用;返回栈地址 | free 后置 NULL | | 越界 | 走出数组/分配范围 | `p[10]`(数组只有 10 个) | 边界检查 | ## 变体验证(3 题,全过=学会) 1. `int *f() { int arr[5]; return arr; }` → **悬垂**(返回局部数组地址,栈销毁后失效) 2. 陷阱题:全局变量 `int g = 5;` 后 `int *p = &g;` → **不是野指针**(全局变量活整个程序期) 3. 陷阱题:`int *p = NULL; *p = 1;` → **不是野指针**,是 NULL 解引用——NULL 是可控的空,野指针是失控的指 ## 口述要点(面试怎么讲) - **结论先行**:三类野指针(未初始化/悬垂/越界)+ 防护三件套(初始化/置空/查界) - **为什么 free 后置 NULL**:让指针指向明确,防止它变成悬垂野指针再去操作失效内存;不置也行(只要不再用),但置 NULL 是防御习惯,拦的是"万一再用" - **悬垂 vs 内存泄漏**:不是一回事——悬垂 = 用了已释放的(多用),泄漏 = 该释放没释放(少用) - **嵌入式为什么野指针直接重启**:PC 有 MMU(地址翻译+权限检查),进程访问非法地址被 MMU 拦截 → 只杀当前进程(段错误);MCU 裸机大多没有 MMU,任务共享同一地址空间,野指针打到不存在的地址 → 总线错误 → **HardFault** → 无 handler 就死循环,看门狗超时 → 复位重启 - **MMU vs MPU**:MMU = 完整地址翻译 + 权限检查,进程级隔离,Linux 的根基(PC 靠它);MPU = 只做区域访问权限检查、不做翻译,Cortex-M4/M7/M33 常见,能挡一部分野指针但任务不隔离。FreeRTOS on STM32 → 无 MMU(顶多有 MPU)→ 野指针打穿共享内存 - **易错点**:把 NULL 解引用当成野指针;把悬垂和内存泄漏混为一谈 ## 关系网络 | 相邻概念 | 和本主题的关系 | 孤立理解会犯的错 | | --------------- | ---------------------------------------------------------------------------------------- | ------------------------------------ | | 内存泄漏 | 悬垂 = 用了已释放的;泄漏 = 该释放没释放。一多一少 | 把两者混为一谈 | | NULL | NULL 是"可控的空"(显式初始化),野指针是"失控的指" | 以为 NULL 就是野指针 | | 堆 vs 栈 | 悬垂多发生在堆(free 后)和栈(返回局部地址) | 以为野指针只和堆有关 | | HardFault / MMU | 嵌入式野指针归宿 = 总线错误 → HardFault → 复位;MMU 只在带虚拟内存的系统(如 Linux)存在 | 以为野指针只是"值不对",忽视崩溃后果 | ## 学习日期 2026-08-03 --- _每主题一页,复习时只翻本目录。_