动态内存管理详细讲解

在这里插入图片描述

1.动态内存管理的作用

2.动态内存开辟方式

2.1.malloc

2.2.free

2.3.calloc

2.4.realloc

3.完结撒花

    创作不易,望三连支持🙏,谢谢啦qaq

1.动态内存管理的作用

众所周知,在计算机上所存储的各种信息都是在内存中开辟一块指定的空间在里面进行存储的。
在先前的学习中我们已经掌握了许多内存开辟的方式,比如:

int i = 0;//在栈空间上开辟了4个字节
char arr[10] = {0};//在栈空间上开辟了10个字节的连续空间

而对于上述内存开辟方式有两个特点:
1.空间开辟的大小是固定的。
2.数组在声明的时候需指明数组的长度,长度确认了之后大小便不可再修改。
但是有时候对于空间大小的需求是在我们运行程序的时候才会知道,那么上述开辟空间的方式就行不通了。
在C语言中是引入了动态内存开辟,其作用就是程序员在编写代码的途中可以按照自己的需求去申请和释放任意大小的空间,这样就比较灵活了。

2.动态内存开辟方式

在c语言中进行动态内存的开辟使用,就需要学习malloc,calloc,realloc,free这四个动态内存函数。我们要进行动态内存开辟,首先我们需要去向内存申请我们所需要的空间大小,而上述前三个函数就是进行内存的开辟所使用的,开辟完空间后我们就可以任意使用所开辟的空间来进行存储,在最后对之前所开辟的空间不再使用时需要我们对其进行释放,以便以后的读取使用,上述最后一个函数就是用来释放之前所开辟的内存空间,下面我们就进入这四个函数的学习。

2.1.malloc

malloc用来进行内存的开辟,我们看下图进行学习:
在这里插入图片描述malloc函数,其返回类型为void*,需要传一个无符号的数 size,其头文件使用stdlib.h或者malloc.h都可以,下面方框中指出所传的形参size是malloc要开辟的空间大小,单位为字节。
向malloc传一个无符号数之后,malloc函数就会向内存申请一块连续可用的空间,并返回指向这块空间地址void类型的指针。如果开辟失败,比如开辟空间过大,内存不能满足,那么就会返回一个空指针NULL,因此malloc函数的返回值必须进行检查。而malloc函数的返回类型为void,所以并不知道开辟空间的类型,在使用的时候需要使用者自己来确定。如果参数size为0,那么malloc函数的行为标准是未定义的,这取决于编译器。
下面我们实际上代码为大家进行讲解:
在这里插入图片描述上面我们就是使用malloc函数创建了40个字节的内存大小,这里我们是使用这40个字节来储存了一个整形数组,
所以对于malloc所返回的指针我们就要把其强转为int类型,再创建一个int类型的变量进行接受。接收后第一步就是要判定其是否为空指针,如果p确实为空指针那说明malloc再内存开辟失败了,我们就可以使用perror函数把错误码打印出来,之后就return 1结束函数。如果p不是空指针就代表40字节的空间已经创建好,p里面存放的是这块空间的首地址,下面我们就可以去使用这些空间去存储数组,如上图所示。
我们存储完数组并且打印出来后,我们的目的已经达成,便不再需要这块空间,之后我们就需要对其进行释放。

2.2.free

在这里插入图片描述我们先看上面的函数原型void free(void* memblock),free是没有返回值的,free函数形参需要传递之前所开辟内存的首地址,如果memblock指向的不是动态开辟的内存,那么free函数的行为是未定义的,并且memblock是空指针的话,那么函数什么事都不会做。
我们上面讲解的代码补齐:
在这里插入图片描述可以看到对于free函数的使用只用传一个所开辟空间的起始地址即可将所开辟的空间进行释放。
这里再提醒大家一点,因为我们在开辟动态内存的最后都是要将先前开辟的动态内存进行释放的,而free函数释放是需要把创建动态内存的起始地址传过去的,所以我们创建储存malloc函数返回值的指针在使用过程中的不能被改变的,可以看到我在用指针p对所开辟的空间进行访问的时候始终都没有改变p的值。
在代码的最后我还操作将p赋值了空指针。free函数是不会改变p的值的,而我们创建使用并释放了动态内存后,但是p里面还是保留这刚创建的动态内存的起始地址,假设起始地址为0x0012ff40,如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/12b42ce331d647038fadd5f3c09d85cb.png

这是很危险的,可能会引发野指针问题,所以我们在完成一系列操作后,是必须要将p赋值为空指针。

2.3.calloc

