【落羽的落羽 C语言篇】动态内存管理·上

embedded/2024/12/26 18:09:24/

在这里插入图片描述

文章目录

  • 一、动态内存管理是什么
  • 二、动态内存管理相关函数
    • 1. malloc
    • 2. free
    • 3. calloc
    • 4. realloc
  • 三、柔性数组
    • 1. 概念
    • 2. 使用

一、动态内存管理是什么

我们之前已经知道,定义变量就是申请一块空间,int a;就是申请四个字节的空间,char arr[20]就是申请20个字节的空间。这样的空间申请方式有两个特点:空间开辟的大小是固定的、开辟好的空间大小不能再修改了。但是对于内存空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行时才能知道,那么这种申请内存空间的方式就不能满足我们的需求的

所以,C语言中引入了动态内存管理(开辟),让我们能自由地申请和释放内存空间。
而要实现这一点,必须要依靠下面的相关函数。

二、动态内存管理相关函数

以下四个函数malloc、free、calloc、realloc,都包含在头文件stdlib.h中。

1. malloc

C语言提供了一个动态内存开辟的函数malloc:
在这里插入图片描述
这个函数能申请一块大小为size个字节的内存空间,返回指向这块空间的指针。
但要注意的是:

  • 内存空间可能会开辟失败,如果失败了,会返回NULL空指针,因此使用malloc后一定要对返回值做检查。
  • malloc并不知道开辟空间的存放数据类型是什么,返回指针的类型是void*。所以一般需要使用者对返回值进行强制类型转换
  • 参数size为0,malloc的行为是未定义的,结果取决于编译器。

2. free

C语言还提供了一个函数free,是用来实现动态内存的释放和回收的:
在这里插入图片描述

free能将参数指针指向的空间进行释放和回收,free后该指针变成野指针。以前也讲过,这时最好把野指针置为NULL。
但要注意的是:

  • 如果参数ptr指向的空间不是动态开辟的,那么函数free的行为是未定义的。
  • 如果参数ptr是NULL空指针,那么free函数什么都不做

在一个程序中,如果开辟的动态空间不用free释放回收,操作系统也会在程序退出的时候自动回收这块内存;但一般情况下,我们动态开辟好的空间在使用完后,习惯性free掉,为了节省空间。

举个栗子:用malloc申请一块空间,存放num个int数据,打印出来,再free掉这块空间。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{int num = 0;scanf("%d",&num);int* p = NULL;p = (int*)malloc(num*sizeof(int));assert(p != NULL);//检查是否空间开辟成功for(int i=0 ; i<num ; i++)scanf("%d", p+i);for(int i=0 ; i<num ; i++)printf("%d ", *(p+i));free(p);p = NULL;//有必要的,牢记出现野指针随即置为NULLreturn 0;
}

在这里插入图片描述在这里插入图片描述

3. calloc

还有一个函数是calloc,也是用来进行动态内存分配的:
在这里插入图片描述
这个函数的功能是为num个size大小的数据开辟一块空间,并且把每个字节初始化为0。它与函数malloc的区别只在于calloc能将每个字节初始化为0,其余的使用方法与malloc完全相同。

int* p = (int*)calloc(10, sizeof(int));
assert(p != NULL);
for(int i=0 ; i<10 ; i++)printf("%d ",*(p+i));
free(p);
p = NULL;

在这里插入图片描述

如果要求我们对申请的内存空间的内容初始化,那么应该使用calloc。

4. realloc

在申请空间后,有时我们发现申请好的空间太小了,有时又觉得太大了,为了合理地使用内存,我们会对已经开辟的内存大小做一些调整,relloc函数是用来实现这个功能的。
在这里插入图片描述

参数指针ptr代表着要调整的空间,size是调整后想要的大小。函数返回指向调整后的空间的指针。当然,也可能开辟空间失败,返回空指针NULL。

realloc调整空间大小有两种情况:

  • 情况1:原空间之后有足够大的空间
  • 情况2:原空间之后没有足够大的空间

对于这两种情况:

  • 情况1:扩展内存直接从原空间之后追加。
  • 情况2:在堆空间上另外找一个大小合适的连续空间来使用,将原空间中的已有数据拷贝到新空间内,释放回收原空间。

有了这个函数,我们就可以在写项目的过程中,实时调整要使用的内存空间的大小。
很好理解吧~

在这里插入图片描述

三、柔性数组

1. 概念

C99中,结构体中的最后一个元素允许是未知大小的数组,这就是柔性数组。
例如:

struct s
{int i;char c;int a[];
}

