7. 野指针:产生原因、危害、如何避免
野指针,说白了就是指向「垃圾内存」的指针。它不像 NULL 指针那样老老实实指向 0 地址,而是指向一块你根本不知道是什么的地方。我刚开始学 C 语言时,觉得指针已经够难了,野指针更是让我吃了不少苦头。
嗯,咱们今天就把野指针彻底讲清楚。你想想看,一个指针变量里存了个地址,但这个地址对应的内存要么被释放了,要么压根没分配过,要么已经不属于你了——这就是野指针。
7.1 野指针是怎么产生的?
我在项目中遇到过三种最常见的野指针场景,几乎每次排查内存问题都能碰到它们。
7.1.1 指针未初始化
局部指针变量如果不初始化,它的值是随机的。你拿着这个随机地址去读写,后果不堪设想。
#include <stdio.h>
int main() {
int *p; // 野指针!p 的值是随机的
*p = 10; // 危险!往随机地址写数据
return 0;
}
这段代码一跑,轻则段错误,重则把别的进程的数据给改了。我曾经在调试一个嵌入式程序时,就因为少了个初始化,导致系统随机重启,查了两天才找到原因。
7.1.2 指针指向已释放的内存
用 free() 释放了内存,但指针还保留着原来的地址。这时候指针就成了「悬空指针」。
#include <stdlib.h>
int main() {
int *p = (int *)malloc(sizeof(int));
*p = 42;
free(p); // 内存已释放
*p = 100; // 野指针!这块内存可能已被系统回收
return 0;
}
为什么 free 之后还能写?因为 free() 只是把内存标记为「可用」,并没有把指针变量清空。p 里存的地址还在,但那个地址已经不属于你了。
7.1.3 指针操作越界
数组下标越界,或者指针偏移超出了合法范围,也会产生野指针。
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
p += 10; // 越界了!p 指向了 arr 之外的内存
*p = 999; // 危险操作
return 0;
}
这种错误在循环里特别容易犯。我记得有一次写排序算法,循环条件写错了,结果指针一路跑到了栈空间,把函数返回地址都给改了——程序直接飞了。
7.2 野指针的危害有多大?
野指针的危害,我总结为三类:
| 危害类型 | 具体表现 | 排查难度 |
|---|---|---|
| 程序崩溃 | 段错误(Segmentation Fault),直接退出 | 中等 |
| 数据污染 | 修改了不该改的数据,结果诡异 | 高 |
| 安全漏洞 | 被攻击者利用,执行任意代码 | 极高 |
最可怕的是数据污染。程序不崩溃,但计算结果全是错的。你想想看,一个银行转账系统里,野指针把余额给改了——这问题上线了才被发现,那后果...
7.3 如何避免野指针?
我个人习惯用三个方法,基本能杜绝 99% 的野指针问题。
7.3.1 初始化指针为 NULL
声明指针时立刻初始化。如果暂时不知道指向哪里,就赋值为 NULL。
int *p = NULL; // 好习惯
NULL 指针虽然也不能直接解引用,但至少你能用 if (p != NULL) 来检查。野指针你根本没法判断它是否合法。
7.3.2 free 之后立即置 NULL
这是我最强调的一点。释放内存后,顺手把指针置为 NULL。
free(p);
p = NULL; // 避免悬空指针
我曾经在一个团队里推行过这个规范。有个同事嫌麻烦,觉得「反正后面不用了」。结果三个月后,那段代码被复用,free 之后又解引用,线上出了事故。从那以后,全组都老老实实加上了 p = NULL。
7.3.3 避免返回局部变量的地址
函数返回时,局部变量会被销毁。如果返回了局部变量的地址,调用方拿到的就是野指针。
int* get_value() {
int a = 10;
return &a; // 危险!a 是局部变量
}
int main() {
int *p = get_value();
printf("%d\n", *p); // 未定义行为
return 0;
}
正确的做法是用 malloc 分配内存,或者把结果通过参数传回来。
7.4 野指针知识体系
下面这张图总结了野指针的核心知识点,方便你快速回顾:
- 野指针指向非法内存,比 NULL 指针更危险
- 三大来源:未初始化、释放后未置空、越界访问
- 三大危害:崩溃、数据污染、安全漏洞
- 避免方法:初始化、free 后置 NULL、不返回局部变量地址
嗯,野指针这块内容就这些。说白了就是一句话:管好你的指针,别让它指向不该去的地方。我做了这么多年 C 语言开发,每次写指针操作时都会多问自己一句:「这个指针现在安全吗?」——这个习惯救了我很多次。
公众号:蓝海资料掘金营,微信 deep3321