动态内存管理详细讲解

目录

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

2. 动态内存函数的介绍

2.1 malloc和free

2.2  calloc

2.3  realloc


今天要和大家分享的内容是的动态内存管理,我们先从他的定义入手学习。

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

我们到现在已经掌握了内存开辟的方式就是要么创建一个变量,要么就创建一个数组

	int a = 0;
	char arr[10] = { 0 };

 如上所示,创建一个变量,就是在栈空间上开辟四个字节;创建一个char类型的数组里面有十个元素,就是在栈空间上开辟10个字节的连续的空间;

那我们目前学到的开辟空间的方法只有这两种方法;

这两种方法创建的空间大小是固定的,int类型就是四个字节,char类型的数组就是十个字节,不能将其增大也不能减小。所以这种方式在某些环境下是有一定的局限性的,所以为了让我们能更加灵活的控制我们所需要的内存的空间的大小,这时我们就需要学习C语言中动态内存管理的功能,

也会涉及到其中的一些相关的函数,接下来就对这些函数进行分析。

2. 动态内存函数的介绍

2.1     malloc和free

先在官方的网站上大概了解一下它的内容

malloc本质上是一个函数,使用时要包含<stdlib.h>的头文件,返回类型是人任意类型的指针,参数是 size_t类型,也就是无符号整形, 他的作用是在内存中申请数个字节的内存块,返回一个指针指向这个内存块的起始位置。

上代码简单使用一下这个函数

int main()
{
	int* p =(int*) malloc(20);
	return 0;
}

那么这段代码什么意思呢?

假设我们要开辟二十个字节的空间,我们就在malloc后面的括号中输入想要开辟的空间,注意单位是字节;

再来观察一下malloc函数的返回值,是一个任意类型的指针,我们在使用时要将其具体细化成我们想要的数据,那这里我们举一个例子,不妨将其定义为整形指针,所以也要给malloc函数开辟的空间的类型强制转化成int类型(我们所要使用的类型)

那以上就是malloc开辟空间的简单使用。

接下来在深度研究之前要了解一些知识

上图是我们计算机内存划分的简单的示意图,这里要为大家说明的是我们所创建的临时变量,和函数的形式参数以及具有临时性的变量都放在栈区;

我们今天索要讲解的动态内存管理的函数所申请的内存空间都在堆区中,可以看到上图有很多动态内存管理的函数,都会一一讲解;

最后就是静态区,静态区所存放的就是static所修饰的静态变量和全局变量;

了解完这些我们再回头看我们的malloc函数

 我们使用malloc申请空间的时候要注意,malloc申请空间可能因为空间不足而导致申请失败,

而申请失败时就会返回一个空指针,所以我们在使用malloc函数时要对其进行判断;

 代码如下

int main()
{
	int* p =(int*) malloc(20);
	if (p = NULL)
	{
		printf("%s\n",strerro(errno));
        return 1;
	}

上面说过申请失败会返回一个空指针,我们不妨对其进行判断 :

如果p是空指针的话,就打印出他错误的原因,打印分析出错的原因后就利用return 1结束当前的程序,因为已经申请空间失败了,不能再往下走了,相信大家不难看懂

使用malloc函数申请空间后,要使用free来释放空间,

那free是怎样释放空间的呢?

 可以看到free的函数的参数是一个任意类型的指针,也就是说使用free释放空间的时候,只需要将malloc申请的空间的首地址传进free函数中即可。

所以上面的代码中,malloc申请空间的首地址赋值给了指针变量p,所以我们将p传入free中即可

接下来将代码调试起来

 可以看到malloc开辟的空间的首地址已经赋给了p;

 这时free函数已经释放了p指针指向的空间,

但是这时我们要注意到是虽然空间已经得到了释放,但我们可以看到指针p指向了一块不知道的空间,这时p就成了一块野指针,所以我们要在最后将空指针赋予p

 这时我们就看到p已经忘记了当时申请的20个字节的空间在哪

这时候就避免了野指针的问题; 

申请空间和释放空间都研究过后我们再来看一看如何使用这块空间;

使用也非常的简单,既然我们申请的时一块连续的空间,我们不妨用数组来将其应用:

 对申请的二十个空间进行访问我们可以利用数组的遍历对其访问,并将其打印出来以便我们观察

可以看到使用其申请的空间没有那么困难。

2.2    calloc

 可以看到calloc函数的返回值是一个任意类型的指针,函数的两个参数都是size_t类型的数。

下面的对函数功能的描述是说开辟并且用'0'初始化数组。

用代码简单使用calloc函数:

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("calloc()--%s", strerror(errno));
		return 1;
	}
	//使用
	 int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;

}

可以看到使用对其开辟的空间时只是将其打印出来

 运行的结果就是将其开辟的内容全都初始化为0;

那calloc和malloc有何区别呢?

1.参数不同

