你了解C语言中的柔性数组吗?

news2024/11/25 1:21:38

本篇博客主要讲解C99中的新语法:柔性数组。

柔性数组

1.什么是柔性数组?

柔性数组就是大小可以变化的数组。

注意跟C99中的变长数组区分开来,变长数组指的是可以使用变量来指定大小,并且不能初始化的数组,比如:

int n = 0;
scanf("%d", &n);
int arr[n]; // 变长数组

柔性数组是在结构体中声明的。它满足:

  1. 是结构体的最后一个成员变量。
  2. 大小不确定,一般空出来或者用0来填充。

比如:

struct S
{
	char ch;
	double d;
	int arr[];
};

或者:

struct S
{
	char ch;
	double d;
	int arr[0];
};

上面的2中写法是等价的,但是有些编译器只支持其中的一种写法。

在进行如何使用的讲解之前,先来思考一个问题:结构体S的大小是多少?也就是说,sizeof(struct S)是多大?

不同的编译器结果不一定一样。事实上,sizeof(struct S)计算的是结构体中除了变长数组之外的大小,在VS2022,X64环境下,考虑内存对齐,计算出来的结果是16。

2.柔性数组应该如何使用?

为了给柔性数组分配空间,应该使用动态内存管理。假设使用malloc来申请空间,我们应该考虑柔性数组的大小,算出总的大小。

以上面的struct S为例。假设我想要数组arr的大小是10个int,那么要开辟的总大小就是除了柔性数组之外的大小+柔性数组的大小。前面提到了,除了柔性数组之外的大小就是sizeof(struct S),而柔性数组的大小是10*sizeof(int),就能如下开辟空间:

struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
if (ps == NULL)
{
	// ...
}
// ...

这样,结构体中的成员数组arr就有了10个int的空间了,我们可以正常的使用这个结构体,只需把成员数组arr当成int arr[10];这样的数组即可。

当然,之所以叫“柔性数组”,这个数组不仅可以指定初始化的大小,也可以改变大小,毕竟是动态内存开辟出来的。只需要使用realloc即可,新的大小的计算方式和前面一样,也是sizeof(struct S)+柔性数组新的大小。比如,如果我想把数组的大小扩大成原来的2倍,可以这么写:

struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
if (tmp == NULL)
{
	// ...
}
else
{
	ps = tmp;
}
// ...

这样,ps指向的结构体的最后一个成员数组的容量就变成了20个int。

记住,所有动态申请的内存,在使用结束后,都需要调用free释放。比如以上的结构体s使用完后,需要free(ps);

3.柔性数组的替代方案及内存分布对比

其实,我们可以不使用柔性数组实现类似的效果。分析一下,以上的结构体struct S的特点是:

  1. 使用动态内存管理,在堆区上申请空间。
  2. 有一个成员数组,可以动态的改变大小。

如果不使用柔性数组,而是直接使用动态内存管理的思路,也是可以的。比如:

struct S
{
	char ch;
	double d;
	int* arr;
};

先malloc出一个结构体出来。

struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
{
	// ...
}
// ...

接着再malloc出一块空间,交给arr管理。假设开辟出10个int的空间。

ps->arr = (int*)malloc(10 * sizeof(int));
if (ps->arr == NULL)
{
	// ...
}
// ...

如果想要扩容,直接对ps->arr进行realloc即可。比如,把arr指向的空间扩容成原来的2倍。

int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
if (tmp == NULL)
{
	// ...
}
else
{
	ps->arr = tmp;
}

这块空间使用结束后,记得释放空间。注意一定要先释放arr,再释放ps,因为如果先释放ps,结构体内的arr就成了野指针,就找不到原来arr指向的空间了,形成了内存泄漏。

free(ps->arr);
ps->arr = NULL;
free(ps);
pf = NULL;

使用和不使用柔性数组的内存分布对比:

  1. 对于柔性数组,动态申请的内存只存放了一个完整的结构体,所有的成员变量(数组)整体上是连续的空间。
  2. 不使用柔性数组实现类似的效果,需要先动态开辟出一个结构体,再开辟出另外一块空间,用结构体的一个成员变量(一个指针)来管理。相当于malloc了2次,有2块独立的空间。

