C语言 一个特殊的数组【柔性数组】

news2024/11/20 10:28:41

文章目录

  • 前言
  • 柔性数组的特点
  • 柔性数组的使用
  • 柔性数组的优势
  • 写在最后

前言

  • 也许你从来就没有听过柔性数组flexible array)这个概念,但他是真的存在;
  • 柔性数组的概念存在于C99标准当中,C99标准表示:结构体的最后一个成员可以是一个大小不确定的数组,这个数组就叫做柔性数组;
  • 既然搭配了柔性这一修饰词,那么这个数组就不会是”耿直“的了。

柔性数组的特点

  1. 柔性数组的大小是不确定的;
  2. 他是存在于结构体当中,并且只能放在结构体的最后一个成员位置;
  3. 柔性数组所在位置的前面至少要有一个成员;
  4. 当柔性数组存在于一个结构体当中时,计算结构体的大小,不将柔性数组算在内;
  5. 柔性数组是未知大小的数组,要使用他,必须要用malloc来为他开辟空间,并且这个空间的大小一定要大于所在结构体的大小,简单来说,这个开辟的空间的大小减去所在的结构体大小剩下的空间就是这个柔性数组可以使用的了。

柔性数组的定义:

1.
struct s
{
	char a;
	int a[];  // [] 里不放数据
};
// 当然也可以
2.
struct s
{
	int a;
	char b[0]; // 上面的定义在有些编译器上可能会报错,这样也是可以的
};

上述1中的int a[],2中的char b[0],就是所谓的柔性数组。

柔性数组的大小是不确定,下面来测试一下结构体的大小:

#include <stdio.h>
//1.
struct s1
{
	char a;
	char b;
	int c;
	int d[];  // [] 里不放数据
};
// 当然也可以
//2.
struct s2
{
	int a;
	int b;
	int c;
	char d[0]; // 上面的定义在有些编译器上可能会报错,这样也是可以的
};

int main()
{
	printf("%d\n", (int)sizeof(struct s1));
	printf("%d\n", (int)sizeof(struct s2));

	return 0;
}

运行结果:

在这里插入图片描述
如果我们不看最后一个柔性数组成员,计算出来的结构体大小恰好是上面的结果,所以,当结构体里面含有柔性数组成员的时候,计算结构体大小时,是不会将柔性数组的大小计算在内的。

柔性数组的使用

有了上面对柔性数组的认识,那么柔性数组是如何使用的呢?

上面说了要用malloc来为此开辟空间,并且开辟的空间的大小要大于所在结构体的大小。

例子:

#include <stdio.h>
#include <stdlib.h>

struct s
{
	char a;
	int b[]; // 柔性数组
}* s; // 创建结构体指针变量

int main()
{                                 // 这里是结构体的空间 //这是柔性数组的空间:十个整型
	struct s* tmp = (struct s*)malloc(sizeof(struct s) + 40);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	s = tmp;
	
	// 给柔性数组赋值
	for (int i = 0; i < 10; i++)
	{
		s->b[i] = i + 1;
	}

	// 打印柔性数组的内容
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", s->b[i]);
	}

	// 记得释放空间
	free(s);

	return 0;
}

在这里插入图片描述
运行结果:

在这里插入图片描述

当然,如果需要给柔性数组扩容,可以用realloc进行以下操作(核心代码):

                                           // 将柔性数组的空间增大到可以存储15个整型
	struct s* cur = (struct s*)realloc(s, sizeof(struct s) + 60);
	if (cur == NULL)
	{
		perror("realloc fail");
		exit(-1);
	}
	s = cur;

	// 给扩容的空间赋值
	for (int i = 10; i < 15; ++i)
	{
		s->b[i] = i + 1;
	}

	// 打印柔性数组扩容后的内容
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", s->b[i]);
	}

运行结果:

在这里插入图片描述

据此,柔性数组的使用掌握这些已经够用了,可以看到,柔性数组是可以随意更改数组大小的,“柔性”体现的淋漓尽致,不过一定要注意内存泄漏的问题。

柔性数组的优势

学会了柔性数组的使用,实际上,上面实现的柔性数组的功能还可以这样设计:

#include <stdio.h>
#include <stdlib.h>

typedef struct s
{
	char b;
	int* a;
}s; // typedef 重命名

