野指针
概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
目录
一、野指针成因
1、指针未初始化
例:
int main()
{
int* p;
*p = 10;
return 0;
}
代码中p没有初始化,意味着没有明确指向
而对于一个局部变量不初始化的话,放的就是随机值
*p = 10;
此时的p就是野指针(非法访问,当没有初始化时,放入随机值,而*p进行解引用时,通过*p所找到的地址并非是属于我们的)
2、指针的越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*(p++) = i;
}
return 0;
}
当i<10时,p还不是野指针
当i=10时,指针p就超出了数组arr的范围,p就是野指针,用VS编译器运行会出现:
百度直译过来就是:运行时检查失败2-变量“arr”周围的堆栈已损坏。
一般报这种错误,都是内存越界,着重检查:
(1)、多维数组初始化是否有误
(2)、数组下标调用是否越界
3、指针指向的空间释放
int* test()
{
int a = 20;
return &a;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
在以上代码中,看似没有问题,但是这里的p其实就是一个野指针。为什么这样说呢?
原因:当我们调用函数test,调用结束后,返回了一个int*的类型,也就是将a的地址返回到调用函数的地方,即赋给了p,值得注意的是,a这个变量是我们在调用函数时,临时创建的一个变量,函数调用完毕,则那一块内存空间就会被销毁掉,所谓销毁就是将相应的空间还给了操作系统。
这里可能会有人说,不对呀,我运行了,结果是正常的呀,没什么问题的。
啊啊啊,一定不要这样想啊!我打个比方,你的一个好朋友,她给了你一个地址,你通过这个地址就可以去找到她,但是,你们俩吵架了,她悄悄搬家了,却没有告诉你,你依然通过那个地址可以找到那个地址,但是,房子里面却住了别人。
而在上述代码中,你之所以还能将a的值打印出来,那是因为你的好朋友虽然和你吵架了,但她还没有找到合适的地方搬走,但此时你再去找她,是不太合适的,换句话讲,你是无法预知打开门的是不是她,充满了不确定性,所以说p为野指针。
咱们做个测试,因为函数test()的空间已经还给操作系统了,所以我们,再次使用系统中的内存空间,就有可能会影响*p的值
代码:
#include<stdio.h>
int* test()
{
int a = 20;
return &a;
}
int* test1()
{
int b=5;
return &b;
}
int main()
{
int* p = test();
int* p1 = test1();
printf("%d\n", *p);
return 0;
}
在这里,我们在上述代码中,又定义了一个函数test1(),并调用了它一次,而我们还是继续打印*p,注意啊,打印的还是*p,不是*p1,但我们此时来观察输出,如图:
为什么呢?
因为上述中,我们调用完test(),相应空间就还给操作系统了,也就是说,调用test1()时,test1()会向系统申请一块空间,而操作系统是完全可以将刚才分配给test()的空间再次分配给test1(),所以在test1()中,将其对应的地址内容改变成了5。
通俗的说,就是你的好朋友20,已经搬走了,5住进去了,你现在再通过旧的地址去找到的房子时,开门的人就是5了。
二、 如何规避野指针
1、指针初始化
如果有确切的值就给它初始化为相应的值,如果没有,则初始化为NULL
2、小心指针越界
这个问题在自己编写代码时,就需要注意了,因为编译器不会报错
3、指针指向空间释放,则立马将其置为空指针(NULL)
4、避免返回局部变量的地址
5、指针使用之前检查其有效性
在使用前判断指针是否为空指针(NULL),因为空指针它不指向任何对象