【进阶C语言】动态内存管理+柔性数组

news2024/11/22 16:57:51

文章目录

    • 1.动态内存的开辟
      • 内存的布局
      • 内存池
      • 内存碎片
      • 内存泄漏
    • 2.动态内存函数
      • malloc
        • 功能
        • 函数
      • calloc
        • 功能
        • 函数
      • realloc
        • 功能
        • 函数
        • 开辟时遇到的两种情况
      • free
        • 功能
        • 函数
    • 3.  建议
    • 4.柔性数组
      • 特性:
      • 定义
      • 使用
      • 优点

1.动态内存的开辟

内存的布局

在这里插入图片描述
我们常用的内存开辟函数都是在堆区开辟的——malloc,calloc,realloc,free。

内存池

  1. 定义
     内存池(Memory Pool)是一种内存分配方式。
  2. 优点
     内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升

注意:这里的提升是一定程度上的,不一定效率提升很大。

内存碎片

定义
 内存碎片即“碎片的内存”描述一个系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用,这一问题的发生,原因在于这些空闲内存以小且不连续方式出现在不同的位置
简单来说:就是一些比较小且无法进行利用的内存空间。

内存泄漏

定义:
 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

程序结束时,会将占用的内存还给操作系统,但是如果有一个不停运行的程序呢?如果在运行时所占用的内存空间无法及时的释放,则可能在一段时间后,程序死机或者运行缓慢,这就是我们电脑死机重启恢复正常的原因。那如果我们将用完的内存及时的还给操作系统,这样我们的程序,在理论上,具有了持续运行的能力。

2.动态内存函数

malloc

功能

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

返回的指针建议强制类型转换为所使用指针的类型,因为返回的类型是void*的指针,容易报出警告。

函数

1.返回类型:void*
2.参数
要开辟的字节个数——size_t

注意:
1.开辟失败,则返回一个空指针,空指针是不能被解引用的,要进行检查以免出错。
2.开辟成功,则返回指向该空间首位置的指针。
3.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
4.用完的函数要及时的还给操作系统。

calloc

功能

与malloc的功能相似,向内存申请一块连续可用的空间,并且设置初始值,并返回指向这块空间的指针。

函数

1.返回类型:void*
2.参数

1.要开辟的字节个数——size_t
2.要设置的初始值——size_t
3.其它的注意事项与malloc基本相同

realloc

功能

不够使用的内存空间重新开辟,进行增容。

函数

1.返回类型:void*
2.参数

1.要调整的内存空间的地址——void*
2.要申请的比原先更大的字节数——size_t

开辟时遇到的两种情况

在这里插入图片描述

这个函数考虑的很周到:
情况一:返回的是原先的地址
情况二:将原先不够的空间进行释放,还给操作系统,,同时返回的是开辟好的空间的地址。

free

功能

将在堆区开辟的已经使用过的空间返还给操作系统,经常与上面的内存开辟函数进行搭配着使用。

注意:
1.其它区上可不能用free
2.free过后的空间指针并没有被置为空指针,要手动置为空指针
3.在传入NULL时,free啥也不干。
4.对已经释放的空间的起始位置再一次释放会报错,所以要及时置为空指针。

函数

1.返回类型:void
2.参数
要释放空间位置的起始地址——void*

3.  建议

1.对接收的指针要进行检查,防止其为空指针
2.对释放后的指针所指向的空间后,将指针及时的置为NULL,防止报错。
3.free的范围仅限在堆区所开辟的空间使用
4.注意使用指针的越界问题
5.动态内存函数的开辟尤其是在函数内部开辟时,要及时的在函数内部置为空指针,否则这块开辟的空间将无法进行正常的使用,会导致内存泄漏的问题。

4.柔性数组

特性:

 它允许你在定义结构体时创建一个空数组,而这个数组的大小可以在程序运行的过程中根据你的需求进行更改特别注意的一点是,并且还要求这样的结构体至少包含一个其他类型的成员,这个空数组必须声明为结构体的最后一个成员

注意:
1.在结构体里面才能出现柔性数组
2.结构中的柔性数组成员前面必须至少一个其他成员。
3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
4.这个空数组必须声明为结构体的最后一个成员

定义

法1:

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

法2:

typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4

使用

typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;
int main()
{
	int i = 0;
	type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
	//如果不够使用realloc进行增容
	for(i=0; i<100; i++)
	{
	  p->a[i] = i;
	}
	free(p);
	p=NULL;
	return 0;
}

优点

看与之功能相似的代码:

typedef struct st_type
{
	int i;
	int *p_a;//指针可以指向开辟空间的地址
}type_a;//此结构体的大小是固定的——32位8字节/64位12字节
int main()
{
	type_a *p = (type_a *)malloc(sizeof(type_a));
	p->i = 100;
	p->p_a = (int *)malloc(p->i*sizeof(int));
	return 0;
}

为了与柔型数组的功能靠拢,我们将空间都开辟在堆区上。
接下来我们画图来看这个代码:
在这里插入图片描述
再看一下柔性数组的实现图解:
在这里插入图片描述
对比看:

区别:一个是一块一块的开辟,另一个是整块的开辟。
联想到内存池和内存碎片的概念,我们会发现柔型数组符合内存池的概念,占用一整块空间,会在一定程度上提高内存的使用效率,而模拟的那个会导致内存碎片的不断积累从而会降低内存的利用效率。
并且在内存释放的时候一个释放两次,这样可能会导致忘记释放的毛病,从而导致内存泄漏,而柔型数组只需释放一次,这样会使代码的使用效率变高。

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

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

相关文章

几个特殊的运算符重载(前置\后置++、前置\后置--、<<、>>)

几个特殊的运算符重载 概念 运算符重载是一个非常重要的概念&#xff0c;在运算符重载中我们可以重新定义 运算符 的具体含义&#xff0c;一个运算符重载函数的定义是 T operator 运算符 (参数) &#xff0c;对于运算符重载有以下重点。 以下讨论的都是双操作数的运算符 双操…

uniapp: 本应用使用HBuilderX x.x.xx 或对应的cli版本编译,而手机端SDK版本是 x.x.xx。不匹配的版本可能造成应用异常。

目录场景与问题描述&#xff1a;原因分析&#xff1a;解决方案&#xff1a;方案一&#xff1a;更新HbuilderX版本方案二&#xff1a;设置固定的版本方案三&#xff1a;忽略版本&#xff08;不推荐&#xff09;场景与问题描述&#xff1a; 项目场景&#xff1a;示例:通过使用Hb…

【docker13】Dockfile

1.Dockerfile是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本 自己的理解是&#xff1a;将多次繁琐的commit命令构成一个Dockerfile文本然后一次性执行完成&#xff0c;可以简化复杂程度 Dockerfile官网 构建三部…

Docker部署 registry

系列文章目录 Docker部署 registry Docker搭建 svn Docker部署 Harbor Docker 部署SQL Server 2017 Docker 安装 MS SqlServer Docker部署 Oracle12c Docker部署Jenkins Docker部署 registry系列文章目录前言一、registry搭建二、使用步骤1. pull registry2. run image3. 验证…

通过gcloud创建Google Kubernetes Engine(GKE)并通过kubectl访问

1 简介 GKE(Google Kubernetes Engine)是一个K8s平台&#xff0c; 我们可以使用gcloud来创建GKE集群。在开始之前&#xff0c;可以查看&#xff1a;《初始化一个GCP项目并用gcloud访问操作》。 2 创建GKE集群 2.1 打开API 在创建集群之前&#xff0c;需要打开Google API&am…

图扑数字孪生水利工程,助力水资源合理利用

前言从大禹治水到三峡大坝的建造&#xff0c;人类为控制和调配自然界的地表水和地下水&#xff0c;修建了许多的水利工程。对水资源进行了广泛的开发利用&#xff0c;诸如农业灌溉、工业和生活用水、水力发电、航运、港口运输、淡水养殖、旅游等。将图扑软件与 GIS、粒子仿真、…

力扣算法(Java实现)—字符串入门(9题)

文章目录1.反转字符串2.整数反转3.字符串中的第一个唯一字符4.有效的字母异位词5.验证回文串6.字符串转换整数 (atoi)7.实现strStr()8.外观数列9.最长公共前缀&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e; 更多资源链接&#xff0c;欢迎访问作者gitee仓…

STM32那些事

STM32芯片型号命名方式STM32开发板的GPIO编程GPIO的函数调用顺序&#xff1a;&#xff08;1&#xff09;使能GPIO时钟&#xff1a;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);第一个参数是GPIO对象&#xff0c;第二个参数是枚举使能&#xff08;2&#xff09;初始化…

Python - 数据容器tuple(元组)

目录 定义元组 元组的相关操作 修改元组 元组的删除 转换为元组tuple 定义元组 元组同列表一样&#xff0c;都是可以封装多个、不同类型的元素在内。 但最大的不同点在于&#xff1a;元组一旦定义完成&#xff0c;就不可修改 元组定义&#xff1a;定义元组使用小括号&…