malloc只有一个参数,要开辟多少字节的空间直接在后面的括号内告诉他就好;

calloc有两个参数,要确定多少和一个数据多大的参数,类似于数组的开辟方式;

2.两者都是在堆区上申请空间,但是malloc不初始化,calloc会初始化为0;

如果要初始化就选择calloc,不需要初始化就选择malloc,具体要看需求使用 。

malloc在返回地址之前没有初始化,而calloc在返回地址之前要全部初始化为0,所以calloc的效率是比malloc要低的

2.3     realloc

继续观察官方网站对这个函数的定义

 它的返回类型是任意类型的指针,函数的一个参数是任意类型的指针,另一个数size_t的无符号整型的整数;

对函数功能的描述就是改变内存块的大小

realloc函数的作用就是 让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小
的调整

我们直接用代码简单探究一下

 我们可以看到这里再使用空间中增加了realloc的使用,这个意思就是说在指针p指向的首地址处,向后开辟40个字节的空间(注意这里开辟空间的字节大小要是加上之前空间的总大小)

使用这段空间也有两种方法,下面为大家用图例演示会更加便于理解

第一种情况:

 

 这种增加内存的情况是在原先的内存空间中续上一段内存(篮筐表示realloc函数续上的内存)。这种情况下是因为本身申请的空间不会受到别的内存的干扰。

第二种情况:
 

 上图这种情况时我们发现原本的空间后面,已经没有我们能够所再申请加长的空间了,这时候realloc就会重新申请一块更大的空间空间在别的地方,同时它会将之前空间的内容拷贝到新的空间中,这块空间一定满足你所开辟的空间,同时也会释放旧的空间,然后返回新空间的地址。

这就是realloc函数使用时内部会发生的情况;

但是要注意的是两种情况放回的地址不一样,第一种情况会返回旧的地址,第二种情况会返回新的地址。当然还要想到的一种情况可能会返回空指针,万一realloc在申请空间失败的情况下就会返回空指针,这点也必须考虑到,所以我们必须要用一个临时指针来存放他申请的空间,并且加以判断

使用例子如下

int*ptr=(int*)realloc(p, 40);
	if (ptr!=NULL)
	{
		p = ptr;
	}
    else
    {
        printf("%s\n",strerror(errno));
        return 1;
    }

先使用realloc函数开辟空间,并将其首地址存入一个临时的指针变量,并加以判断,如果不是空指针,就交给原先的p指针对这块空间进行管理。

如果真的开辟空间失败的话,就结束当前进程。

那接下来就给大家调试起来,让大家看看是否真的会存放在不同的空间

 我们可以看到两个指针的指向的地址是同样的,

那怎么样才会不一样呢?

我们只需要将realloc申请的空间增大

 就可以看到两者的地址已经不一样了,这就是realloc在申请空间时的第二种情况。

以上就是索要分享的动态内存管理的函数的要分享的内容,下次会给大家分享常见的动态内存的错误。

如果这篇文章对你有所帮助,也请三连多多支持一下,感谢阅读。

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

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

相关文章

【差分数组】

差分数组一维差分差分数组的作用差分矩阵结语一维差分 输入一个长度为 n 的整数序列。接下来输入 m个操作&#xff0c;每个操作包含三个整数 l,r,c&#xff0c;表示将序列中 [l,r] 之间的每个数加上 c &#xff0c;请你输出进行完所有操作后的序列。 输入格式 第一行包含两个…

ArduPilot飞控之ubuntu22.04-Gazebo模拟

ArduPilot飞控之ubuntu22.04-Gazebo模拟1. 源由2. Gazebo安装2.1 ubuntu22.04系统更新2.2 安装Gazebo Garden2.3 安装ArduPilot Gazebo插件2.3.1 基础库安装2.3.2 源代码编译2.3.3 配置插件2.4 测试Gazebo Garden3. ArduPilot SITL Gazebo模拟3.1 Gazebo Garden模拟环境3.2 Ar…

大数据周会-本周学习内容总结07

目录 01【hadoop】 1.1【编写集群分发脚本xsync】 1.2【集群部署规划】 1.3【Hadoop集群启停脚本】 02【HDFS】 2.1【HDFS的API操作】 03【MapReduce】 3.1【P077- WordCount案例】 3.2【P097-自定义分区案例】 历史总结 01【hadoop】 1.1【编写集群分发脚本xsync】…

【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…

YOLOv7训练自己的数据集(手把手教你)

YOLOv7训练自己的数据集整个过程主要包括&#xff1a;环境安装----制作数据集----模型训练----模型测试----模型推理 一&#xff1a;环境安装 conda create -n yolov7 python3.8 conda activate yolov7 #cuda cudnn torch等版本就不细说了&#xff0c;根据自己的显卡配置自行…

水果新鲜程度检测系统(UI界面+YOLOv5+训练数据集)

