动态内存管理中常见错误与陷阱

前言:上篇中我们较为详细地介绍了C语言中动态内存是如何开辟和使用的以及柔性数组等相关概念,但是在实际使用中,动态内存开辟是极易出现一些问题的,本篇将就这些常见问题作以总结,避免大家掉入陷阱。

一.常见的动态内存错误 

1.对NULL指针的解引用操作

void test1()
{
	int* p =(int*) malloc(INT_MAX / 4);
	*p = 20;
	//若p值为NULL,则对NULL解引用了,这是错误的
	free(p);
}

这里在开辟空间后并没有对p进行检验,这样在空间开辟失败时,p为空指针NULL,此时对它解引用会出现问题。正确方式如下:

#include<assert.h>
void test1()
{
	int* p =(int*) malloc(INT_MAX / 4);
	//if(p!=NULL)
	assert(p);
	*p = 20;
	free(p);
}

2. 对动态开辟空间的越界访问

void test2()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		exit(EXIT_FAILURE);
        //非正常退出
	}
	for (i = 0;i <= 10;i++)
	{
		*(p + i) = i;
		//当i=10的时候越界访问了
	}
	free(p);
}

对于动态开辟的内存空间开辟多少使用多少,切不能“僭越”。

3.对非动态开辟内存使用free释放 

void test3()
{
	int num = 10;
	int* p = &num;
	free(p);
	//num的空间不是动态内存开辟的,所以free这里使用不当
}

free所释放的是malloc,calloc所开辟的在堆区上的动态内存空间,而对于非动态开辟的内存空间free无法释放其空间。

4.使用free释放一块动态开辟内存的一部分

void test4()
{
	int* p = (int*)malloc(100);
	p++;
	free(p);
	//此时p已经不再指向动态内存的起始位置
	//使用free释放的的仅仅是一块动态内存开辟内存的一部分
}

当p++后p所指向的位置已经不再是动态内存开辟空间的起始位置,此时再释放空间的话仅仅释放了部分空间,这是操作不当的。并且,我们在使用动态内存空间的时候,尽量不要改变其起始位置的地址!这会让我们找不到所开辟空间的具体起始。

5.对同一块内存多次释放

void test5()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);
	//对同一块空间重复释放
}

一次malloc/calloc搭配一个free,不能二次释放。

6.动态开辟内存忘记释放(内存泄漏) 

void test6()
{
	int* p = (int*)malloc(100);
	if (p != NULL)
	{
		*p = 20;
	}
}
int main()
{
	test6();
	//p是局部变量,在出函数后空间就销毁了,此时就找不到动态开辟的空间了,想释放都释放不了了
	while (1);
}

忘记释放不再使用的动态开辟的空间会造成内存泄漏。

切记:动态开辟的空间一定要释放,并且正确释放。

二.动态内存经典笔试题分析 

例1:

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void test()
{
	char* str = NULL;
	GetMemory(str);//传值调用
	//p在出函数的时候销毁了,对应的空间也找不到了
	strcpy(str, "helloword");
    //对NULL的解引用,程序崩溃
	printf(str);
}

输出结果:(程序崩溃)

这里对GetMemory函数进行的是传值调用(形参是实参的一份临时拷贝),但是当局部变量p在出函数后对应空间销毁,实际上str的值并没有修改。如图:

那我们如何成功将开辟空间的地址传给str呢?有如下修改方式:

改1:
 

void GetMemory(char** p)
{
	*p= (char*)malloc(100);
}

void test()
{
	char* str = NULL;
	GetMemory(&str);//传址调用
	strcpy(str, "helloword");
	printf(str);
	free(str);
	str = NULL;
}

 传值调用不行我们还可以进行传址调用,将str地址传给p,对p解引用就找到了str,也就成功地将malloc开辟空间的地址交给了str。如图:

改2:

char* GetMemory(char* p)//char* GetMemory()
{
	p = (char*)malloc(100);
	return p;
}

void test()
{
	char* str = NULL;
	str=GetMemory(str);//这里也可以不传参
    //str=GetMemory();
	strcpy(str, "helloword");
	printf(str);
	free(str);
	str = NULL;
}

第二种方式是通过return直接将p的值带回来,再用str去接收,也能成功将开辟空间的地址交给str。

例2:

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}

void test(void)
{
	char* str = NULL;
	str = GetMemory();
	//p指向的空间已经被回收了,没有操作权限了
	printf(str);
}

int main()
{
	test();
	return 0;
}

 输出结果:烫烫烫烫烫烫烫烫Ol[ 

这里的问题同样是由于局部变量p在出函数后被销毁,对应的“hello world”的空间我们已经失去了操作权限了,即归还给了操作系统,即便地址还在,但是其所指向的空间也是未知的。

那有人会有疑问,之前的例1的改2也采用了return的方式带回地址,为什么到这里就不行了呢?

注意:malloc开辟的空间不会出函数就销毁,但是数组(局部变量)是出了函数就销毁了
malloc申请的空间是在程序退出或者free的时候才释放空间的。

 例3:

void GetMemory(char** p, int num)
{
	*p =(char*)malloc(num);
}

void test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
    //free(str);
    //str=NULL;
}

int main()
{
	test();
	return 0;
}

其实这个代码已经十分完善了,类似与例1中的改1,但是唯一美中不足的是使用完str后对它没有进行释放。 

例4: 

void test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		//非法访问
		printf(str);
	}
}

int main()
{
	test();
	return 0;
}

 输出结果:world

这道题的问题在于对str进行了提前释放,释放之后再对它使用就是非法访问了。

同时许多人存在一个误区:那就是free会使对应指针置空,实则不然,free的作用仅仅是将开辟的空间的使用权限归还给操作系统,对应str的值还是不会改变的,所以free后还有自行给指针置空,以免其成为野指针。 

以上便是对于动态内存开辟中可能遇到的一些常见问题作以解释,希望大家避免以上错误,加深理解。

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值