11、野指针与悬空指针:它们是怎么产生的?如何避免?

指针这东西,用好了是利器,用不好就是自残。我见过太多新手甚至老手,在指针上栽跟头。今天咱们就聊聊两个最经典的指针问题——野指针和悬空指针。

说白了,野指针就是「不知道指向哪」的指针。悬空指针则是「曾经指向对的地方,但现在不对了」的指针。两者都会导致程序崩溃,而且崩溃得毫无规律,特别难排查。

野指针是怎么产生的?

野指针的产生,通常就三种情况:

  • 指针未初始化:定义了一个指针变量,但没有给它赋值。它里面存的是个随机地址。
  • 指针指向已释放的内存:这块内存被 free 或 delete 了,但指针还保留着原来的地址。
  • 指针操作越界:比如数组访问超出范围,指针跑到了不该去的地方。

我举个例子,你看看是不是很眼熟:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p;  // 野指针!没初始化
    *p = 10; // 危险!不知道写到哪里去了
    
    int *q = (int*)malloc(sizeof(int));
    free(q);
    *q = 20; // 悬空指针!内存已经释放了
    
    int arr[5];
    int *r = arr;
    r[5] = 30; // 越界!数组只有5个元素
    
    return 0;
}

你看,这三行代码,每一行都是定时炸弹。我在项目中遇到过类似的情况,一个同事调试了整整两天,最后发现是数组越界导致指针乱飞,把别的变量给覆盖了。

悬空指针又是怎么回事?

悬空指针,其实可以看作是野指针的一种特殊情况。它的特点是:指针曾经有效,但现在无效了

最常见的场景就是:

  • 堆内存被释放后,指针没有置空
  • 函数返回了局部变量的地址
  • 指针指向的对象生命周期结束了

来看个经典例子:

#include <stdio.h>

int* getValue() {
    int a = 100;
    return &a;  // 悬空指针!a是局部变量,函数结束就销毁了
}

int main() {
    int *p = getValue();
    printf("%d\n", *p); // 未定义行为!可能打印100,也可能崩溃
    
    return 0;
}

为什么会这样?因为 a 是栈上的局部变量,函数返回后栈帧被回收,那块内存可能已经被别的函数用了。你再去读,读到的就是垃圾数据。

嗯,这里要注意:编译器有时候会给你一个警告,但不会报错。很多新手看到程序能跑,就觉得没问题了。其实这是最危险的——它可能在你机器上跑得好好的,换台机器就崩了。

如何避免?我总结了几条铁律

我个人习惯,写指针代码时脑子里始终绷着一根弦。下面这几条,是我多年踩坑踩出来的经验:

避坑指南

  1. 指针定义时立即初始化:不知道指向哪,就赋 NULL
  2. 释放内存后立即置空:free 之后马上 p = NULL
  3. 不要返回局部变量的地址:用动态内存或者传参
  4. 使用前检查指针是否为空:if (p != NULL) 再操作
  5. 数组访问时注意边界:别越界,别越界,别越界

我曾经在一个嵌入式项目里,因为一个悬空指针,导致系统运行三天后突然死机。查到最后,发现是某个模块释放了内存,但另一个模块还在用那个指针。从那以后,我定了个规矩:谁分配谁释放,释放后必须置空

代码示例:正确做法 vs 错误做法

咱们直接看代码,对比一下:

// 错误做法
int *p;
*p = 5;  // 野指针

// 正确做法
int *p = NULL;
p = (int*)malloc(sizeof(int));
if (p != NULL) {
    *p = 5;
}

// 错误做法
int *q = (int*)malloc(sizeof(int));
free(q);
*q = 10;  // 悬空指针

// 正确做法
int *q = (int*)malloc(sizeof(int));
free(q);
q = NULL;  // 置空!

你想想看,多写一行 q = NULL 能费多少事?但这一行,可能就帮你省下几天的调试时间。

知识体系图

下面这张图,把野指针和悬空指针的关系、产生原因、避免方法都串起来了:

野指针与悬空指针知识体系 指针问题 野指针(未初始化/越界) 悬空指针(释放后/返回局部地址) 未初始化 数组越界 指针运算错误 free后未置空 返回局部变量地址 生命周期 避免方法:初始化 + 置空 + 检查边界 定义时赋 NULL free 后立即置空 使用前判空

小技巧:调试时如果怀疑指针问题,可以在关键位置打印指针的值。如果发现指针的值是 0xCDCDCDCD(VC 调试模式下)或者 0xDEADBEEF,那基本就是野指针或悬空指针了。

警告:千万不要以为「我小心一点就不会出问题」。指针问题最大的特点就是随机性——今天不崩不代表明天不崩,这台机器不崩不代表那台机器不崩。老老实实按规范写,才是正道。

最后说一句:指针这东西,你越怕它,它越欺负你。把规矩立好,养成好习惯,它就是你手里最锋利的刀。


公众号:蓝海资料掘金营,微信deep3321