摘要&#xff1a;水果新鲜程度检测软件用于检测水果新鲜程度&#xff0c;利用深度学习技术识别腐败或损坏的水果&#xff0c;以辅助挑拣出新鲜水果&#xff0c;支持实时在线检测。本文详细介绍水果新鲜程度检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Python的实…

给准备面试网络工程师岗位的应届生一些建议

你听完这个故事&#xff0c;应该会有所收获。最近有一个23届毕业的大学生和我聊天&#xff0c;他现在网络工程专业大四&#xff0c;因为今年6、7月份的时候毕业&#xff0c;所以现在面临找工作的问题。不管是现在找一份实习工作&#xff0c;还是毕业后找一份正式工作&#xff0…

100天精通Python丨基础知识篇 —— 03、Python基础知识扫盲(第一个Python程序,13个小知识点)

文章目录&#x1f41c; 1、Python 初体验Pycharm 第一个程序交互式编程第一个程序&#x1f41e; 2、Python 引号&#x1f414; 3、Python 注释&#x1f985; 4、Python 保留字符&#x1f42f; 5、Python 行和缩进&#x1f428; 6、Python 空行&#x1f439; 7、Python 输出&…

Linux基础知识点总结

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

史上最详细的改良顺序表讲解,看完不会你打我

目录 0.什么是顺序表 1.顺序表里结构体的定义 2.顺序表的初始化 3.顺序表的输入 4.增加顺序表的长度 5.1顺序表的元素查找&#xff08;按位查找&#xff09; 5.2顺序表的元素查找&#xff08;按值查找&#xff09;在顺序表进行按值查找&#xff0c;大概只能通过遍历的方…

HFish蜜罐的介绍和简单测试(三)

在学习蜜罐时&#xff0c;HFish是个不错的选择。首先是免费使用&#xff0c;其次易于安装管理&#xff0c;然后文档支持比较丰富&#xff0c;最后还有更多扩展功能。第三篇的话作为本系列的最终篇章进行总结&#xff0c;具体是看到哪里写到哪里。 0、HFish平台管理 0.1、报告…

基于SpringBoot实现冬奥会运动会科普平台【源码+论文】

基于SpringBoot实现冬奥会科普平台演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#…

一文吃透SpringBoot整合mybatis-plus(保姆式教程)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

23.3.26总结

康托展开 是一个全排列与自然数的映射关系&#xff0c;康托展开的实质是计算当前序列在所有从小到大的全排列中的顺序&#xff0c;跟其逆序数有关。 例如&#xff1a;对于 1,2,3,4,5 来说&#xff0c;它的康托展开值为 0*4&#xff01;0*3&#xff01;0*2&#xff01;0*1&…

Android 之 打开相机 打开相册

Android 之 打开系统摄像头拍照 打开系统相册&#xff0c;并展示1&#xff0c;清单文件 AndroidManifest.xml<uses-permission android:name"android.permission.INTERNET" /><!--文件读取权限--><uses-permission android:name"android.permiss…

网络编程2(套接字编程)

套接字编程UDP协议通信&#xff1a;TCP通信&#xff1a;套接字编程&#xff1a;如何编写一个网络通信程序 1.网络通信的数据中都会包含一个完整的五元组&#xff1a; sip&#xff0c;sport&#xff0c;dip&#xff0c;dport&#xff0c;protocol&#xff08;源IP&#xff0c;源…

【linux】多线程控制详述

文章目录一、进程控制1.1 POSIX线程库1.2 创建线程pthread_create1.2.1 创建一批线程1.3 终止线程pthread_exit1.4 线程等待pthread_jion1.4.1 线程的返回值&#xff08;退出码&#xff09;1.5 取消线程pthread_cancel1.6 C多线程1.7 分离线程pthread_detach二、线程ID值三、线…

C/C++内存管理

内存管理在C中无处不在&#xff0c;内存泄漏几乎在每个C程序中都会发生。因此&#xff0c;要学好C&#xff0c;内存管理这一关势在必得&#xff01; 目录 1.C/C内存分布 2.C语言中动态内存管理方式 3.C内存管理方式 3.1.new和delete操作内置类型 3.2.new和delete操作自定义类型…

SQL注入之HTTP请求头注入

Ps&#xff1a; 先做实验&#xff0c;在有操作的基础上理解原理会更清晰更深入。 一、实验 sqli-lab 1. User-Agent注入 特点&#xff1a;登陆后返回用户的 User-Agent --> 服务器端可能记录用户User-Agent 输入不合法数据报错 payload: and updatexml(1,concat("~&…

异或相关算法

文章目录1. 异或的性质2. 题目一3. 题目二4. 题目三5. 题目四1. 异或的性质 我们知道&#xff0c;异或的定义是&#xff1a;相同为0&#xff0c;相异为1。所以也被称为无进位相加&#xff0c;根据这定义&#xff0c;我们可以得出三个性质&#xff1a; 1. N ^ N0。2. N ^ 0N。3…