首页 > 系统相关 >动态内存管理【malloc,calloc,realloc和free的理解】【柔性数组的概念】

动态内存管理【malloc,calloc,realloc和free的理解】【柔性数组的概念】

时间:2024-04-01 13:03:22浏览次数:443  
标签:malloc int realloc free 内存 空间 NULL 开辟

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

我们知道,当我们创建变量的时候,我们会向系统申请一定大小的空间内存。比如int a=10或者int arr[10];我就向内存申请了4或者40个字节的大小来存放数据。但是当我们一旦申请好这个空间,大小就无法调整了。但是对于空间的需求,不仅仅就只有上面的情况。有时候我们需要的空间大小只有在程序运行的时候才能知道,那么数组编译时开辟空间的方式就不能满足了。

在C语言中,引入了动态内存开辟,让程序员可以自己申请和释放空间,就比较灵活了。

注意,以下介绍的函数头文件都是stdlib.h

二.malloc,free,calloc和realloc

1.malloc函数的理解

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

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

2.如果开辟失败,则返回一个NULL指针,因此我们在使用malloc的时候一定要检查。

3.因为它的返回值是void*,malloc函数不知道返回值的类型,具体在使用时还是程序员来定。

4.如果参数size(单位是字节)为0,malloc的行为是标准没有定义的,取决于编译器。

比如我想开辟能存放5个整数的空间

int main()
{
	int* p = (int*)malloc(20);//这里开辟20个字节的空间,把起始地址放在p里
	if (p == NULL)//判断也没有开辟成功
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p +i) = i + 1;
	}
	return 0;
}

我们可以调试看一下在内存中它是怎么开辟的。

代码运行完毕后:

这些就是malloc函数的使用了。还有我上面写的第4条,明明我用malloc是来申请空间的,结果我要申请0个字节的空间,这就显得有点没用了。

我们需要注意的一点,这些开辟的空间都在内存的哪一个区域?不管是malloc,free,calloc或者realloc这些跟动态内存有关的都存放在内存的堆区。而我们创建的像是局部变量,形式参数这些是在栈区。而静态变量,全局变量,这些在静态区。

2.free函数的理解

这个函数是专门用来做动态内存的释放和回收的。

1.如果参数ptr指向的空间不是动态内存开辟的,那free函数的行为的未定义的。

2.如果参数ptr是NULL指针,则函数什么事也不做。

也就是说,我想释放哪里的空间,就把这一块空间的起始地址给这个函数的参数。

free的作用就是把空间的使用权限还给了操作系统,还是上面的代码:

#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(20);//这里开辟20个字节的空间,把起始地址放在p里
	if (p == NULL)//判断也没有开辟成功
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p +i) = i + 1;
	}
	free(p);
	p = NULL;
	return 0;
}

调试运行之后是这样的:

注意,我们在把p指向的那一块空间给释放了之后,p指针还是存在的,但是它没有指向的东西了,此时的p就是一个野指针,那我们就必须给这个野指针一条绳子NULL来栓住它。

3.calloc函数的理解

calloc函数也是用来动态申请空间的,但是它的用法跟malloc不太一样。

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

2.与malloc的区别就是,malloc在开辟空间的时候没有初始化这个功能。

举个例子:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p = NULL)
	{
		perror("calloc");
		return 1;
	}
	return 0;
	free(p);
	p = NULL;
}

这里我没有进行赋值操作,但是在内存中这些值就已经变成了0。

其他的地方跟malloc是一样的,在释放空间后也是需要给上一个NULL。

4.realloc函数的理解

这个函数是基于动态内存的,如果我们在开辟空间的时候,觉得我们申请的空间太大了或者太小了,为了能够合理的使用内存,我们可以使用realloc函数实现对动态开辟内存大小的调整。

1.ptr是要调整的内存地址,size是调整之后的大小。

2.返回值是调整之后的内存起始位置。

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

4.realloc函数在调整时有两种情况,一种是原来的空间后面有足够的空间大小,另一种是后面的空间不够了。