4.柔性数组有哪些优点?

既然不使用柔性数组也能实现类似的效果,为什么还要使用柔性数组呢?因为柔性数组有以下的2个优点:

  1. 使用柔性数组只用malloc一次,free一次。如果不使用柔性数组,要malloc两次,free两次。在使用上,柔性数组更方便、更简单。
  2. 由于柔性数组在整体上只开辟了一块连续的空间,根据局部性原理,缓存的命中率更高,增加了效率(虽然其实也没增加多少)。

总结

  1. 柔性数组指的是结构体的最后一个成员变量是一个大小不确定的数组,数组的大小会空出来或者给0。
  2. 柔性数组的使用需要进行动态内存管理,具体的大小计算是sizeof(结构体类型)+柔性数组大小
  3. 不使用柔性数组也可以实现类似的效果,但是柔性数组是有它独特的优势的。如果使用柔性数组,只需要malloc和free一次;如果不使用柔性数组,对于结构体需要malloc一次,结构体内的指针又要malloc一次,总共需要malloc两次,free两次。比较起来,使用柔性数组更简单、方便,而且整体是一块连续的空间,根据局部性原理,效率更高。

感谢大家的阅读!

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

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

相关文章

实例方法、类方法、静态方法、实例属性、类属性

背景:今天在复习类相关知识的时候,突然想到这几种类型的方法的区别和用法,感觉有点模棱两可,于是总结一下,加深记忆。 定义:想要区别和理解几种方法,首先要定义一个类,要在类中加深…

mysql如何修改时区

mysql 里CST时区的坑 一、 问题简述 mysql里CST时区是个非常坑的概念,因为在mysql里CST既表示中国也表示美国的时区。但是在JDK代码里,CST这个字符串被理解为CenTral Standard Time(USA)(GMT-6)&#xff…

java框架都有哪些

Java框架是对Java2中的一些基本概念进行抽象,封装成能被开发者使用的类库,使之能快速开发应用程序。它让开发者能够专注于业务逻辑而不是实现细节。可以说, Java框架是 Java开发中的重要组成部分,它极大地方便了开发者。下面为大家…

腾讯云GPU服务器NVIDIA P40 GPU、P4、T4和GPU自由卡详解

腾讯云GPU云服务器,GPU云服务器实例可选GN8机型、GN6S机型、GN7机型等规格,搭载 NVIDIA P40 GPU,最长可3年,云服务器吧来详细说下腾讯云GPU云服务器: 目录 腾讯云GPU云服务器 腾讯云GPU自由卡 腾讯云GPU云服务器 腾…

选品趋势分析 | 2023开斋节将至,穆斯林时尚在TIKTOK上增长势头正劲!

2023年Q1 穆斯林时尚的销售额环比2022年Q4 暴涨153%,领跑TikTok电商大盘,成为2023年Q1的超级黑马品类。加之开斋节临近,人民陆续开始为庆祝开斋节作采购准备,购物需求相应激增,其中,穆斯林群体会在开斋节的…

GELU激活函数

GELU是一种常见的激活函数,全称为“Gaussian Error Linear Unit”, 作为2020年提出的优秀激活函数,越来越多的引起了人们的注意。 GELU (Gaussian Error Linear Units) 是一种基于高斯误差函数的激活函数,相较于 ReLU 等激活函数&#xff0c…

java版工程项目管理系统-功能清单 图文解析

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目显示…

MySQL事务的四大特性及事务的隔离级别

什么是事务?Transaction 常言道:能不麻烦就不麻烦,能简单化就简单化。但是为什么需要事务呢? 事务:用来保证一个业务的完整性,最大的优势就是回滚,并撤销正在进行的所有未提交的修改&#xff1…

linux运行串口相关的java.jar报错:java.lang.UnsatisfiedLinkError

目录 linux运行串口相关的java.jar报错如下: java.lang.UnsatisfiedLinkError是Java中的一个错误类型,通常发生在调用本地(native)方法或使用JNI(Java Native Interface)时。 在Java中,本地方…

微分中值定理—柯西中值定理

