【动态内存】C语言动态内存使用常见错误及其避免方法(初学者避雷)

C语言动态内存使用常见错误及其避免方法(初学者动态内存避雷手册)

求个赞求个赞求个赞求个赞 谢谢🙏

先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️

动态内存开辟常见错误
1.对NULL指针的解引用
2.越界访问
3.使用free释放非动态开辟的空间
4.使用free释放动态内存中的一部分
5.对同一块动态开辟的空间,多次释放
6.动态开辟的空间忘记释放-比较严重

对空指针的非法解引用

一般来说,有一定基础的我们都不会故意解引用空指针,但是,我们在使用动态内存开辟的时候,可能会解引用到”无形“的空指针

int main()
{
	int*p=(int*)malloc(10000000000000000000);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;//err
	}
	return 0;
}

在上面这个代码中,看似没有什么问题。但是我们知道malloc函数是有可能开辟动态内存空间失败的,我们一口气开辟这么多字节的空间,我们堆上空间不够用了,malloc函数就会开辟失败,我们通过www.cplusplus.com网站搜索malloc函数我们可以知道,如果函数开辟失败,会返回空指针,那么这个时候,我们的p就是一个空指针,空指针怎么能解引用呢?
我们可以在for之前用一个perror()函数来查看我们的问题perror("main:");

在这里插入图片描述
为了避免在编写程序的时候遇到这个问题,我们可以用assert()断言后再使用该指针,或者使用if语句,当指针为真的时候再使用该指针

越界访问问题

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	//越界访问,我们只开辟了40个字节,不是四十个整型
	for (i = 0; i < 40; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

正如这个代码,我们必须知道malloc函数在堆上开辟的时候的单位是字节,而我们这里却要访问40个整型,也就是160个字节,这就越界访问了
每写一步我们思考清楚就可以避免这个问题。

使用free释放非动态开辟的空间

free只能用来释放堆上的动态开辟的空间,而不能释放栈上的空间,栈上的内存空间,在函数结束,程序结束都会自动释放的。

//3.使用free释放非动态开辟的空间
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	free(p);//err
	p = NULL;
	return 0;
}

每写一步我们思考清楚就可以避免这个问题。

使用free释放动态内存中的一部分

释放了,但没完全释放

//4.使用free释放动态内存中的一部分
int main()
{
	int* p = malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p++ = i;//我的p不见了
	}
	free(p);//err
	p = NULL;
	return 0;
}

*在这个代码中,由p指针指向的动态内存开辟后,我们的p在后续操作中改变了它自己的值,这意味这什么?
这代表没东西再可以找到这块空间了,我们到时候free§只是free()掉了p后面的一些空间,那么p前面的空间呢?不管了?
所以如果p这个位置改变了,找不到了是一件很可怕的事情。

那么,我们如何避免呢?
如果在后续我们要操作p,我们一开始就要用另一个的指针变量来记录我一开始p的位置,最后free()这个记录起始位置的指针变量,这样就可以避免“free()了,但没完全free()的问题”

对同一块动态开辟的空间,多次释放的问题

//5.对同一块动态开辟的空间,多次释放
int main()
{
	int* p = (int*)malloc(100);
	//使用
	//...
	free(p);//万一一个在函数里面,没发现,就会出现这种情况

	//
	free(p);
	return 0;
	//所以这个时候,我们需要再free完,立刻把p置为空指针
	//这样的话,第二次调用free()的时候,传空指针,什么事情都不会发生
}

看到这里,有些伙伴可能会问:我肯定不会两个free写在一起啊
这里其实只是比较明显而已,如果在函数里面,已经free了,后面又写了很多代码,最后想起来又free一次,这样就会出现这种问题了

为了避免这个问题,我们一定要主动地在free()之后,马上把我们的p置空:p=NULL;这样我们后面就算在free(),free()函数传NULL给它的时候,是不会有任何问题的。

动态开辟的空间忘记释放(比较严重的问题)

//6.动态开辟的空间忘记释放
void test()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		return;
	}
	//使用
	//忘记释放了
	// 
	// 
	// 
	//当我们出函数的时候,p会自动释放,那没人可以找到那块在堆上的空间了
	//如果return以后,就没有人可以找到p指向的空间了
	//这样会造成内存泄露
}
int main()
{
	test();
	return 0;
}

我们在前面介绍部分释放这个问题的时候已经知道,不free()会造成内存泄漏
在这里,我来为大家总结一下堆上动态开辟的空间的两种释放方式
1.程序员代码主动释放,即调用free()函数
2.程序结束
在我们一开始使用动态内存的阶段,这个问题可能看似没那么严重,程序好像照样跑,反正泄露一点也没关系,程序结束它就自动释放了
但如果我们这是一个服务器的程序呢,24小时跑呢,假如内存只有32个g,每天泄露一点,那机器不就越来越卡了?,最后只会卡死,卡死了就重启,又卡死,再重启。这种问题就会对我们的机器的使用造成很大困扰。所以内存泄漏其实是一个比较严重的问题,所以我们一定要记得free().

尾声

看到这里,相信你对动态内存使用的一些雷区有了更深的了解,也相信你已经学会了避免这些问题的方法。
如果你感觉这期博客对你帮助的话,别忘了转发,收藏,点赞,关注,一波四连再走哦!

posted @ 2021-10-27 12:06  背包Yu  阅读(5)  评论(0编辑  收藏  举报  来源