int main()
{
	s* stru;  // 指向类型为s的结构体指针
	s* tmp1 = (s*)malloc(sizeof(s));
	if (tmp1 == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	stru = tmp1;  // 将开辟的结构体空间给给stru
	stru->b = 'a';
	                // 开辟可以存放十个整型的空间
	int* tmp2 = (int*)malloc(sizeof(int) * 10);
	if (tmp2 == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	stru->a = tmp2; // 将开辟的可以存放十个整型的空间给给stru指向的结构体里的整型指针

	// 给a指向的空间赋值
	for (int i = 0; i < 10; i++)
	{
		stru->a[i] = i + 1;
	}

	// 打印a指向的空间里的内容
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", stru->a[i]);
	}

	// 别忘记释放噢,这里开辟了两次就要释放两次,并且要有里到外依次释放
	free(stru->a);
	stru->a = NULL;
	free(stru);
	stru = NULL;

	return 0;
}

柔性数组的使用那个设计放法与这个设计方法实现的效果是一样的,但是柔性数组的那个设计有两个好处:

  1. 方便内存释放:
    可以看到第二个设计释放了两次,如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
  2. 这样有利于访问速度:
    连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,也没有高多少,反正你跑不了要用做偏移量的加法来寻址)

写在最后

多学一些知识,在以后能用来解决问题的方法就更多,看完本篇文章,希望你能够理解柔性数组并且可以很好的使用。

感谢阅读本小白的博客,错误的地方请严厉指出噢!

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

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

相关文章

Linux-进程概念

目录 进程状态&#xff1a; 操作系统简图 调用进程bin.exe的详细过程 cpu运行队列的结构 R进程和阻塞进程 进程状态&#xff1a;挂起&#xff1a; Linux操作&#xff1a; ​编辑 R运行状态 S休眠状态 T暂停状态&#xff1a; kill kill -18 表示继续 kill -9 杀死进程…

肿瘤内微生物群在癌症转移中的新作用

谷禾健康 癌症是一种复杂的疾病&#xff0c;归因于多因素变化&#xff0c;导致治疗策略困难。 90%的癌症患者死于复发或转移。癌症转移是恶性肿瘤进展的关键步骤&#xff0c;由癌细胞内在特性和外在环境因素决定。 一些微生物组通过诱导癌性上皮细胞和慢性炎症促进癌发生、癌症…

光耦合器:类型及其应用

光耦合器&#xff1a;类型及其应用 介绍 光耦合器&#xff08;也称为光隔离器&#xff09;是一种在两个隔离电路之间传输电信号的半导体器件。光耦合器由两部分组成&#xff1a;发射红外光的LED和检测LED光的光敏器件。这两个部件都装在一个带有连接销的黑匣子中。输入电路接收…

数据可视化笔记记录(二)pandas

笔记内容是根据B站上面的一位老师的视频而记录了&#xff0c;因为我也还在学习&#xff0c;所以个人理解可能会出现错误&#xff0c;若有错误请指出。另外这篇文章会很长 B站视频连接、 numpy,matplotlib的学习记录 pandas 学习记录 SerIes结构&#xff0c;一图胜千言 Seri…

实用工具:FastDoc 文档提取工具

实用工具&#xff1a;FastDoc 文档提取工具 简单、实用的 HTTP API 文档生成工具&#xff0c;支持 Spring MVC/Spring Boot 下的控制器信息提取&#xff0c;类似 Swagger 但稍有不同的机制。 在线演示地址在 https://framework.ajaxjs.com/demo/fast-doc/。 关于研发者这工具…

二三层转发原理

二层转发原理 数据帧 二层即数据链路层的转发是以数据帧的格式进行转发&#xff0c;数据帧的格式如下&#xff1a; 目的地址(Destination Address,DA) &#xff1a;可以是单独的地址,或者是广播或组播MAC地址。 源地址(Source Address,SA) &#xff1a;用来识别发送没备,在S…

听说你想用开发者工具调试我的网站?挺可以的啊。25

本篇博客重点为大家介绍&#xff0c;如何禁止用户在浏览器中查看源码&#xff0c;禁用开发者工具调试等前端需求 案例已更新到 爬虫训练场 文章目录禁用右键&#xff0c;禁用 F12禁用 ctrl U 查看源代码&#xff0c;禁用 ctrl shift i 打开开发者工具实现开发者工具无限 deb…

arcgis使用Python脚本进行批量截图

arcgis使用Python脚本进行批量截图 介绍 最近公司数据部那边有个需求&#xff0c;需要结合矢量数据和影像数据&#xff0c;进行批量截图&#xff0c;并且截图中只能有一个图斑&#xff0c;还要添加上相应的水印。 思路 一开始我是准备使用QGIS直接进行批量出图&#xff0c;…

