什么是野指针? 指针变量存的地址是一块非法内存地址。进而形成野指针。但是需要注意一点,野指针不是NULL指针。
1 野指针的概念
- 野指针变量中的值是非法内存地址,进而形成野指针
- 野指针不是NULL指针,是指向不可用内存的地址的指针
- NULL指针并无危害,很好判断,也很好调试
- C语言的语言层面无法判断一个指针所保存的地址是否是合法的。
那么野指针是如何产生的呢?
以下在代码中的编程不规范会导致野指针的产生:
- 局部指针变量没有被初始化(我们知道局部变量不初始化就是随机值,所以如果局部指针被赋为随机值,那么这个随机值所代表的地址很有可能是不能访问的,是非法的地址。那么它就是野指针)
- 指针所指向的变量在指针被使用之前被销毁。比如在函数的返回值是指针。在函数返回后,函数的调用栈就被销毁了,接着使用被返回的指针时,这个指针所指向的内存已经被销毁,所以形成野指针
- free指针后,还依然使用指针,这也是野指针
- 进行了错误的指针运算和错误的强制类型转换(后面代码案例会分析)
1.1 野指针代码案例初探
- 代码41-1.c
#include <stdio.h>
#include <malloc.h>
int main()
{
int* p1 = (int*)malloc(40);
int* p2 = (int*)1234567;
int i = 0;
for(i=0; i<40; i++)
{
*(p1 + i) = 40 - i;
}
free(p1);
for(i=0; i<40; i++)
{
p1[i] = p2[i];
}
return 0;
}
- 使用gcc4.4.5编译器编译上述代码:上述代码编译没有问题,运行会出现段错误。如果使用较新版本的编译器,可能编译都会直接报错。
- 分析错误原因:
段错误一般就是野指针问题:
- 首先第8行将1234567强制转换为int*指针变量并赋值给p2。然后在第29行使用p2。1234567这个地址,肯定是不能访问的。所以p2就是野指针。所以运行时访问p2指针指向的内存就会产生段错误
- 16行已经将指针p1的内存释放。但是在20行又使用p1.这也是野指针,会造成段错误
- 第13行中,因为p1在第7行只申请了40字节也就是10个int的内存。所以13行使用p1加了40次,肯定会越界访问了本不是p1申请的内存。这也会造成段错误
2 如何避免野指针
遵循以下几条规则,避免野指针的产生
- 绝不在函数中返回局部指针变量或者局部数组
- 任何变量在定义后,必须先初始化
- 字符数组必须确认最后一个元素是‘\0’结束符才能将该数组当成是字符串,否则不能将其当成字符串。因为字符串的所有操作都是基于最后的’\0’结束符,如果没有这个结束符,很有可能在操作字符串的时候产生内存越界
- 任何使用与内存操作相关的函数必须指定长度信息。在C语言中,长度信息很重要,它往往是变量类型的一部分。如数组。如下图:
2.1 野指针代码案例分析进阶
- 代码41-2.c
#include <stdio.h>
#include <string.h>
#include <malloc.h>
struct Student
{
char* name; //这里有指针,很容易忘记初始化
int number;
};
char* func()
{
char p[] = "D.T.Software";
return p; //返回局部数组/指针,gcc4.4.5编译器会编译警告
}
void del(char* p)
{
printf("%s\n", p);
free(p); //free之后要将p置NULL
}
int main()
{
struct Student s; //s中有指针变量,这里没有初始化,容易产生野指针
char* p = func(); // 返回的指针指向的内存已经被销毁
strcpy(s.name, p); //p指向的内存已经被销毁不能使用,且s中的name指针没有初始化也会产生野指针
s.number = 99;
p = (char*)malloc(5);
strcpy(p, "D.T.Software"); //内存越界
del(p);
return 0;
}
- 上述代码编译就会产生警告。警告第15行返回局部指针。程序运行产生段错误
- 其他的会产生野指针的地方在代码中已经详细的说明。自己分析即可。
3 总结
- 知道野指针的由来以及如何避免产生野指针。这是非常重要的!!!