【一文教你学会动态内存管理】

news2024/11/20 0:35:59

1.为什么会存在动态内存分配?

2. 动态内存函数的介绍

  • 2.1 malloc函数和free函数

  • 2.2 calloc函数

  • 2.3 realloc

3. 常见的动态内存错误

  • 3.1 对NULL指针的解引用操作

  • 3.2 对动态开辟空间的越界访问

  • 3.3 对非动态开辟内存使用free释放

  • 3.4 使用free释放一块动态开辟内存的一部分

  • 3.5 对同一块动态内存多次释放

  • 3.6 动态开辟内存忘记释放(内存泄漏)

1.为什么会存在动态内存分配?

我们现在知道的开辟内存方式是创建变量,创建数组。而这些东西是在栈区上开辟空间的,一旦创建完成,是无法更改的,是固定死的。就像数组,在创建数组时,已经指定了数组的大小,编译后无法再更改。

这时候就需要动态内存分配

2. 动态内存函数的介绍

2.1 malloc函数和free函数

1.malloc函数

学习新函数时,就从库里面找函数的声明,解析来学习。

void* malloc (size_t size);

该函数的功能是,向堆区申请一块size个字节大小的空间。

如果申请成功,返回申请的空间的起始地址,如果申请失败,返回空指针NULL。 至于malloc函数在库里的为什么是void*,这是因为malloc函数也不知道使用者需要这块空间来存放什么,具体要什么类型的指针,使用者自己决定。

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

下面举个例子来深入了解:

在这里插入图片描述
在这段代码中,我们用了malloc函数来社区内40个字节的空间,(一个int是4个字节,*10是40字节)
但是这样写是不对的,前面说过,malloc是向内存中的堆区申请空间,如果申请失败,就返回NULL,如果现在不判断p的值,直接使用的话,就会出问题,所以我们应该判断p的有效性。

在这里插入图片描述

顺便提一下,perror是一个报错的函数,假如申请空间失败返回NULL,perror会报告相应的错误信息。来演示一下:
假如申请INT_MAX个字节的空间:
在这里插入图片描述
这是一个极大的数字,看看是否能够申请成功。
在这里插入图片描述
在这里,perror报告错误:没有足够的空间。具体的用法请学习一下。

free函数

free函数和malloc函数是成对出现的。

free函数是专门用来释放申请的空间的。

具体用法如下:

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

在申请完空间之后,如果不用了,就需要释放掉,把申请的空间还给操作系统,即 有借有还,再借不难 的道理。

总结:malloc函数是向堆区申请一块空间,如果申请成功,则返回该空间的首地址,如果申请失败,则返回NULL。

free函数是将申请的空间释放掉

2.2 calloc函数

calloc函数也是向堆区中申请内存的函数。具体参数如下:

void* calloc (size_t num, size_t size);

第一个参数是申请的元素个数,第二个参数是申请的每个元素大小。

注意:calloc为元素数组分配一个内存块,并将其所有位初始化为零。

也就是说,calloc函数不仅申请空间,还将该空间初始化为0。

举个例子来证明:

int main()
{
	int *p = (int*)calloc(10, sizeof(int)); 
	if (p == NULL)
	{
		perror("calloc");
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

结果如下:

在这里插入图片描述

结果就是:将申请的空间自动初始化为0。

calloc函数与malloc函数类似,只是malloc函数只负责申请空间,没有初始化,而calloc函数会申请空间并初始化。

不过也不是说calloc函数比malloc函数更高级,calloc函数 初始化也会花费时间,malloc函数不初始化,所以它也节省了一部分时间。

每个函数之间各有优缺点。

所以在使用函数的时候,结合实际情况来使用。

2.3 realloc函数

realloc函数,让我们动态申请的空间更加灵活。

当我们觉得动态申请的空间太大或者太小时,可以用realloc函数来调整动态申请的空间大小。

realloc函数的原型如下:

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

ptr是需要重新调整的空间的起始地址,
size 调整之后新大小
注意:是调整后的大小

假如刚开始动态申请的空间是40字节,发现不够用了,想加大10个字节的空间,那么使用realloc函数重新调整空间时,参数就是50。

realloc函数的返回值是调整后的空间的起始地址。

既然返回调整后的起始地址,那会不会出现申请失败的情况?会不会出现空间不足以申请的情况?
会的。

情况1: 重新申请空间时,假如后面的空间足够大,那么realloc函数就会返回空间的首地址。原来有的数据不会变化:
在这里插入图片描述
情况2: 如果重新申请空间失败,则返回NULL。

情况3:****如果在原来的空间后面没有足够大的空间来增容,realloc函数会自动在内存中的其他位置找一块满足我们要求的空间,并把原空间的数据拷贝到新空间中,然后返回新空间的起始地址。

在这里插入图片描述
基于情况2和情况3,我们是不是用原空间的指针来接收呢?

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return ;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
	不够了,增加空间
	int* p = (int*)realloc(p, sizeof(int) * 10);

还是刚才的例子,上面这段代码对吗?
不对。
因为在重新realloc调整空间的时候,如果用原空间的地址来接收的话,万一申请失败呢?
如果申请失败,realloc函数会返回一个NULL,如果用p来接收的话,p原来指向的空间的数据就丢失了!就找不到了。
所以不能用原空间p来接收realloc返回的地址,我们需要用一个临时指针来接收返回的地址。

	int* ptr = (int*)realloc(p, sizeof(int) * 10);

这样写才是正确的,然后判断ptr是否为空,如果不为空,再把这个ptr存的地址赋给p。

	if (ptr != NULL)
	{
		p = ptr;
		ptr = NULL;(防止ptr成为野指针)
	}

以上就是三个申请空间的函数的基本情况,根据需求,选择不同的函数。

3. 常见的动态内存错误

3.1 对NULL指针的解引用操作

void test()
{
	int* p = (int*)malloc(INT_MAX);
	*p = 20;
	free(p);
}

int main()
{
	test();
	return 0;
}

这段代码的错误是,没有判断p的值就对p进行解引用,如果申请的空间失败,返回NULL,对NULL进行*操作,是非法的。

改正很简单:只需判断p是否为NULL即可。

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	if (p == NULL)
	{
		perror("malloc");
		return;
	}
	*p = 20;
	free(p);
	p = NULL;
}

改正结果如上。

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

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		perror("malloc");
		return;
	}

	for (i = 0; i < 20; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

上面这段代码的错误是,malloc函数只申请了10个整型大小的空间,(40)字节,但是在赋值的时候,i的范围取到了20,造成对后面的空间非法访问。

改正如下:只需把i的范围调整到i<10即可,或者最初申请的空间到20个整型大小(80字节)

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		perror("malloc");
		return;
	}

	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}

	free(p);
	p = NULL;
}

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

void test()
{
int a = 10;
int *p = &a;
free(p);
p = NULL;
}

int main()
{
	test();
	return 0;
}

上面代码的错误在于,a是一个变量,在内存中的栈区创建,不是用malloc等函数申请出来的空间,后面free§的时候,free释放的是堆区上申请的空间,明显两者有差异。释放是非法的。

在这里插入图片描述
运行时也产生错误,并且关掉这个程序会很卡顿。

改正方法有多种,可以把free§去掉。

3.4 使用free释放一块动态开辟内存的一部分