微分中值定理—柯西中值定理前面我们已经学习了罗尔中值定理,和拉格朗日中值定理,它们的相同点是,研究的曲线都能用函数来表示。那假如曲线不能被函数表示呢,用柯西中值定理。 1 定义 柯西中值定理是拉格朗日中值定理的推广。如果&#xff0c…

助力企业节能降耗:综合能效管理之场景控制

企业综合能效管理系统是为企业提供能耗管理、电能质量和用能安全监测的整套解决方案,系统可采集多种类型能源(电、水、天然气、工业气体、冷热量等)数据,并对能源消耗进行分析,包括分类分项能耗、区域能耗、部门能耗数…

海思编码:1、mpp系统详谈以及VI、VPSS、VENC之间的关系

在HiMPP手册中都会有这么一张图 1、VI部分 视频输入设备 视频输入设备支持标准 BT.656、标准 BT.1120、自定义时序等若干种时序输入,负责对时序进行解析。 视频物理通道 视频物理通道负责将输入设备解析后得到的视频数据输出到 DDR。在真正将数据输出到 DDR 之前…

FTP-----局域网内部远程桌面

此文包含详细的图文教程。有疑问评论区留言。博主第一时间解决。 目录 一、被远程桌面的电脑 1.开启远程权限 2.添加账户,有本地账户跳过这步 3.帐号隶属于 远程桌面 4.帐号隶属于 本地用户组 二、本地电脑连接远程桌面 前提条件: 1.两台电脑在…

接口自动化【二】(图形验证码处理)

文章目录 前言一、图形验证码图片获取(需要实际截图做对比补充)二、调第三方接口获取验证码三、后端登录接口(举例)总结前言 讲解了图片验证的处理;在接口测试中遇见的一些问题;多部分编码的注意点 一、图形验证码图片获取(需要实…

【4.13(补)】二叉搜索树的遍历、插入、删除

文章目录二叉搜索树的最近公共祖先二叉搜索树中的插入操作删除二叉搜索树中的节点二叉搜索树的最近公共祖先 235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode) 因为二叉搜索树是有序的,第一次找到p和q中间的值,就是最近的公共祖先…

【FPGA-DSP】第六期:Black Box调用流程

目录 1. 实际操作流程 1.1 Verilog 代码编写 1.2 system generator操作 1.2.1 Black box模块 1.2.2 Simulink 搭建 2. Simulink模型优化 System Generator是一个Xilinx公司的工具,用于设计数字信号处理系统。Black Box是System Generator中的一个block&#…

【数值模型系列】CAMx编译运行中的几个小问题

最近在CAMx的编译运行工作中,遇到了几个小问题,在此记录一下。 问题1:CAMx2IOAPI编译报错 报错如下: CAMx2IOAPI依赖于IOAPI和NetCDF库,在Makefile文件中加以配置即可,但出现此错的原因其实是默认的Makef…

【大数据之Hadoop】十四、MapReduce之Combiner合并

Combiner是Mapper和Reducer之间的组件,其组件的父类是Reducer。 Combiner和Reducer的区别: Combiner是运行在每一个MapTask所在的节点,即对每一个MapTask的输出进行局部汇总,减少网络传输量。 Reducer则是接收全局是Mapper的输出…

涨点神器:Yolov5/Yolov7引入CVPR2023 InternImage:注入新机制,扩展DCNv3,助力涨点,COCO新纪录65.4mAP!

1.InternImage介绍 论文:https://arxiv.org/abs/2211.05778 代码:GitHub - OpenGVLab/InternImage: [CVPR 2023 Highlight] InternImage: Exploring Large-Scale Vision Foundation Models with Deformable Convolutions 理论部分参考知乎:CVPR2023 Highlight | 书生模型霸…

GPT-4要革程序员的命?智能开发的理想与现实 | 爱分析调研

“生成式人工智能(AIGC)将在三年内终结编程。” ——Matt Welsh,前哈佛大学计算机科学教授、Google 工程主管 GPT-4 也许还不完美,但智能开发时代真的来了 美国时间3月14日,OpenAI 正式发布 GPT-4,在 Chat…