第二章.线性回归以及非线性回归—梯度下降法

第二章.线性回归以及非线性回归 2.5 梯度下降法 1.流程&#xff1a; 初始化θ0,θ1 不断改变θ0,θ1&#xff0c;直到J(θ0,θ1)到达一个全局最小值或局部极小值 2.图像分析&#xff1a; 1).图像层面分析代价函数&#xff1a; ①.红色区域表示代价函数的值比较大&#xff0…

leetcode 1443.Minimum Time to Collect All Apples in a Tree(收集苹果所需最短时间)

给出节点个数n, edges是连接的边&#xff0c;[a,b]是连接的两个顶点。 hasApple表示第 i 个顶点上是否有苹果。 走一条边需要耗时1s, 从顶点0出发&#xff0c;最后回到顶点0, 问收集所有苹果所需最短的时间。 思路&#xff1a; &#xff08;1&#xff09;DFS 可以把问题简化…

pdf文档控件Aspose.PDF for .NET 授权须知

Aspose.PDF是一款高级PDF处理API&#xff0c;可以在跨平台应用程序中轻松生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现&#xff0c;保护和打印文档。无需使用Adobe Acrobat。此外&#xff0c;API提供压缩选项&#xff0c;表创建和处理&#xff0c;图形和图像功能&…

[ 解决报错篇 ] tomcat 执行 startup.bat 文件报错 -- tomcat 启动失败(安装 java 环境并配置环境变量)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Linux——팔 gdb部分基础知识以及操作系统的初级理解

文章目录一、gdb部分基础知识1、打断点2、逐语句过程3、监视&#xff0c;内存4、部分功能指令二、进程概念的初级理解1、冯-诺依曼体系结构2、操作系统的初级理解一、gdb部分基础知识 承接上一篇。 1、打断点 放一下全部代码 1 #include <stdio.h>2 3 int ADD(int n)4…

vs2017调试ffprobe源码

鄙人之前写过vs2017调试ffmpeg源码 现在由于需要分析视频文件里面的具体帧情况&#xff0c;需要用到ffprobe工具&#xff0c;为此本篇博客搭建vs2017工程&#xff0c;可以调试ffprobe&#xff0c;ffprobe比ffmpeg简单很多。 首先找到ffmpeg的编译目录&#xff0c;将下列三个文…

结构体内存对齐与位段详解

文章目录前言一、内存对齐1.内存对齐的规则2. 内存对齐的具体实例与运用3.为什么会有内存对齐&#xff1f;4.修改默认对齐数二、位段1.什么是位段2.位段的例子3.位段的优缺点结语前言 学习了结构体&#xff0c;你会算结构体的占用字节数吗&#xff0c;许多人恐怕摇头&#xff0…

ArcGIS分式、假分式标注

解说一下 ArcGIS中不同标注 直接标注语句分享见文后 分式标注 假分式标注 二、标注语句与视频教学 ArcGIS分数、假分数标注获取语句地址&#xff1a; ArcGIS分式、假分式标注&#xff01;标注语句直接分享 推荐学习 ArcGIS之模型构建器&#xff08;ModelBuilder&#xff0…

初级通讯录的实现详细攻略

我们设计的要求存储多少个人的信息我们使用宏&#xff0c;这样方便修改建立三个文件先从主函数入手为了持续多次&#xff0c;实现操作&#xff0c;我们利用循环&#xff0c;循环次数未知&#xff0c;我们只能选择while循环&#xff0c;do-while循环&#xff0c;我们始终要进行一…

ORB-SLAM2 --- LoopClosing::Run 回环检测线程解析

目录 1.函数作用 2.code 3.函数解析 3.1 查看是否有待处理的回环关键帧LoopClosing::CheckNewKeyFrames 3.2 检测闭环LoopClosing::DetectLoop 3.3 计算当前关键帧和上一步闭环候选帧的Sim3变换 3.4 闭环矫正 1.函数作用 回环检测&#xff0c;又称闭环检测&#xff0c…

文件操作和IO

一.文件的概念&#xff1a;狭义的文件指&#xff1a;硬盘上的文件和目录广义上的文件指&#xff1a;计算机中的很多软硬件资源路径&#xff1a;绝对路径&#xff1a;以c&#xff1a;d盘符开头的路径&#xff0c;比如c:/Intel/Logs/text.txt相对路径&#xff1a;以当前所在的目录…