void test()
{
	int* p = (int*)malloc(100);
	if(p == NULL)
	{
		perror("malloc");
		return;
	}
	p++;
	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

上面代码的错误在于,申请的100字节的空间,用p来接收,但是p又++了,此时p不再指向申请的空间的起始地址,释放的时候,只释放一部分,剩下的空间没有释放完,造成内存泄漏

在这里插入图片描述
运行时一样会报错。

所以申请的空间的起始地址不能丢失。

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

void test()
{
	int *p = (int *)malloc(100);
	free(p);
	free(p);//重复释放
}

int main()
{
	test();
	return 0;
}

重复释放也会出现错误。

在这里插入图片描述
运行时依然报错。

3.6 动态开辟内存忘记释放(内存泄漏)

void test()
{
	int *p = (int *)malloc(100);
	if(NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	while(1);
}

在test函数内部申请的空间,除了test函数后,没有返回值,意味着申请的这块空间丢失了,没有人记得这块空间的存在,造成了内存泄露!

关于动态内存,你学会了吗?学会了不妨关注我吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/163169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Uniswap v3 详解(一):设计原理

刚看完 Uniswap v2 的代码&#xff0c;本来打算写一个 Uniswap v2 设计与实现&#xff0c;结果 Uniswap v3 就发布了。趁着这个机会就先写一个 Uniswap v3 设计与实现吧。 因为 v3 版本的实现复杂度和 v2 已经不在一个量级了&#xff0c;难免会有理解上的偏差&#xff0c;本文…

ESP8266-01s+STM32+MQTT+ONNET+EMQX实现定时发送心跳包并配置MQTT断开连接后进行重连

目录:1.情况介绍2.发送心跳包和MQTT重连实现步骤3.运行效果1.情况介绍 硬件通过ESP8266-01s连接自己的MQTT服务器EMQX的时候&#xff0c;发现连上后没过多久就自动断开了&#xff0c;由于硬件代码使用的是ONNET的案例代码改的&#xff0c;所以发现该案例代码并没有发送心跳包和…

测试篇(一):需求、BUG、测试用例、开发模型和测试模型、配置管理和软件测试

目录一、什么是需求1.1 需求的概念1.2 用户需求1.3 软件需求二、什么是测试用例2.1 测试用例的概念三、什么是BUG3.1 BUG(软件错误)的概念四、开发模型4.1 软件生命周期4.2 瀑布模型4.3 螺旋模型4.4 增量、迭代模型4.5 敏捷模型五、测试模型5.1 软件测试V模型5.2 软件测试W模型…

(详细简单成功版本)Mysql主从复制

博主不易&#xff0c;如果有帮助点个赞加个关注再走啊家人们 目录 介绍 配置-前置条件 大坑&#xff1a;如果两份mysql文件都是复制的一份&#xff0c;那么就修改其中一份的uuid&#xff0c;要不后续配置不成功 配置-主库Master 1.修改Mysql数据库的配置文件my.ini 2.重启…

mysql快速生成100W条测试数据(6)地区天气情况并存入mysql数据库

这是之前的文章里面包含一些以前的一些操作流程可以进行参考学习 更加详细操作步骤在第一篇文章里面 mysql快速生成100W条测试数据&#xff08;1&#xff09;&#xff1a;游戏人物数据 mysql快速生成100W条测试数据&#xff08;2&#xff09;公司员工信息 mysql快速生成100W条测…

202:vue+openlayers: easing的API及在view.animation中使用示例

第202个 点击查看专栏目录 本示例的目的是介绍如何在vue+openlayers项目中使用easing。 easing在openlayers共用五种表现形式,easeIn,easeOut,inAndOut,linear,upAndDown。 easing主要在view.animation中使用。 名称说明easeIn开始慢,然后加速easeOut开始快,然后加速inA…

PyTorch实例3——迁移学习

传送门&#xff1a;蓝桥云课实验 目录1. 实验环境2. 实验目的3. 相关原理4. 实验步骤4.1 数据收集4.1.1加载数据4.1.2 GPU运算4.2 数据预处理4.3 创建模型4.3.1 构建迁移模型4.3.2 训练模型测试绘制图表4.3.2.1 预训练模式4.3.2.2 固定值模式4.4 结论1. 实验环境 Jupyter Note…

【JavaGuide】数据库基础知识总结

数据库基础知识总结1.什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性&#xff1f;2.主键和外键有什么区别?3.什么是 ER 图&#xff1f;4.数据库的三范式5.drop、delete 与 truncate 区别&#xff1f;1.什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性&#xff…

el-switch 开关文字显示在开关里面

提示&#xff1a; active-color"#305BE7" active-text"开" 打开时的背景颜色和内容 inactive-color"#7D8294" inactive-text"关" 关闭时的背景颜色和内容 内容&#xff1a; <el-switch v-model"value2" class&qu…

Java 基础之文件处理

1. Stream 流 2. File 和 IO Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标&#xff1a; 2.1 控制台输入 Java 的控制台输入由 System.in 完成&#xff0c; 将 System.in 包装在一个 BufferedReader 对象中来创建一个字符流&#x…

大数据必学Java基础(一百二十二):POM模式-Maven工程关系

文章目录 POM模式-Maven工程关系 一、依赖 1、特性:依赖的传递性 2、原则:两个原则

[Linux]冯诺依曼体系结构

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…

98家央企及下属上市企业全名单(2023版)

1月10日&#xff0c;央企专业化整合又有新动作。国家电网与国家电投生物质发电项目专业化整合项目在北京举行管理权交接仪式&#xff0c;中国电建与通用技术集团医疗资源专业化整合项目在京签约。经过整合&#xff0c;我国生物质发电领域龙头企业国能生物重组进入国家电投&…

springboot ssm招生管理系统java

招生管理系统是基于java编程语言&#xff0c;mysql数据库&#xff0c;springboot框架和idea工具开发&#xff0c;本系统分为学生和管理员两个角色&#xff0c;学生可以注册和登陆&#xff0c;查看招生公告信息&#xff0c;查看招生专业信息&#xff0c;在线申请专业&#xff0c…

Tapdata Cloud 场景通关系列:数据入湖仓之 MySQL → Doris,极简架构,更实时、更简便

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata Cloud 自去年发布云版公测以来&#xff0c;吸引了近万名用户的注册使用。应社区用户上生产系统的要求&#xff0c;Tapdata Cloud 3.0 将正式推出商业版服务&#xff0c;提供对生产系统的 SLA 支撑。Tapdata 目前专注在实时数…

RNN从理论到实战【理论篇】

来源&#xff1a;投稿 作者&#xff1a;175 编辑&#xff1a;学姐 要深入理解深度学习&#xff0c;从零开始创建的经验非常重要&#xff0c;从自己可以理解的角度出发&#xff0c;尽量不使用外部完备的框架前提下&#xff0c;实现我们想要的模型。本系列文章的宗旨就是通过这样…

【JavaSE】数据类型与变量

数据类型与变量数据类型与变量1. 字面常量2. 数据类型3. 变量3.1 变量概念3.2 语法格式3.3.1 整型变量3.3.2 长整型变量3.3.3 短整型变量3.3.4 字节型变量3.3 浮点型变量3.4.1 双精度浮点型3.4.2 单精度浮点型3.4 字符型类型3.5 布尔型变量3.6 类型转换3.7.1 自动类型转换&…

TensorFlow 实战案例: ResNeXt 交通标志图像多分类,附Tensorflow完整代码

各位同学好&#xff0c;今天和大家分享一下如何使用 Tensorflow 构建 ResNeXt 神经网络模型&#xff0c;通过 案例实战 ResNeXt 的训练以及预测过程。每个小节的末尾有网络、训练、预测的完整代码。 ResNeXt 是 ResNet 的改进版&#xff0c;在 bottleneck卷积块 结构上进行了较…

阿里高级技术专家方法论:如何写复杂业务代码?

阿里妹导读&#xff1a;张建飞是阿里巴巴高级技术专家&#xff0c;一直在致力于应用架构和代码复杂度的治理。最近&#xff0c;他在看零售通商品域的代码。面对零售通如此复杂的业务场景&#xff0c;如何在架构和代码层面进行应对&#xff0c;是一个新课题。结合实际的业务场景…

ECM工业能耗管理云平台

在我国的能源消耗中&#xff0c;工业企业是能源消耗的主要群体&#xff0c;能源消耗量占全国能源消耗总量的70%左右&#xff0c;传统方式进行各类工厂能耗的计量&#xff0c;造成能耗数据不完整、不准确、不全面&#xff0c;因而无法进行能耗分析与诊断&#xff0c;造成普遍在各…