动态内存管理


在这里插入图片描述

为什么要用动态内存管理

#include<stdio.h>
int main()
{
  int arr[20] = {0};
 return 0;
}

这样创建数组大小就是固定的,无法进行动态调整大小
所以我们引入了4个动态内存管理的函数

拓展:malloc,calloc,realloc,free在内存中的堆区
局部变量和形式参数在栈区
静态变量和全局变量在静态区

一.malloc函数

  • 函数原型:void* malloc(size_t size);
  • 申请的是一块连续存放的空间
  • malloc向内存申请了字节大小为size的动态的空间
  • 如果申请成功返回指向指向这块空间首元素的指针
  • 如果申请失败,则返回NULL,所以一定要检查是否为NULL
  • size = 0,理论上可行,但是申请0字节没意义
int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		perror("malloc");
		//给它显示错误,并打印在屏幕上
		return 1;
		//提前返回
	}
	//使用
	//...
	for (int i = 0; i < 5; i++)
	{
		*(p + i) = i + 1;
	}
	
	free(p);
	//将申请的空间还给操作系统
	//也就是将空间释放
	p = NULL;
	//不使用置为NULL,不置为NULL,是野指针
	//野指针很危险

	return 0;
}

注意:
malloc函数不对空间中的内容进行初始化,这些内容是随机值

二.calloc函数

  • 函数原型:void* calloc(size_t num,size_t size)
    malloc和calloc函数非常相似
    注意:
    唯一的区别是malloc函数在malloc函数会在返回空间地址之前把所有申请的空间的内容初始化为0
int main()
{
	int* p = (int*)calloc(5, 20);
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}

	//使用
	//...
	for (int i = 0; i < 5; i++)
	{
		*(p + i) = i + 1;
	}

	free(p);//把申请空间释放掉,后面会介绍
	p = NULL;

	return 0;
}

三.realloc函数

  • 函数原型:void* realloc(void*str,size_t size)
  • realloc函数可以调整开辟完空间后的大小
  • str是指向要开辟空间的起始空间,size是调整为size的空间大小(比如原来空间是10字节,size是20,就是将原空间调整为20),返回的指针可以是任何类型
  • 返回值与前面一样,如果调整成功,返回开辟好空间的首地址,如果调整失败,则返回NULL
int main()
{
	int* p = (int*)calloc(5, 20);
		if (p == NULL)
		{
			perror("calloc");
			return 1;
		}
	
		//使用
		//...
		for (int i = 0; i < 5; i++)
		{
			*(p + i) = i + 1;
		}//将开辟好的空间内容赋值
		int* str = (int*)realloc(p, 100);
		//调整成功(返回值不为NULL),就使用
		if (str != NULL)
		{
			p = str;//使用
			for (int i = 0; i < 10; i++)
			{
				*(p + i) = i + 1;
			}

			free(p);//释放空间,后面介绍
			p = NULL;
			str = NULL;
		}
		else
		{
		 //否则调整失败,打印错误信息
			perror("realloc");
			free(p);//释放空间,后面介绍
			p = NULL;
			str = NULL;
		}

	return 0;
}

注意:
recalloc调整空间大小的时候有3种情况:

1.刚好后面的空间是足够存下调整后的空间的大小的
注意:空间是连续存放的,这里没画好
在这里插入图片描述
2.后面的空间不足以装下调整后的空间
所以会在堆区重新找一份新的空间,并且大小符合要求
并且会把原有的20字节的内容拷贝到新开辟的空间中
原有空间会被释放掉
返回新开辟空间的起始地址
在这里插入图片描述
红色区域是已使用的空间

3.需要扩展的空间后方没有空间可以扩张,并且堆中也没有符合要求的空间给50字节扩张,所以无法正常开辟空间,会返回NULL
在这里插入图片描述

四.free函数

  • 函数原型:void free(void* str)
  • free函数就是用来释放malloc,realloc,calloc动态开辟的空间的
  • free函数释放空间,为了避免内存的泄露(内存的泄露指的是这块内存空间使用过后没有还给操作系统,并且之后也不在使用这个指针,这块空间就一直被占用,周而复始,空间就会越来越少,也会使系统的性能下降)
  • 如果传入的NULL,那么free函数什么也不做
free(pf);
pf = NULL;//它的使用就是这么简单

五.常见的动态内存错误

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

int* pf = (int*)malloc(20);
*pf = 30;//如果pf是NULL的话,就有问题
         //所以要判断pf是不是NULL

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

int main()
{
	int* pf = (int*)malloc(20);//5个int
	if (pf == NULL)
	{
		perror("malloc");
		return 1;
	}

	//使用
	for (int i = 0; i <= 5; i++)//6个int
	{
		*(pf + i) = i + 1;
	}


	return 0;
}

检查是否越界访问

3.对非动态开辟的空间使用free函数

int a = 0;
int* p = &a;
free(p);

free函数只能使用在动态开辟的内存释放

4.使用free释放一部分动态开辟的空间

int* pf = (int*)malloc(sizeof(int)*5);
pf++;
free(pf);
pf = NULL;

注意:
free函数释放空间的指针要指向这块空间的起始地址
才是完整释放动态的空间

  1. free函数对同一块内存空间的多次释放
int* pf = (int*)malloc(sizeof(int)*5);
free(pf);
free(pf);

要解决这个问题非常简单,只要这个指针不使用了,要把它立即置为NULL,这样再对它进行多次释放,指针为空,free函数为空,什么也不做,就不存在对同一块空间的多次释放

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

void test()
{
 int* pf = (int*)malloc(sizeof(int)*5);
 if(pf != NULL)
 {
  *pf = 90;
 }
}
int main()
{
  test();
  return 0;
}

尽量做到谁(函数)申请的空间谁来释放
如果不能释放,记得告诉别人,记得要释放

  • 46
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值