c语言还提供了calloc函数来进行动态内存的开辟,我们看下图:
在这里插入图片描述可以看到其函数原型为void* calloc(size_t num,size_t size),与malloc不同,calloc的形参多了一个参数,但是其参数所表示的意思是与malloc一样的,num表示所开辟类型的个数,size表示要开辟类型的大小,单位为字节。calloc函数的功能就是为num个大小为size的元素开辟一块空间,而其与mallic函数的区别就是calloc会在返回地址之前把开辟的空间内每个字节都初始化为全0。
举一个例子:
在这里插入图片描述上图代码就是拿calloc开辟的内存空间,并没有对内存空间进行赋值,打印出来所有元素就是0。
也就是说如果我们需要对申请的内存空间进行初始化,那么使用calloc便可以方便的完成任务。

2.4.realloc

这个才是今天的重头戏。
realloc函数的出现才使动态内存管理更加灵活。
有时候我们会发现开辟的空间太小了,有时候我们又发现开辟的空间太大,那么为了合理的使用内存,我们要对开辟内存的大小进行灵活的调整,而realloc函数就可以做到对动态开辟内存大小的调整。
我们看下图进行学习:
在这里插入图片描述realloc函数原型为void* realloc(void*memblock,size_t size),对于其参数,size表示所要开辟的内存空间大小,而memblock上图翻译是:指向先前分配的内存块的指针,就是指先前创建的动态内存的起始地址,而对于realloc函数的返回值是分了三种情况:
1、realloc调整空间失败,那么会返回空指针NULL。
2、realloc调整空间成功,但是在已经开辟好的内存后边没有足够的空间进行扩大,比如下面这种情况:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/87c6088481cc48858ab9a6d524ede0dd.png
假设原先已经开辟了40个字节(图中绿色方块),而现在想再开辟40个字节的空间进行使用,但是在原先开辟的空间后面内存不足40个字节,图中绿框后面红框代表还要开辟的49个字节,但是一部分已经被紫色框占领,这时候,realloc函数会在内存空间的堆区重新找一个满足新的空间大小需求的空间,同时会把旧的数据拷贝到新的空间里面,然后释放旧的空间,同时realloc函数返回的是新的空间的起始地址。
3、realloc调整空间成功,在已经开辟的动态内存空间有足够的空间进行扩大,比如下面这种情况:
在这里插入图片描述在原先开辟的40个字节的后面是有足够的空间再开辟40个字节的,所以就可以直接进行内存空间扩大,然后返回旧的空间的起始地址。
下面我们看下边代码示例给大家进行讲解:

int main()
{
	//int* p = (int*)malloc(40);
	int* p = (int*)calloc(10, sizeof(int));
	//检验返回值
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用所创建的内存空间
	/*for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}*/
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//所创建的空间不够,需要进行扩容
	int* ptr = (int*)realloc(p, 5 * sizeof(int));
	if (ptr != NULL)
	{
		p = ptr;
	}
	else
	{
		perror("realloc");
	}
	//空间使用
	for (int i = 10; i < 15; i++)
	{
		*(p + i) = i;
	}
	//打印
	for (int i = 10; i < 15; i++)
	{
		printf("%d ", *(p + i));
	}
	//使用完后进行释放
	free(p);
	p = NULL;
	return 0;
}

其运行结果为:
在这里插入图片描述上面代码是之前讲calloc敲的,我进行改进了一下,下面我们直接从扩容的地方进行讲起,这里我们是进行了5个整形大小的扩容,需要注意的是,我们用realloc进行扩容之后并没有直接拿先前所使用的指针p进行接收返回值,因为我们刚讲过,如果realloc调整空间大小失败的话函数返回的是空指针,而我们如果直接拿指针p去接收,万一realloc函数调整失败,那么就会将空指针NULL赋值到指针p当中去,而指针p的值被改变,之前所开辟的空间也就不能再找回来了,所以我们在接收realloc函数的返回值时必须验证是否为空指针,如果不是才能将其值在赋给指针p。
而后面就是指针就简单使用,这里就不多阐述了。
而对于realloc函数除了能实现内存空间调整,它还能实现与malloc函数相同的功能,看下面代码:

int main()
{
	//开辟动态内存空间
	int* p = (int*)realloc(NULL, 40);
	//使用
	。。。
	//释放
	free(p);
	p = NULL;
	return 0;
}

上面代码,对realloc函数第一个参数所传的指针为空指针,realloc函数实现的功能与malloc函数的功能是一样的,开辟了一块40字节的空间。

3.完结撒花

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步
在这里插入图片描述

  • 38
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值