C语言0数组和柔性数组使用介绍 c 数组 全部0
yuyutoo 2024-10-12 00:46 16 浏览 0 评论
前言:
上次看到一篇面试分享,里面有个朋友说,面试官问了char[0] 相关问题,但是自己没有遇到过,就绕过了这个问题。
我自己在这篇文章下面做了一些回复。
现在我想结合我自己的理解,解释一下这个 char[0] C语言柔性数组的问题。
作者:良知犹存
转载授权以及围观:欢迎关注微信公众号:羽林君
或者添加作者个人微信:become_me
0数组和柔性数组的介绍
0数组顾名思义,就是数组长度定义为0,我们一般知道数组长度定义至少为1才会给它分配实际的空间,而定义了0的数组是没有任何空间,但是如果像上面的结构体一样在最后一个成员定义为零数组,虽然零数组没有分配的空间,但是它可以当作一个偏移量,因为数组名这个符号本身代表了一个不可修改的地址常量。柔性数组也叫可伸缩性数组,而0数组是一种柔性数组。
因为在早期没引入0长度数组的时候, 大家是通过定长数组和指针的方式来解决的, 但是定长数组定义了一个足够大的缓冲区, 这样使用方便, 但是每次都造成空间的浪费指针的方式, 要求程序员在释放空间是必须进行多次的free操作, 而我们在使用的过程中往往在函数中返回了指向缓冲区的指针, 我们并不能保证每个人都理解并遵从我们的释放方式所以 GNU 就对其进行了0长度数组的扩展. 当使用data[0]的时候, 也就是0长度数组的时候,0长度数组作为数组名, 并不占用存储空间。这样就可以更加高效的利用内存。
在C99之后,也加了类似的扩展,只不过用的是 char payload[]这种形式(所以如果你在编译的时候确实需要用到-pedantic参数,那么你可以将char payload[0]类型改成char payload[], 这样就可以编译通过了,当然你的编译器必须支持C99标准的,如果太古老的编译器,那可能不支持了。
0数组的常规使用
首先我们定义一个结构体,再在一个结构体的最后,定义一个长度为0的数组,就可以使得这个结构体是可变长的。
如下所示:
// 0长度数组
struct zero_buffer
{
int len;
char data[0];
};
这个时候 data[0] 只是个数组名, 是不占用存储空间的.
这个结构体的大小用sizeof取长度,实际就是它的成员int的长度,data[0]不占用空间。(数组名仅仅是一个符号, 它不会占用任何空间, 它在结构体中, 只是代表了一个偏移量, 代表一个不可修改的地址常量!)
sizeof(struct zero_buffer) = sizeof(int)
printf("zero struct length is:%d int length is:%d\n",sizeof(struct zero_buffer),sizeof(int));
zero struct length is:4 int length is:4
对于0长数组的这个特点,很容易构造出我们需要的数据结构,如缓冲区,数据包等等。
结构体定义如上所示
假设我们需要设置一条tcp待发送的数据,长度是15,数据内容是"Hello My Friend",这样我们就可以按照如下去定义了。其中 zbuffer->data 为定义数据的地址,len表示数据的长度。
开辟空间之后使用
我们使用的时候, 只需要开辟一次空间即可。
#define CURR_LENGTH 15
struct zero_buffer *zbuffer = NULL;
// 开辟
if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
{
zbuffer->len = CURR_LENGTH;
memcpy(zbuffer->data, "Hello My Friend", CURR_LENGTH);
printf("%d, %s\n", zbuffer->len, zbuffer->data);
}
使用完释放空间
释放空间一次释放即可
// 销毁
free(zbuffer);
zero_buffer = NULL;
其他方法实现一些不定长数据的传输
除了0数组之外,还有使用定长数组和指针数组实现柔性数组的功能。
定长数组
定长数组顾名思义,就是在结构体里面有个定长的数组,这个数组大小是按照我们定义数据最大来进行设置的,为了就是防止数据储存的时候溢出。
定义
// 定长缓冲区
#define MAX_LENGTH 512
struct max_buffer
{
int len;
char data[MAX_LENGTH];
};
不过使用过程中,比如我要发送 512 字节的数据, 如果用定长包, 假设定长包的最大长度 MAX_LENGTH 为 1024, 那么就会浪费 512 个字节的空间, 也会造成不必要的流量浪费。如果数组结构对齐放置(这块知识详细可以看我之前的数据对齐的文章) sizeof(struct max_buffer) = sizeof(int)+ sizieof(char) * MAX_LENGTH
数据包的构造
一般来说, 我们会返回一个指向缓冲区数据结构 max_buffer 的指针.
#define CURR_LENGTH 512
struct max_buffer *mbuffer = NULL;
if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
{
mbuffer->len = CURR_LENGTH;
memcpy(mbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", mbuffer->len, mbuffer->data);
}
前部分 4 个字节 p->len, 作为包头(就是多出来的那部分),这个包头是用来描述紧接着包头后面的数据部分的长度,这里是 1024, 所以前四个字节赋值为 1024 (既然我们要构造不定长数据包,那么这个包到底有多长呢,因此,我们就必须通过一个变量来表明这个数据包的长度,这就是len的作用),
而紧接其后的内存是真正的数据部分, 通过 p->data, 最后, 进行一个 memcpy() 内存拷贝, 把要发送的数据填入到这段内存当中
释放空间
当使用完毕释放数据的空间的时候, 直接释放就可以了
free(mbuffer);
mbuffer = NULL;
使用定长数组, 作为数据缓冲区, 为了避免造成缓冲区溢出, 数组的大小一般设为足够的空间 MAX_LENGTH, 而实际使用过程中, 达到 MAX_LENGTH 长度的数据很少, 那么多数情况下, 缓冲区的大部分空间都是浪费掉的.
但是使用过程很简单, 数据空间的开辟和释放简单, 无需程序员考虑额外的操作
指针数组
它和0数组的区别在于,零数组最后一个结构体元素定义一个data[0],而指针数组就是结构体中需要定义一个指针数组,这里面的指针数组不需要特定在结构体的最后一个元素。
struct point_buffer
{
char *data;
int len;
};
考虑数组结构对齐(这块知识详细可以看我之前的[数据对齐](https://mp.weixin.qq.com/s/35jJQy166-GgR9RaHafhog)的文章), 那么数据结构的大小 sizeof(point_buffer)= sizeof(int) + (补齐int与char * 类型的长度值)+ sizeof(char * ),在我的64位编译环境中int类型是4byte,char * 类型为8byte,所以补齐的长度为8-4,最终sizeof(point_buffer) 为16byte。
如果结构体加上 _attribute((packed)) 数据对齐修饰,则 sizeof(point_buffer)= sizeof(int) sizeof(char * ),最终计算为12byte。
空间分配使用
#define CURR_LENGTH 1024
struct point_buffer *pbuffer = NULL;
if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
{
pbuffer->len = CURR_LENGTH;
if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
{
memcpy(pbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", pbuffer->len, pbuffer->data);
}
}
分配内存时,需采用两步
首先, 需为结构体分配一块内存空间;
其次,再为结构体中的成员变量分配内存空间.
这样两次分配的内存是不连续的, 需要分别对其进行管理. 当使用长度为的数组时, 则是采用一次分配的原则, 一次性将所需的内存全部分配给它.
释放
相反, 释放时也是一样的.
free(pbuffer->data);
free(pbuffer);
pbuffer = NULL;
使用指针结果作为缓冲区, 只多使用了一个指针大小的空间, 无需使用固定长度的数组, 不会造成空间的大量浪费.
但那是开辟空间时, 需要额外开辟数据域的空间, 施放时候也需要显示释放数据域的空间, 但是实际使用过程中, 往往在函数中开辟空间, 然后返回给使用者指向 struct point_buffer 的指针, 这时候我们并不能假定使用者了解我们开辟的细节, 并按照约定的操作释放空间, 因此使用起来多有不便, 甚至造成内存泄漏
小结:
定长数组使用方便, 但是却浪费空间, 指针形式只多使用了一个指针的空间, 不会造成大量空间分浪费, 但是使用起来需要多次分配, 多次释放。所以最优解
0数组的优劣以及注意事项
优点 :比起在结构体中声明一个指针变量、再进行动态分配的办法,这种方法效率要高。因为在访问数组内容时,不需要间接访问,避免了两次访存。此外,0数组也不会像定长数组会造成一定的内存的浪费。
缺点 :在结构体中,数组为0的数组必须在最后声明,使用上有一定限制。
结语
这就是我分享的零数组,如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。
—END—
推荐阅读
【1】C++的智能指针你了解吗?
【2】嵌入式底层开发的软件框架简述
【3】CPU中的程序是怎么运行起来的 必读
【4】cartographer环境建立以及建图测试
【5】设计模式之简单工厂模式、工厂模式、抽象工厂模式的对比
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。
参考文章:https://blog.csdn.net/gatieme/article/details/64131322
相关推荐
- Python操作Word文档神器:python-docx库从入门到精通
-
Python操作Word文档神器:python-docx库从入门到精通动动小手,点击关注...
- Python 函数调用从入门到精通:超详细定义解析与实战指南 附案例
-
一、函数基础:定义与调用的核心逻辑定义:函数是将重复或相关的代码块封装成可复用的单元,通过函数名和参数实现特定功能。它是Python模块化编程的基础,能提高代码复用性和可读性。定义语法:...
- 等这么长时间Python背记手册终于来了,入门到精通(视频400集)
-
本文毫无套路!真诚分享!前言:无论是学习任何一门语言,基础知识一定要扎实,基础功非常的重要,找一个有丰富编程经验的老师或者师兄带着你会少走很多弯路,你的进步速度也会快很多,无论我们学习的目的是什么,...
- 图解Python编程:从入门到精通系列教程(附全套速查表)
-
引言本系列教程展开讲解Python编程语言,Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,它也是互联网最热门的编程语言之一。Python生态丰富,库(模块)极其丰富,这使...
- Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够
-
本书是Python经典实例解析,采用基于实例的方法编写,每个实例都会解决具体的问题和难题。主要内容有:数字、字符串和元组,语句与语法,函数定义,列表、集、字典,用户输入和输出等内置数据结构,类和对象,...
- Python函数全解析:从入门到精通,一文搞定!
-
1.为什么要用函数?函数的作用:封装代码,提高复用性,减少重复,提高可读性。...
- Python中的单例模式:从入门到精通
-
Python中的单例模式:从入门到精通引言单例模式是一种常用的软件设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁创建和销毁的对象,比如日志对象、线程池、缓存等...
- 【Python王者归来】手把手教你,Python从入门到精通!
-
用800个程序实例、5万行代码手把手教你,Python从入门到精通!...
- Python从零基础入门到精通:一个月就够了
-
如果想从零基础到入门,能够全职学习(自学),那么一个月足够了。...
- Python 从入门到精通:一个月就够了
-
要知道,一个月是一段很长的时间。如果每天坚持用6-7小时来做一件事,你会有意想不到的收获。作为初学者,第一个月的月目标应该是这样的:熟悉基本概念(变量,条件,列表,循环,函数)练习超过30个编...
- Python零基础到精通,这8个入门技巧让你少走弯路,7天速通编程!
-
Python学习就像玩积木,从最基础的块开始,一步步搭建出复杂的作品。我记得刚开始学Python时也是一头雾水,走了不少弯路。现在回头看,其实掌握几个核心概念,就能快速入门这门编程语言。来聊聊怎么用最...
- 神仙级python入门教程(非常详细),从0到精通,从看这篇开始!
-
python入门虽然简单,很多新手依然卡在基础安装阶段,大部分教程对一些基础内容都是一带而过,好多新手朋友,对一些基础知识常常一知半解,需要在网上查询很久。...
- Python类从入门到精通,一篇就够!
-
一、Python类是什么?大家在生活中应该都见过汽车吧,每一辆真实存在、能在路上跑的汽车,都可以看作是一个“对象”。那这些汽车是怎么生产出来的呢?其实,在生产之前,汽车公司都会先设计一个详细的蓝图...
- 学习Python从入门到精通:30天足够了,这才是python基础的天花板
-
当年2w买的全套python教程用不着了,现在送给有缘人,不要钱,一个月教你从入门到精通1、本套视频共487集,本套视频共分4季...
- 30天Python 入门到精通(3天学会python)
-
以下是一个为期30天的Python入门到精通学习课程,专为零基础新手设计。课程从基础语法开始,逐步深入到面向对象编程、数据处理,最后实现运行简单的大语言模型(如基于HuggingFace...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)