我先简单写一下代码,简单说一下第四点会出现的问题。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(5, sizeof(int));//这里原本是20个字节的大小
	if (p = NULL)
	{
		perror("calloc");
		return 1;
	}
	int* ptr=(int*)realloc(p, 40);//这里调整为40个字节
	if (ptr != NULL)//这里我没有直接把开辟空间的起始地址给p,就是害怕万一开辟空间失败了,原来的空间也没了
	{
		p = ptr;//这里空间调整成功了就可以使用我们开辟的40个字节的空间了
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(p + i) = i + 1;
		}
		free(p);//一定要记得释放,否则会存在内存泄漏的问题
		p = NULL;//释放空间了一定要置为空指针
	}
	else
	{
		perror("realloc");
	}
	return 0;
}

 这里就是开辟空间扩大的情况。

情况一:原内存之后有足够的空间的话,会直接在原来的空间的后面再开辟20个字节,凑够40个字节。

情况二:原内存之后没有足够的空间的话,这个函数会在堆区的内存里找一个新的空间,并且是满足我们所要开辟的空间的,而且会把原来空间的数据拷贝一份到新的空间,原来的空间释放掉,并且会把新的空间的起始地址返回。

情况三:如果调整失败了,直接返回NULL。

注意:realloc不仅仅可以调整空间的,它也可以开辟空间,我们想一下,如果我给ptr的参数是一个空指针呢?

#include<stdlib.h>
int main()
{
	realloc(NULL, 20);//这个等价于malloc(20)
	return 0;
}

以上就是动态空间分配需要使用到的函数。

5.总结4个函数

当我们在申请空间后,不再用这块空间了,虽然,当程序结束的时候,操作系统会回收这一块空间,但最好是我们主动去用free释放,尽量做到谁(函数)申请的空间谁释放。因为如果我的这块空间一直不退出的话这一块内存就一直被占用着,谁也用不了。如果不能释放,要告诉使用的人,要记得释放。

三.柔性数组

关于柔性数组,有几点需要介绍一下:

1.它存在于结构体中,是结构体的最后一个成员

2.最后的一个成员是数组,而且没有指定大小

struct S
{
	char c;
	int i;
	int arr[];//这个就是柔性数组,并没有规定具体的大小或者写成int arr[0]
};

这就是柔性数组

1.柔性数组的特点

1.结构体里的柔性数组成员前面必须至少有一个其他成员

2.sizeof计算这样含柔性数组大小的时候,不包括柔性数组的内存

3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

关于前两点

直接就只是i的大小。

再来看第三点:

