详解C/C++动态内存函数(malloc、free、calloc、realloc)

8 篇文章 3 订阅

1. malloc和free

为解决静态内存开辟存在的问题,C语言提供了一个动态内存开辟的函数:

ffb4c1c3a2a349e195811d2e35d1d916.png

 malloc为memory allocation的简写,意为内存分配。

这个函数的作用是向内存申请一块连续可用的空间,并返回指向这块空间的指针。

比如,我申请40个字节的空间,以前我们可以用数组的方法,现在我们用malloc函数申请40个字节的空间:

	//int arr[10];
	int* p = (int*)malloc(40);

9038c407e55d4f37b5d7b0864f3d8267.png

malloc函数有以下特点: 

如果开辟成功,则返回一个指向开辟好空间的指针;

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;

返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定;

如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。 

所以我们需要对malloc开辟的空间做检查:


	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");//如果开辟失败,告知原因
		exit(-1);//开辟失败直接退出
	}

需要注意的是:

对于malloc函数申请的内存空间,当程序退出时还给操作系统;当程序不退出时,malloc动态申请的内存不会主动释放。


所以这时C语言提供了另外一个函数free,专门用来做动态内存的释放和回收:

fb18f46997634541bc7e81e21dcc37c1.png

用法如下:

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");//如果开辟失败,告知原因
		exit(-1);//开辟失败直接退出
	}
	free(p);
    p = NULL;//避免p被释放后成为野指针
	return 0;
}

需要注意的是:

free函数用来释放动态开辟的内存;

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的(也就是说free函数只能释放动态开辟的空间,不能释放静态开辟的空间);

如果参数 ptr 是NULL指针,则free函数什么事都不做。

 (malloc和free都声明在stdlib.h头文件中)


2. calloc

C语言还提供了一个函数叫calloc ,calloc为contiguous allocation的简写,意为动态内存分配并清零,calloc函数也用来动态内存分配。

原型如下:

2a899cb315a24bd7b5dabe2478751473.png

函数的功能是为 num个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0;

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

具体用法与malloc大致相同:

int main()
{
	int* p = (int*)calloc(10, sizeof(int));//10个大小为4字节的元素
	if (p == NULL)
	{
		perror("calloc");//如果开辟失败,告知原因
		exit(-1);//开辟失败直接退出
	}
	//打印已开辟好的元素
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
    free(p);
    p = NULL;//避免p被释放后成为野指针
	return 0;
}

d0bdd40423704cc493aed54f70170d3c.png


3. realloc

在我们使用malloc函数与calloc函数申请过空间之后,

我们可能会遇到申请的空间过大了,比如我动态申请了1000个字节的空间,可发现我只需要10个字节的空间;

又有可能遇到申请的空间过小了,比如我动态申请了100个字节的空间,可最后发现我却要10000字节的空间;

针对这两种现象,为了合理地使用内存,我们一定会对内存空间做灵活地调整,那 realloc 函数就可以做到对动态开辟内存大小的调整。

realloc函数原型如下:

2e638f5144fa455599e352f2649f9700.png

解释:

1.ptr 是要调整的内存地址;

2.size 调整之后新大小;

3.返回值为调整之后的内存起始位置;

4.这个函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

 关于第3条,我们抛出这样的疑问:

返回值为调整之后的内存起始位置,那么这个调整之后的起始位置,

①到底是realloc函数保留了原来malloc/realloc函数已经开辟好的空间,只是在原来内存的基础上紧挨着新增了一些空间,然后返回了原来旧内存空间的起始地址;

②还是realloc函数舍弃了原来malloc/realloc函数已经开辟好的空间,又再另一块地方重新开疆拓土,申请了一块新的内存空间,然后返回了新内存空间的起始地址。

对于这个问题,其实两种说法都对,我们要分两种情况讨论:

情况一:原有空间之后有足够大的空间。

45cc70756b264333b9b7ea88f05753fd.png

情况二:原有空间之后的空间不足

1.开辟新的空间;
2.将旧的空间中的数据拷贝到新的空间;
3.释放旧的空间;
4.返回新空间的起始地址。

d23bbdc207bb4a6485fa732ec1e6c096.png