Ant Design在处理业务表单中的一些实践

文章目录前言表单处理实践Modal 清空旧数据使用Form.create和getFieldDecorator对Form进行包装表单控件是switch自定义表单控件Table列表SelectshowSearch 用于在选择框中显示搜索框其他需要注意的组件Tabs其它前言 目前&#xff0c;很多中小企业前端实现采用了react&#xff…

二、总线控制

文章目录一、引子二、总线判优控制1.基本概念2.方式&#xff08;1&#xff09;集中式①链式查询方式②计数器定时查询方式③独立请求方式&#xff08;2&#xff09;分布式三、总线通信控制1.基本概念2.方式&#xff08;1&#xff09;同步通信①介绍②同步式数据输入③同步数据输…

数据结构与算法学习推荐

推荐&#xff1a;官网地址&#xff1a;http://localhost:8000/algorithm/github&#xff1a;https://github.com/paiDaXing-web/You-Dont-Know-Algorithm 点star 大话搜索搜索一般指在有限的状态空间中进行枚举&#xff0c;通过穷尽所有的可能来找到符合条件的解或者解的个数。…

佳能C5235彩色激光复印机复印有底灰

故障描述&#xff1a; 佳能C5235彩色激光复印机&#xff0c;复印的时候有不同颜色的底灰问题&#xff1b; 检测分析&#xff1a; 打印不同纯色的纸张进行测试发现每张纸的上面都有不同颜色的底灰&#xff1b; 拆机进行检测吧&#xff0c;先打开前盖&#xff0c;拧下左右两边的螺…

CMake基础教程

CMake最大优势是跨平台&#xff0c;可以根据不同的平台生成Makefile文件&#xff0c;看下CMake的基础使用。 cmake&#xff08;单目录单文件&#xff09; 第一个最简单的cmake构建。 demo1.cpp代码&#xff1a; #include <iostream>using namespace std;int main(int…

【基于机械臂触觉伺服的物体操控研究】UR5e动力学建模及代码实现

我的毕设题目定为《基于机械臂触觉伺服的物体操控研究》&#xff0c;这个系列主要用于记录做毕设的过程。 前言&#xff1a;UR系列是优傲公司的代表产品&#xff0c;也是目前比较通用的产品级机械臂。所以我打算用该机械臂进行毕设的仿真实现。关于其动力学建模&#xff0c;网…

台阶问题-

台阶问题 题目描述 有NNN级的台阶&#xff0c;你一开始在底部&#xff0c;每次可以向上迈最多KKK级台阶&#xff08;最少111级&#xff09;&#xff0c;问到达第NNN级台阶有多少种不同方式。 输入格式 两个正整数N&#xff0c;K。 输出格式 一个正整数&#xff0c;为不同…

JVM(一)——架构基础

JVM java虚拟机 java gc 主要回收的是 方法区 和 堆中的内容&#xff0c;以下架构图是重点&#xff1a; 方法区和堆是线程共享&#xff0c;java栈、本机方法栈、程序计数器是线程私有。 运行时数据区可以用Runtime.getRuntime()获取 字节码执行引擎&#xff0c;修改程序计数器…

不要怀疑了,个人也是可以做好跨境电商的!

近几年随着跨境电商卖家们赚得盆满钵满&#xff0c;许多人都想从中分一杯羹&#xff0c;进而入住了跨境电商市场&#xff0c;有人与一些公司企业合作&#xff0c;也有人选择了自己做跨境电商平台&#xff0c;个人做的优劣势又有哪些呢&#xff1f; 个人做跨境电商平台最明显突…

A V L树

概念 在之前介绍了搜索二叉树&#xff0c;但是当我们插入的数据若是有序或者接近于有序&#xff0c;那么此时查找的效率底下&#xff0c;于是俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法&#xff1a;当向二叉搜索树中插入新结点后&am…

javaEE学生教学实习计划申报系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 javaEE学生教学实习计划申报系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&…

正点原子【第四期】手把手教你学 Linux之驱动开发篇-01

学习目的 了解驱动开发和应用开发的过程&#xff0c;具有一定的基础就行 第一讲&#xff1a;linux驱动开发与裸机开发区别 刚开始听不懂很正常&#xff0c;等之后学了一点你就会知道它说啥了 第二讲&#xff1a;字符设备驱动开发基础 字符设备驱动是最简单的&#xff0c;块设…