野指针(Wild Pointer / Dangling Pointer)

前言

野指针(Wild Pointer / Dangling Pointer) 是编程中一个非常危险的概念。

简单定义

野指针 是指一个指针变量,它存储的地址是无效的、已经释放的、或未初始化的内存地址

当你通过这个指针去读写内存时,就会发生未定义行为(Undefined Behavior),可能导致:

  • 程序崩溃(段错误)
  • 数据损坏
  • 难以复现的诡异 Bug
  • 安全漏洞

常见产生原因

1. 栈内存被回收后继续使用(最经典)

func foo() *int {
    x := 42          // x 在栈上
    return &x        // 返回 x 的地址
}

func main() {
    p := foo()       // p 指向 foo 的栈帧
    fmt.Println(*p)  // 危险!foo 返回后栈帧可能被覆盖 → 野指针
}

注意:Go 编译器会通过逃逸分析x 自动分配到上,避免这个问题。

2. 手动释放内存后继续使用(C/C++ 常见)

int* p = malloc(sizeof(int));
*p = 10;
free(p);     // 内存已释放
*p = 20;     // 野指针!p 现在是 dangling pointer

3. 未初始化指针

int* p;      // p 是野指针(垃圾地址)
*p = 100;    // 直接崩溃或乱写内存

4. 局部变量地址返回(栈已销毁)

在 Go 语言中

  • Go 没有手动 free,主要靠 GC 管理堆内存。
  • 野指针风险大大降低,但仍然存在:
    • 使用 unsafe.Pointer 不当
    • 闭包 + 协程导致的生命周期问题
    • CGO 中调用 C 代码返回的指针
    • 指向已被 GC 回收的对象(极少见,但 unsafe 下可能)

Go 编译器通过逃逸分析主动把可能产生野指针的变量搬到堆上,这就是"堆内存存在的意义"。

如何避免野指针

  • Go:尽量少用 unsafe,相信编译器的逃逸分析。
  • C/C++:严格遵守 malloc 后必须 free,并把指针置为 NULL;推荐使用智能指针(std::unique_ptrshared_ptr)。
  • 良好习惯:指针使用后立即置空;不要返回局部变量地址。

一句话总结

野指针 = 指向"已经不存在或无效内存"的指针,是内存安全事故的头号元凶之一。