野指针概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
一、野指针成因
1、指针未初始化就使用
#include<stdio.h> int main() { int* p; *p = 10; return 0; }
此段代码中,局部变量p未初始化,也就意味着p为随机值没有方向,*p就会非法访问内存空间,此时p就为野指针。
有效改善方法: 初始化为空指针。如果定义指针的时候实在不知道赋什么值,可以先将其定义为空指针,即int*p=NULL;,NULL是代表空指针的意思,后面如果用到指针的话再让指针指向具有实际意义的地址,然后通过指针的解引用改变其指向的内容。
#include<stdio.h> int main() { int* p=NULL; int b = 8; p = &b; *p = 100; printf("%d",*p); return 0; }
2、指针越界访问
#include<stdio.h> int main() { int arr[10] = { 0 }; int i = 0; int* p = arr; for (i = 0; i <= 10; i++) { *p = i; //i=10时越界 } return 0; }
arr整型数组,有10个元素,初始化元素值为0,arr数组名表示首元素地址传给了指针p,通过对指针解引用改变数组元素值,当i=10时,此时*p访问的内存空间不在数组有效范围内,,此时*p就属于非法访问内存空间,p为野指针
3、指针指向空间的释放、
#include<stdio.h> int* test() { int a = 10; return &a; //&a=0x0012ff40 } int main() { int* p = test(); return 0; }
程序开始后首先进入主函数,执行第一步,调用test函数将返回值赋给p,test函数的返回值是局部变量a的地址,假设a的地址为0x0012ff40,由于a只在test函数内有效,出了test函数其内存空间就被释放,也就意味着a的地址编号不存在,短时间内如果再次利用这块地址,它的值还未被改变也就是0x0012ff40还存在,p的值为0x0012ff40,但此时p为野指针,因为p里面所存放的地址是无效的。
二、 野指针的避免
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
三、关于内存空间释放的补充
代码:
#include<stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("%d", *p);
return 0;
}
运行结果:
分析:
由于a是test函数内的局部变量,而局部变量只有被调用时才会为其分配内存空间,所以进入主函数但未进入test函数时变量a没有内存空间,如上图所示
主函数的第一步首先调用test函数,进入test函数后执行到局部变量的定义时就会为其分配内存空间,如上图所示
通过对变量的赋值进而对内存空间赋予编号地址,如上图所示
test函数将局部变量a(int占4个字节)的地址返回给p,即p=0x005BF850,如上图所示
由于test函数运行完后test函数内的局部变量所占的内存空间会被释放,即a的内存空间编号0x005BF850不存在,但那块地址还是在的只不过不属于a了,如果没有其他函数调用这块地址,那块地址的名称暂时可能还是0x005BF850 ,观察图中内存窗口程序执行到p定义那一行时内存并未发生变化因为暂无其他函数使用这块空间,如上图所示
所以进行printf函数时打印p所指向的内容*p时还为10,但此时p为非法访问内存空间,为野指针。但如果在打印之前有其他函数调用了这块地址,这块地址的名称就会发生变化,不再是0x005BF850,打印p所指向的内容*p时不再为10。
观察图中内存窗口可以发现,执行完printf("%d", *p);后内存发生了变化
代码:
#include<stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("Hello\n");
printf("%d", *p);
return 0;
}
#include<stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("Hello\n");
printf("Welcome!\n");
printf("%d", *p);
return 0;
}
运行结果:
分析:
由于进行printf函数打印p所指向的内容*p之前,Hello利用了那块儿内存,使得那块儿内存的名字不再是0x005BF850,变成了Hello地址的编号,所以打印p所指向的内容*p时的值不再是10,此时就为随机值了。
观察图中数据可以发现,程序执行完printf("Hello\n");时内存单元就发生了变化,是因此printf函数调用了内存,使得p的值发生了变化,*p也随着变化。
再执行完printf("Welcome!\n");时内存单元又发生了变化,是因此printf函数调用了内存使得p的值发生了变化,*p也随着变化。
执行完printf("%d", *p);时内存单元又发生了变化,是因此printf函数调用了内存使得p的值发生了变化,*p也随着变化。