要注意的是:

  • 结构体中的柔性数组前面必须有至少一个其他成员,柔性数组必须是结构体的最后一个成员
  • 结构体的大小计算不会算上柔性数组的大小
  • 包含柔性数组的结构体需要用malloc函数进行内存分配,并且分配的内存应该大于结构体的大小,适应柔性数组的预期使用大小。

2. 使用

举个栗子:

struct s
{char c;int a[];
};struct s* p = (struct s*)malloc(sizeof(struct s)+100*sizeof(int));
assert(p!=NULL);
p->c = 'x';
for(int i=0 ; i<100 ; i++)p->a[i] = i;
free(p);
p = NULL;

这样,结构体成员c获得了值’x’,结构体成员柔性数组a获得了100个整型元素的连续空间并存入了一些数据。

有人可能会有疑惑,即使不使用柔性数组,在结构体中定义一个指针,在这个指针指向的空间,在开辟足够的大小来存储数据,也可以达到我们的目的。那么使用柔性数组的优势是什么?
好处是:

  • 方便内存回收释放。使用柔性数组,在结构体使用完毕后,我们只需进行一次free就可以把所有开辟的空间释放掉。但是不使用柔性数组的话,我们需要free两次,一次释放结构体空间,一次释放结构体成员指针开辟的空间。
  • 有利于提高访问速度。连续的内存有利于提高访问速度,也有利于减少内存碎片。

在这里插入图片描述

本篇完,感谢阅读~


http://www.ppmy.cn/embedded/148958.html

相关文章

【Rust自学】7.1. Package、Crate和定义Module

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 7.1.1. Rust的代码组织 代码组织主要包括&#xff1a; 那些细节可以对外暴露&#xff0c;而哪些细节是私有的在作用域内哪些名称有效… …

ML-Agents:训练配置文件(一)

注&#xff1a;本文章为官方文档翻译&#xff0c;如有侵权行为请联系作者删除 Training Configuration File - Unity ML-Agents Toolkit–原文链接 ML-Agents&#xff1a;训练配置文件&#xff08;一&#xff09; ML-Agents&#xff1a;训练配置文件&#xff08;二&#xff09;…

对 MYSQL 架构的了解

MySQL 是一种广泛使用的关系型数据库管理系统&#xff0c;其架构主要包括以下几个关键部分&#xff1a; 一、连接层 客户端连接管理&#xff1a;MySQL 服务器可以同时处理多个客户端的连接请求。当客户端应用程序&#xff08;如使用 Java、Python 等语言编写的程序&#xff09;…

Llama 3 模型系列解析(一)

目录 1. 引言 1.1 Llama 3 的简介 1.2 性能评估 1.3 开源计划 1.4 多模态扩展 ps 1. 缩放法则 2. 超额训练&#xff08;Over-training&#xff09; 3. 计算训练预算 4. 如何逐步估算和确定最优模型&#xff1f; 2. 概述 2.1 Llama 3 语言模型开发两个主要阶段 2.2…

云手机+YouTube:改变通信世界的划时代技术

随着科技的不断进步&#xff0c;手机作为人们生活中不可或缺的工具&#xff0c;也在不断地更新换代。近年来&#xff0c;一个名为“油管云手机”的全新产品正在引起广泛的关注和讨论。作为一个运用最新科技实现的新型手机&#xff0c;它在通信领域带来了全新的体验和革命性的变…

从汽车企业案例看仓网规划的关键步骤(视频版)

大家好&#xff0c;欢迎收看本期视频。本期内容将以汽车行业为例&#xff0c;带您了解仓库选址和仓网规划如何解决实际问题&#xff0c;以及需要注意的关键步骤。 案例描述 国内某大型汽车企业目前在全国范围内拥有十多个生产厂地和近千家供应商。这些供应商分布在多个地理区…

errant是怎么产生的

目录 1.产生errant GTID的原因2.检查errant GTID3.处理errant GTID方式一 忽略errant GTID方式二 重置从库方式三 注入空事务 在MySQL中&#xff0c;errant GTID&#xff08;错误GTID&#xff09;是指在从库上存在但在主库上不存在的GTID。 这通常是由于在从库上执行了不应存在…

Gemini 2.0:面向智能体时代的全新 AI 模型

2024年12月11日&#xff0c;Google 发布了 Gemini 2.0 系列的首个模型——Gemini 2.0 Flash&#xff08;实验版&#xff09;。凭借多模态方面的新进展以及原生工具的使用&#xff0c;Gemini 2.0 Flash (实验版) 能够构建新的 AI 智能体&#xff0c;推动了实现通用 AI 助手愿景的…