9.野指针.md 4.2 KB

野指针

判别规则(核心,唯一要记的)

  • 指针指向的内存"不确定" → 未初始化
  • "曾经有效但已失效" → 悬垂(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


每主题一页,复习时只翻本目录。