1 引言

定长数组包

在平时的开发中,缓冲区数据收发时,如果采用缓冲区定长包,假定大小是 1k,MAX_LENGTH 为 1024。结构体如下:

//  定长缓冲区struct max_buffer{    int   len;    char  data[MAX_LENGTH];};

数据结构的大小 >= sizeof(int) + sizeof(char) * MAX_LENGTH为了防止数据溢出的情况,data 的长度一般会设置得足够大,但也正是因为这样,才会导致数组的冗余。

假如发送 512 字节的数据,  就会浪费 512 个字节的空间, 平时通信时,大多数是心跳包,大小远远小于 1024,除了浪费空间还消耗很多流量。

内存申请:

if ((m_buffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL){    m_buffer->len = CUR_LENGTH;    memcpy(m_buffer->data, "max_buffer test", CUR_LENGTH);    printf("%d, %s\n", m_buffer->len, m_buffer->data);}

内存释放:

free(m_buffer);m_buffer = NULL;

指针数据包

为了避免空间上的浪费,我们可以将上面的长度为 MAX_LENGTH 的定长数组换为指针, 每次使用时动态的开辟 CUR_LENGTH 大小的空间。数据包结构体定义:

struct point_buffer{    int   len;    char  *data;};

数据结构大小 >= sizeof(int) + sizeof(char *)但在内存分配时,需要两步进行:

  • 需为结构体分配一块内存空间;
  • 为结构体中的成员变量分配内存空间;

内存申请:

if ((p_buffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL){    p_buffer->len = CUR_LENGTH;    if ((p_buffer->data = (char *)malloc(sizeof(char) * CUR_LENGTH)) != NULL)    {        memcpy(p_buffer->data, "point_buffer test", CUR_LENGTH);        printf("%d, %s\n", p_buffer->len, p_buffer->data);    }}

内存释放:

free(p_buffer->data);free(p_buffer);p_buffer = NULL;

虽然这样能够节约内存,但是两次分配的内存是不连续的, 需要分别对其进行管理,导致的问题就是需要对结构体和数据分别申请和释放内存,这样对于程序员来说无疑是一个灾难,因为这样很容易导致遗忘释放内存造成内存泄露。

有没有更好的方法呢?那就是今天的主题柔性数组。

2 柔性数组

什么是柔性数组?

柔性数组成员(flexible array member)也叫伸缩性数组成员,这种代码结构产生于对动态结构体的需求。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,鉴于这种代码结构所产生的重要作用,C99 甚至把它收入了标准中:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.

柔性数组是 C99 标准引入的特性,所以当你的编译器提示不支持的语法时,请检查你是否开启了 C99 选项或更高的版本支持。

C99 标准的定义如下:

struct test {    short len;  // 必须至少有一个其它成员    char arr[]; // 柔性数组必须是结构体最后一个成员(也可是其它类型,如:int、double、...)};
  • 柔性数组成员必须定义在结构体里面且为最后元素;
  • 结构体中不能单独只有柔性数组成员;
  • 柔性数组不占内存。

在一个结构体的最后,申明一个长度为空的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为 0 的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量,

但对于这个数组的大小,我们可以进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!

对于柔性数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等, 其实柔性数组成员在实现跳跃表时有它特别的用法,在Redis的SDS数据结构中和跳跃表的实现上,也使用柔性数组成员。它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题

柔性数组解决引言的例子

//柔性数组struct soft_buffer{    int   len;    char  data[0];};

数据结构大小 = sizeof(struct soft_buffer) = sizeof(int),这样的变长数组常用于网络通信中构造不定长数据包, 不会浪费空间浪费网络流量。

申请内存:

if ((softbuffer = (struct soft_buffer *)malloc(sizeof(struct soft_buffer) + sizeof(char) * CUR_LENGTH)) != NULL){    softbuffer->len = CUR_LENGTH;    memcpy(softbuffer->data, "softbuffer test", CUR_LENGTH);    printf("%d, %s\n", softbuffer->len, softbuffer->data);}

释放内存:

free(softbuffer);softbuffer = NULL;

对比使用指针和柔性数组会发现,使用柔性数组的优点:

  • 由于结构体使用指针地址不连续(两次 malloc),柔性数组地址连续,只需要一次 malloc,同样释放前者需要两次,后者可以一起释放。
  • 在数据拷贝时,结构体使用指针时,必须拷贝它指向的内存,内存不连续会存在问题,柔性数组可以直接拷贝。
  • 减少内存碎片,由于结构体的柔性数组和结构体成员的地址是连续的,即可一同申请内存,因此更大程度地避免了内存碎片。另外由于该成员本身不占结构体空间,因此,整体而言,比普通的数组成员占用空间要会稍微小点。

缺点:对结构体格式有要求,必要放在最后,不是唯一成员。

3 总结

在日常编程中,有时需要在结构体中存放一个长度是动态的字符串(也可能是其他数据类型),可以使用柔性数组,柔性数组是一种能够巧妙地解决数组内存的冗余和数组的越界问题一种方法。非常值得大家学习和借鉴。

喜欢本篇内容请给我们点个在看

c99变长数组_你学过数组,那你知道柔性数组吗?相关推荐

  1. c99变长数组_C语言变长数组使用详解

    看如下代码: #include typedef struct { int len; int array[]; }SoftArray; int main() { int len = 10; printf ...

  2. lstm 变长序列_基于变长时间间隔LSTM方法的胎儿异常体重预测

    1 介绍 预测胎儿体重是产前监护的重要内容, 是医生对孕妇进行临床处理的重要依据. 近年来研究显示, 低体重儿的存活率和扛感染能力相对低下[, 并且与低智商有密切联系[. 而巨大儿则会引起胎儿宫内窘迫 ...

  3. c语言随机生成整数存放一维数组_文科生学 Python 系列 7: Numpy 数组/索引和切片...

    第四课:本课内容: • 0. 导入 NumPy 包 • 1. 创建 NumPy 数组 • 2. 索引和切片 • 3. 读取文件 • 4. 布尔型索引 • 5. 数组的运算 • 6. 常用函数举例 Nu ...

  4. python 空数组_从零开始学python之numpy

    Numpy是python中一个常用的库,其支持大量的维度数据与矩阵运算,也拥有针对数据运算的数学函数库,用于独特的数组存储方式及对应的处理方法.一般用以下方式导入: import numpy as n ...

  5. Java实现有序数组和无序数组_【算法】字典的诞生:有序数组 PK 无序链表

    参考资料 <算法(java)>                           - - Robert Sedgewick, Kevin Wayne <数据结构>       ...

  6. java中将txt转换为数组_关于android:在Java中将列表转换为数组

    本问题已经有最佳答案,请猛点这里访问. 在Java中,如何将EDCOX1的0度转换为EDCOX1×1? 检查以下代码: ArrayList tiendas; List tiendasList; tie ...

  7. 多个数字数组_七个问题帮助初学者深入理解Java数组

    短文涨姿势,看了不白看,不关注等啥? 几乎所有的高级语言当中,都提供了一种叫做"数组"的东西,Java语言当然也不例外.我们通过数组可以很方便的存储和管理一组数据.因为在Java语 ...

  8. javascript字典中添加数组_如何在 JavaScript 中更好地使用数组

    在 freeCodeCamp 社区阅读原文. 本文短小精悍,我保证.在过去的数个月里,我注意到在我审阅的 pull request 中有四个(关于数组使用的)错误经常出现.同时,我自己也会犯这些错误, ...

  9. C语言分支与循环--分支篇,那些分支语句的坑你都避开了吗(附加C99变长数组)

    前言

最新文章

  1. 台湾国立大学郭彦甫Matlab教程笔记(17)numerical integration
  2. 我的第五个网页制作:pre、html转义、abbr标签的使用
  3. VTK:可视化算法之DecimateHawaii
  4. Qt Creator使用命令行选项
  5. JAVA vs C++之速度—
  6. linux qt应用程序全屏,QT在ubuntu下实现界面全屏,侧边栏隐藏,上边栏隐藏【实例】...
  7. python模块导入_python模块导入
  8. Oracle 11g Dataguard搭建及知识梳理
  9. 为什么要做漏洞扫描呢?
  10. Spring boot配置log4j
  11. 《图解机器学习-杉山将著》读书笔记---CH5
  12. Android studio jni
  13. SQL安装步骤及可能遇到的错误
  14. [ZZ]Sign Up for the First-Ever Appium Roadshow on August 20th in New York City
  15. c语言图片渐入代码_如何用c语言读取图片
  16. 反客为主?00 后大学生 “反向背调” 雇主,Z 世代要重塑职场
  17. 携程网今日瘫痪 官方称遭到不明攻击
  18. R语言和Julia以及Python使用Feather包共享数据
  19. linux intel wifi驱动,ubuntu 8.04下面 Intel WIFI link 5100无线网卡驱动安装
  20. 清华大学-中国人民银行金融研究所2023年联合培养博士研究生招生简章

热门文章

  1. ts文件编译后变量在vscode里报错
  2. Nagios基本搭建
  3. 【C++】复制构造函数
  4. easyui使用datagrid时列名包含特殊字符导致表头与数据错位的问题
  5. hdu 2795 公告板 (单点最值)
  6. Xilinx FPGA 仿真环境设置(ISE + Modelsim + Debussy)
  7. Linux系统编程之进程与线程控制原语对比
  8. OpenCV的cv::cvtColor函数之“CV_RGB2GRAY”: 未声明的标识符错误解决方法
  9. 机器学习实战:支持向量机
  10. TI的TCP/IP协议栈---NDK