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

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

(本篇完)

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Adversarial Nibbler挑战:与多元社区持续开展开放红队测试

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【MySQL数据库】MySQL 高可用搭建方案——MHA实战

MHA&#xff08;Master High Availability&#xff09; MHA实战 MHA&#xff08;Master High Availability&#xff09; 一、MHA简介二、MHA搭建准备要求&#xff1a;mha集群搭建&#xff0c;4台服务器&#xff0c;1主2从&#xff0c;1台mha2.1实验思路2.2实验准备 三、搭建MyS…

【数据结构】链式二叉树详解

个人主页~ 链式二叉树基本内容~ 链式二叉树详解 1、通过前序遍历的数组来构建二叉树2、二叉树的销毁3、二叉树节点个数4、二叉树叶子节点个数5、二叉树第k层节点个数6、二叉树查找7、前序遍历8、中序遍历9、后序遍历10、层序遍历与检查二叉树是否为完全二叉树Queue.hQueue.c层序…

Nginx实战:防盗链

防盗链的概念 内容不在自己的服务器上&#xff0c;通过技术手段将其他网站的内容&#xff08;比如 一些音乐、图片、软件的下载地址&#xff09;放置在自己的网站中&#xff0c;通过这 种方法盗取其他网站的空间和流量 防盗链技术背景 防止第三方引用链接访问我们的图片&#x…

FJSP:蛇鹫优化算法(SBOA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

详细介绍 FJSP&#xff1a;蛇鹫优化算法&#xff08;Secretary bird optimization algorithm&#xff0c;SBOA&#xff09;求解柔性作业车间调度问题&#xff08;FJSP&#xff09;&#xff0c;提供MATLAB代码-CSDN博客 完整MATLAB代码 FJSP&#xff1a;蛇鹫优化算法&#xff…

SQL实验 连接查询和嵌套查询

一、实验目的 1&#xff0e;掌握Management Studio的使用。 2&#xff0e;掌握SQL中连接查询和嵌套查询的使用。 二、实验内容及要求&#xff08;请同学们尝试每道题使用连接和嵌套两种方式来进行查询&#xff0c;如果可以的话&#xff09; 1&#xff0e;找出所有任教“数据…

十_信号7-信号集

int sigemptyset(sigset_t *set); 清空信号集 int sigfillset(sigset_t *set); 填充满 信号集 int sigaddset(sigset_t *set, int signum); 向信号集中添加信号 int sigdelset(sigset_t *set, int signum); 从型号集中删除信号 int sigismember(const sigset_t *set, int s…

人大金仓×广州医科大学附属肿瘤医院 互联网智慧医疗服务平台国产化升级

KINGBASE 广州医科大学附属肿瘤医院是国内领先的肿瘤专科医院&#xff0c;在金仓数据库的支撑下&#xff0c;近日成功完成移动智慧综合服务平台国产化升级。作为互联网智慧医疗服务平台项目的核心平台&#xff0c;预计将服务数十万人次。这一升级改造不仅提高了医疗服务的效率和…

961题库 北航计算机 组成原理选择题 附答案 选择题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 某计算机采用大端方式&#xff0c;按字节编址。某指令中操作数的机器数为 1234 FF00H&#xff0c;该操作数采用基址寻址方式&#xff0c;形式地址 ( 用补码表示 ) 为FF1…

如何监控慢 SQL?

引言&#xff1a;在开发和维护数据库驱动的应用程序时&#xff0c;监控慢 SQL 查询是确保系统性能和稳定性的关键一环。慢 SQL 查询可能会导致系统性能下降、资源浪费和用户体验差等问题。因此&#xff0c;及时监控和优化慢 SQL 查询对于保障系统的正常运行和用户满意度至关重要…

neutron学习小结

概述 基于yoga版本学习neutron&#xff0c;通过源码、官方文档、部署环境进行学习 neutron-dhcp-agent neutron.agent.dhcp_agent.main 创建server&#xff0c;调oslo_service launch server&#xff0c;最后实际调了server的start方法 neutron.service.Service.start Serv…

【UML用户指南】-03-UML的14种图

1、结构图 1、类图&#xff08;class diagram&#xff09; 展现了一组类、接口、协作和它们之间的关系。 在面向对象系统的建模中所建立的最常见的图就是类图。类图给出系统的静态设计视图。 包含主动类的类图给出系统的静态进程视图。构件图是类图的变体。 2、对象图&a…

转让北京电力施工总承包二级资质变更条件和流程

在电力工程领域&#xff0c;资质等级是企业能否参与竞标、承接工程的重要标志之一。北京电力工程总包二级资质的转让&#xff0c;是指已经取得该资质的企业将其资质转让给需要的企业。这种转让是基于合作与共赢的原则&#xff0c;旨在推动电力工程行业健康、稳定发展&#xff0…

Gin入门

Gin入门 声明&#xff1a;本博客为看李文周大佬gin入门视频笔记gin入门 我的代码仓库6月 沉着冷静/2023 - 码云 - 开源中国 (gitee.com) 安装 go get -u github.com/gin-gonic/gin第一个Gin实例&#xff1a; package mainimport ("github.com/gin-gonic/gin" )…

llvm 3.5 源码分析 clang for x86 001 之搭环境

0&#xff0c;目标 编译 针对x86 的&#xff0c;debug 的 c语言的编译器 1&#xff0c;下载代码 git clone --recursive 。。。llvm-project.git $ cd llvm-project 2&#xff0c;预备代码 llvm 3.5 版本的源代码&#xff0c;早期版本&#xff0c;可能比较小比较容易debug $…

发送Http请求的两种方式

说明&#xff1a;在项目中&#xff0c;我们有时会需要调用第三方接口&#xff0c;获取调用结果&#xff0c;来实现自己的业务逻辑。调用第三方接口&#xff0c;通常是双方确定好&#xff0c;由对方开放一个接口&#xff0c;需要我们根据他们提供的接口文档&#xff0c;组装Http…

STM32(九):USART串口通信 (标准库函数)

前言 上一篇文章已经介绍了如何用STM32单片机中独立看门狗来实现检测按键点灯的程序。这篇文章我们来介绍一下如何用STM32单片机中USART通信协议来串口通信&#xff0c;并向XCOM发送信息。 一、实验原理 1.通信的介绍 首先&#xff0c;我们先介绍一下通信&#xff0c;何为通…

C语言 | Leetcode C语言题解之第128题最长连续序列

题目&#xff1a; 题解&#xff1a; typedef struct {int key;UT_hash_handle hh; }Hash; int longestConsecutive(int* nums, int numsSize) {Hash* headNULL;Hash* tempNULL;for(int i0;i<numsSize;i){int numnums[i];HASH_FIND_INT(head,&num,temp);if(!temp){temp…

数据结构与算法04-栈和队列

介绍 栈和队列。事实上它们并不是全新的东西&#xff0c;只不过是多加了一些约束条件的数组而已。但正是这些约束条件为它们赋予了巧妙的用法。 栈和队列都是处理临时数据的灵活工具。在操作系统、打印任务、数据遍历等各种需要临时容器才能构造出美妙算法的场景&#xff0c;…

SQL实验 带函数查询和综合查询

一、实验目的 1&#xff0e;掌握Management Studio的使用。 2&#xff0e;掌握带函数查询和综合查询的使用。 二、实验内容及要求 1&#xff0e;统计年龄大于30岁的学生的人数。 --统计年龄大于30岁的学生的人数。SELECT COUNT(*) AS 人数FROM StudentWHERE (datepart(yea…