所以针对情况一和情况二,对realloc函数的正确用法应为:

int main()
{
	//先用malloc开辟40个字节的空间
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");//如果开辟失败,告知原因
		exit(-1);//开辟失败直接退出
	}
	//开辟成功后,将这块空间初始化为1~10
	for (int i = 0; i < 10; i++)
	{
		p[i] = i + 1;
	}
	//再用realloc再增加40个字节的空间
	int* ptr = (int*) realloc(p, 80);//未避免情况二的发生,不能用p接收新地址
	if (ptr != NULL)
	{
		p = ptr;//如果开辟成功了,再把ptr拷贝给p
		ptr = NULL;
	}
	else
	{
		perror("realloc");//如果开辟失败,告知原因
	}
	//打印数据
	for (int i = 0; i < 20; i++)
	{
		printf("%d\n", p[i]);
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

运行结果:

3f614f8686ba4fdaad67d2388c7a656e.png


验证:

情况一:新开辟的空间比较小,后面有足够的空间,(新开辟4字节):

a76cf40bb150479ca2c593c7835137e6.png

情况二:原有空间之后的空间不足(新开辟400字节)

b2aa9b5c044d46e6b966810dc524e204.png

(小知识:如果p为空指针,那么realloc与malloc的功能相同;如果缩小空间,那么就一定是情况一了。)

(本篇完)

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C语言中,动态内存分配与释放是非常常见的操作。C语言提供了四个函数来实现动态内存分配与释放,分别是malloc、callocreallocfree。 1. malloc函数 malloc函数用于在内存堆中分配一块指定大小的内存空间,并返回其首地址。其基本语法如下: ```c #include <stdlib.h> void* malloc(size_t size); ``` 其中,size_t是一个无符号整型类型,用于表示需要分配的内存空间大小(以字节为单位)。malloc函数返回一个void类型的指针,指向分配的内存空间的首地址。如果分配失败,则返回NULL。 例如,以下代码用于在内存堆中分配10个int类型的数组空间: ```c int *p = (int *) malloc(10 * sizeof(int)); ``` 2. calloc函数 calloc函数用于在内存堆中分配一块指定大小的内存空间,并将其清零,然后返回其首地址。其基本语法如下: ```c #include <stdlib.h> void* calloc(size_t num, size_t size); ``` 其中,num和size都是size_t类型的整数,用于表示需要分配的内存空间大小(以字节为单位)。calloc函数返回一个void类型的指针,指向分配的内存空间的首地址。如果分配失败,则返回NULL。 例如,以下代码用于在内存堆中分配10个int类型的数组空间,并将其清零: ```c int *p = (int *) calloc(10, sizeof(int)); ``` 3. realloc函数 realloc函数用于重新调整已经分配的内存空间的大小,可以扩大或缩小内存空间。其基本语法如下: ```c #include <stdlib.h> void* realloc(void* ptr, size_t size); ``` 其中,ptr是需要重新调整大小的内存空间的首地址,size是需要调整的大小(以字节为单位)。realloc函数返回一个void类型的指针,指向调整后的内存空间的首地址。如果调整失败,则返回NULL。 例如,以下代码用于将之前分配的10个int类型的数组空间扩大为20个int类型的数组空间: ```c int *p = (int *) malloc(10 * sizeof(int)); p = (int *) realloc(p, 20 * sizeof(int)); ``` 4. free函数 free函数用于释放之前分配的内存空间,将其归还给内存堆。其基本语法如下: ```c #include <stdlib.h> void free(void* ptr); ``` 其中,ptr是需要释放的内存空间的首地址。调用free函数后,该内存空间将不再可用,程序不能再访问该内存空间中的数据。 例如,以下代码用于释放之前分配的10个int类型的数组空间: ```c int *p = (int *) malloc(10 * sizeof(int)); free(p); ``` 需要注意的是,为了避免内存泄漏和野指针问题,程序员需要谨慎使用动态内存分配与释放,尽量避免不必要的内存分配和释放。同时,为了确保程序的稳定性和可靠性,程序员需要在使用动态内存分配时,充分考虑内存分配失败的情况,并做好错误处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值