struct S
{
	int i;
	int arr[];//这个就是柔性数组,并没有规定具体的大小
};
#include <stdio.h>
int main()
{
	struct S* ps=(struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
    free(ps);
    p=NULL;
	return 0;
}

除了我要开辟的int大小的空间,后面我又加了一个5 * sizeof(int)这个是我要额外开辟的空间。

我们既然是用malloc开辟的空间,那么我们是不是就可以使用realloc来改变这一块空间,随便怎么变大变小。对应到这个数组上,我们不就是把这个数组变成“柔性”了吗?

2.柔性数组的使用

了解了它的一些基本特点,还有前面的几个函数,使用起来也就很简单了。

struct S
{
	int i;
	int arr[];//这个就是柔性数组,并没有规定具体的大小
};
#include <stdio.h>
int main()
{
	struct S* ps=(struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
	if (ps == NULL)//没有开辟成功
	{
		return 1;
	}
	ps->i = 100;
	int a = 0;
	for (a = 0; a < 5; a++)
	{
		ps->arr[a] = a + 1;
	}
	struct S* ret = (struct S*)realloc(sizeof(struct S) + 10 * sizeof(int));//调整为44个字节大小
	if (ret != NULL)
	{
		ps = ret;
	}
	free(ps);
	ps = NULL;
	return 0;
}

到这里,关于动态内存管理的相关知识就已经讲完了。感谢大家的观看,如有错误,请大家多多指正。

标签:malloc,int,realloc,free,内存,空间,NULL,开辟
From: https://blog.csdn.net/2301_81699364/article/details/137169114

相关文章

  • 7款免费开源报表工具,free report
    https://www.youtube.com/watch?v=fBgsh8qabDsEclipseBirt(推荐,Eclipse比较出名)JsreportJesperReport用过,感觉一般HeilicalInsightKNIMEMetabase目前在用,使用比较简单,做BI分析可以,制作有特殊格式要求的打印单据不太可行,比如发票订单之类的Knowage......
  • FreeRTOS从代码层面进行原理分析(3 任务的切换)
    FreeRTOS分析三—任务的切换我们带着三个问题开始了对FreeRTOS代码的探究。1.FreeRTOS是如何建立任务的呢?2.FreeRTOS是调度和切换任务的呢?3.FreeRTOS是如何保证实时性呢?前两篇文章分别从代码的层面分析了FreeRTOS是如何建立任务以及建立的任务是怎么样被调......
  • 12_FreeRTOS消息队列
    FreeRTOS消息队列队列简介队列操作基本过程队列结构体介绍队列结构体整体示意图:队列相关API函数介绍创建队列相关API函数介绍往队列写入消息API函数从队列读取消息API函数队列操作实验代码freertos_demo.c/*********************************......
  • 11_FreeRTOS时间管理
    FreeRTOS时间管理延时函数介绍延时函数演示实验代码freertos_demo.c/*******************************************************************************************************@filefreertos.c*@author正点原子团队(ALIENTEK)*@version......
  • 基于STM32的ModBus实现(二)移植FreeMODBUS TCP
    一、ModBusTCPModbusTCP是一种基于TCP/IP协议的Modbus通信协议的变种。它允许Modbus协议在以太网上进行通信,提供了一种简单而有效的方式来连接不同类型的设备,如传感器、执行器、PLC等。ModbusTCP使用标准的TCP/IP协议栈,因此可以在现有的以太网基础设施上运行,而无需额外的硬......
  • 基于STM32的ModBus实现(一)移植FreeMODBUS RTU
    一、FreeMODBUSFreeModbus是一个开源的Modbus通信协议栈实现。它允许开发者在各种平台上轻松地实现Modbus通信功能,包括串口和以太网。FreeMODBUS提供了用于从设备和主站通信的功能,支持ModbusRTU和ModbusTCP协议。在工业控制和自动化领域广泛应用。FreeModBus可通过官......
  • FreeType Glyph Conventions 翻译(3) ——Glyph Metrics 字符图形参数
    原文地址https://freetype.org/freetype2/docs/glyphs/glyphs-3.html目录基线,笔位置以及布局Baseline,pensandlayouts排版参数和包围盒Typographicmetricsandboundingboxes定位和步进BearingsandAdvances网格对齐Theeffectsofgrid-fitting文本宽度和包围盒Text......
  • Proteus8.0仿真应用设计(十七)基于FreeRTOS、STM32F103C8、HAL库、DHT11、LCD12864的温
    一、简介:        DHT11是一款湿、温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。传感器内部......
  • FreeType编译与使用
    FreeType是一款免费用于渲染字体的开源库。在使用该类库时,最好先过一遍官方文档,其中FreeTypeGlyphConventions部分的文章必读。编译我们可以进入下载界面,点击任意一个地址下载源码。这里笔者使用的是2.13.2版本,解压后会获得一个freetype-2.13.2文件夹。进入目录freetype-2.......
  • FreeRTOS从代码层面进行原理分析(2 任务的启动)
    FreeRTOS分析二—任务的启动上一篇文章我们带着三个问题开始了对FreeRTOS代码的探究。1.FreeRTOS是如何建立任务的呢?2.FreeRTOS是调度和切换任务的呢?3.FreeRTOS是如何保证实时性呢?并且在上一篇文章FreeRTOS从代码层面进行原理分析(1任务的建立)中对任务的创......