初识C语言---野指针

野指针是指未初始化、越界访问或指向已释放内存的指针,可能导致程序崩溃或不可预测行为。本文详细阐述了野指针的三种常见成因,并提供了预防措施,包括初始化指针、防止越界和及时置NULL。同时,通过实例分析了内存空间释放后野指针的问题,揭示了内存管理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 野指针概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

 

 一、野指针成因

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也随着变化。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值