灵活掌控:深入理解C语言的动态内存管理

目录

引言

一、为什么要有动态内存分配?

二、malloc

三、free

四、calloc 

五、realloc

六、常见的动态内存错误

1、对 NULL 的解引用

2、对动态内存开辟空间的越界访问

3、对非动态内存开辟的空间使用 free

4、用 free 释放动态内存空间的一部分

5、对同一空间的反复释放

七、结语


引言

在编写高效且灵活的C语言程序时,内存管理扮演着至关重要的角色。静态分配虽然简单易用,但在面对变长数据结构和复杂需求时,动态内存管理提供了更强的弹性。通过合理地申请、使用和释放内存,我们不仅可以优化资源利用,还能避免潜在的内存泄漏问题。本篇博客将带你全面了解C语言中的动态内存管理机制,掌握相关的核心技术与实践技巧。

一、为什么要有动态内存分配?

因为灵活。现实情况中,有很多的不确定因素,不确定的数量,不确定的大小,如果设置的容量小了不够用,大了又会浪费空间,这时候就绪需要用到动态内存来灵活的设置容量大小。

二、malloc

该函数包含在 <stdlib.h> 中,它的原型如下:

void* malloc(size_t size);

功能:向内存的堆区申请一块连续可用的空间

返回:如果申请成功,则返回这块空间的地址,

           申请失败,则返回 NULL

举个栗子:

//向内存申请一块连续的能放下10个整型的空间
int* ret = (int*)malloc(10 * sizeof(int));
//判断申请是否成功
if (ret == NULL)
{
	perror("malloc");
	return 1;
}

注意:malloc 的返回值为 void* 类型,因此我们需要对齐强制类型转换!转换为你想要的类型,int* ,  char* 等等,甚至结构体指针

三、free

该函数包含在头文件 <stdlib.h> 中,它的原型如下:

void free(void* ptr);

 功能:释放开辟的动态内存

无返回值

举个例子:

//释放掉刚才开辟的空间
free(ret);
ret = NULL; //防止野指针

在我们用完一块空间后一定要记得释放,不然指向该空间的指针被修改或者销毁,那么你将永远找不到这块空间!而这块空间还在占用你的内存!

四、calloc 

该函数包含在 <stdlib.h> 中,它的原型如下:

void* calloc(size_t num, size_t size);

功能:为 num 个大小为 size 的元素开辟一块空间

返回:成功返回空间地址,失败返回 NULL

举个栗子:

//向内存申请一块连续的能放下10个整型的空间
int* p = (int*)calloc(10 ,sizeof(int));
//判断申请是否成功
if (p == NULL)
{
	perror("malloc");
	return 1;
}
//……使用
//释放
free(p);
p = NULL;

注意:calloc 与 malloc 一样都用于开辟一块动态内存,不同的是,calloc 会给每个空间初始化为 0 ,而 malloc 不会 

五、realloc

该函数也是包含在 <stdlib.h> 中,它的原型如下:

void* realloc(void* ptr, size_t size);

功能:调整动态内存的大小

ptr : 被调整的空间地址

size : 调整之后的新大小,单位是字节

返回:成功返回调整之后的新地址,

           失败返回 NULL

举个栗子:

//向内存申请一块连续的能放下10个整型的空间
int* p = (int*)calloc(10 ,sizeof(int));
//判断申请是否成功
if (p == NULL)
{
	perror("malloc");
	return 1;
}
//调整大小为80个字节
int* p2 = (int*)realloc(p, 40 + 40);
//判断是否调整成功
if( p2 == NULL)
{
   perror("realloc");
   return 1;
}
p = p2;
//释放
free(p);
p = NULL;

这里为什么要用 p2 接收扩容后的空间呢?

因为如果扩容失败,realloc 就会返回 NULL,如果用原来的 p 去接收,那么 p 就被置为 NULL,那么,原来的那块空间就彻底跟你 say 拜拜了

那么,realloc 的细节是什么?真的是扩容吗?

其实不是的,realloc 只是申请一块新的空间,然后把源空间的内容拷贝到新空间,然后在返回新空间的地址

六、常见的动态内存错误

1、对 NULL 的解引用

平时大家肯定不会反这种错误,但有时候 NULL 会伪装自己,蒙蔽了广大学者的慧眼,比如:

int* p = (int*)malloc(INT_MAX);
for (int i = 0; i < 10; i++)
{
	p[i] = i;
}

INT_MAX 是有符号int类型的最大值 21247483647 ,很大的一个数字,就会让内存申请失败,返回NULL,而这时候在解引用,就会报错!

为了规避这种错误,建议每申请完一个空间,都要对齐判断是否成功

2、对动态内存开辟空间的越界访问

int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
	perror("malloc");
	return 1;
}
for (int i = 0; i <= 10; i++)
{
	p[i] = i;
}

当 i == 10 时,越界访问

这个问题都是很常见的,这里就不多说了

3、对非动态内存开辟的空间使用 free

注意,free 只能释放动态内存空间,也就是用 malloc calloc realloc 开辟出来的空间,至于数组什么的都不可以!

4、用 free 释放动态内存空间的一部分

int* p = (int*)malloc(10 * sizeof(int));
p = p + 2;
free(p);

这样也不可以!free 必须接收该空间的首地址!

5、对同一空间的反复释放

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	free(p);
	free(p);

这种也是不可以滴!但是为了避免这种错误,可以在每次释放后,给 p 置为 NULL

七、结语

掌握C语言的动态内存管理,不仅是写出高效程序的关键,也是开发安全稳健软件的重要保障。善用malloc、realloc和free等函数,我们可以灵活应对各种复杂场景,实现资源的动态高效利用。在未来的编程旅程中,让我们不断深入探索与实践,成为内存管理的高手,从而写出更加出色与安全的C语言程序。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值