c语言动态内存分配——基本必会知识

为什么要动态内存空间分配

有时候我们需要的空间只有在程序运行的时候才知道,那么数组的固定开辟空间的方式不能满足需求。

动态内存分配函数

1.malloc函数和free函数

头文件:#include<stdlib.h>
使用格式:malloc(总字节数);
malloc的返回值类型为:void*
所以malloc函数并不知道开辟空间的类型,具体类型要靠使用者进行强制类型转换
作用:向内存申请n个空间,申请成功后返回申请的空间的首元素的地址

	int* p =(int*) malloc(11);
	if (p == NULL)
		printf("%s\n", strerror(errno));
	else
	{
		for (int i = 0;i < 10;i++)
			*(p + i) = i;
		for (int i = 0;i < 10;i++)
			printf("%-5d", *(p + i));
	 }
	//当动态申请的空间不再使用时
    //就可以将内存还的操作系统
	free(p);
	p=NULL;

当申请的空间不再使用时,应该主动将申请的空间还给操作系统。
否则会一直占用空间到程序结束。如果在程序的结尾就没有太大的必要,因为在程序结束时会自动返还空间。
虽然指针p指向的空间被释放(既空间存储的参数清除),但指针p依然指向这份空间的地址,使用p依然可以找到这份空间。为了避免发生错误,在释放p的空间时要将p指向空指针。
malloc函数存在开辟失败的情况,所以malloc的返回值一定要检查。
free函数释放的空间必须是动态内存开辟的空间
free函数和malloc函数的头文件都是<stdlib.h>
malloc函数与free函数要成对使用

2.calloc函数

头文件:#include<stdlib.h>
使用格式:calloc(元素个数,每个元素的大小);
calloc的返回值类型为:void*
所以calloc函数并不知道开辟空间的类型,具体类型要靠使用者进行强制类型转换
作用:向内存申请n个空间并初始化为0,申请成功后返回申请的空间的首元素的地址
calloc函数与free函数要成对使用

3.realloc函数

头文件:#include<stdlib.h>
使用格式:realloc(要调整的内存地址,调整之后的空间大小);
realloc的返回值类型为:void*
所以realloc函数并不知道开辟空间的类型,具体类型要靠使用者进行强制类型转换
作用:调整动态开辟内存空间的大小

int* p2 = (int*)realloc(p, 40);
if(p2!=NULL{   //先检查是否追加成功,如果追加成功则覆盖在原指针上
  p=p2;
}
free(P);
p=NULL;

realloc函数作用机理图:
在这里插入图片描述

这时我们想要追加新的空间,有两种可能:
1.原先空间后面的空间足够,则在原先空间后面追加空间即可,那么返回原来空间的地址。

在这里插入图片描述

2.原先空间后面的空间不足,则新找一整块足够的空间重新开辟,并将原先空间中的数值复制过去,
复制后会自动释放原来的空间,那么返回新空间的地址。
在这里插入图片描述

realloc函数注意事项:
1如果p指向的空间之后有足够的内存空间,则字节追加,后返回p。
2如果p指向的空间之后没有足够的内存空间,则realloc函数和重新找一个新的内存区域来开辟一块满足需求的空间,并且将原来空间的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间的地址
3.倘若需要开辟的空间过大,realloc函数可能开辟失败,如果开辟失败,那函数将返回一个空指针。那么结果是不仅开辟失败,而且原来的开辟的地址丢失,无法查找,所以realloc函数的返回值一般不会直接放到原指针中。
realloc函数与free函数要成对使用

常见的动态内存分配错误

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

	int* p = (int*)malloc(40);
	//万一malloc失败了,p就被赋值为NULL
	for (int i = 0;i < 10; i++)
		*(p + i) = i;
	free(p);
	p = NULL;
避免方法:
在使用动态内存分配函数后一定要对返回值进行检查。

2.对动态开辟的内存的越界访问

	int* p = (int*)malloc(20);
	//只开辟了五个元素
	if (p == NULL)
		return 0;
	else
		for (int i = 0;i < 10;i++)
	//访问了十个元素,产生越界访问
			*(p + i) = i;
	free(p);
	p = NULL;
避免方法:
养成在malloc函数填写参数时写(元素个数*每个元素的大小),明确总元素个数。
例如malloc(5*sizeof(int))

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

	//常规开辟的内存存放在栈区
	//而动态开辟内存存放在堆区
	int a = 10;
	int* p = &a;
	*p = 20;
	free(p);
	p = NULL;
		常规开辟的内存存放在栈区
     	动态开辟的内存存放在堆区
     	free函数无法释放常规开辟的内存空间

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

int* p = (int*)malloc(40);
	if (p == NULL)
		return 0;
	else
		for (int i = 0;i < 10;i++)
			*p++ = i;
	//释放空间
	free(p);
	//因为p++,p所指向的空间地址已发生变化
	//free无法释放动态开辟空间的一部分
	p = NULL;
避免方法:
应在程序中避免让指针移动,找到代替代码。释放时注意指针所指向的位置
例如:在循环中(*p++)=*(p+i)

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

int* p = (int*)malloc(40);
	if (p == NULL)
		return 0;
	else
		for (int i = 0;i < 10;i++)
			*(p+ i) = i;
	free(p);
	//......
	free(p);
	p = NULL;
避免方法:
1.团队中谁开辟谁释放。
2.一定要在free函数后面加p=NULL,这样即使后面再释放p也对程序无影响。

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

	while (1)
	{
		malloc(1);
	}
如果不释放内存,那么对于这段空间自己不使用,而别人也无法使用,
则会白白占用大量内存,损耗性能。
避免方法:开辟使用后及时检查是否释放
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值