0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

日常编程中柔性数组的定义和应用

strongerHuang 来源:C语言与CPP编程 作者:自成一派123 2021-11-11 14:51 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1 引言

定长数组包

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

//定长缓冲区
structmax_buffer
{
intlen;
chardata[MAX_LENGTH];
};

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

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

内存申请:

if((m_buffer=(structmax_buffer*)malloc(sizeof(structmax_buffer)))!=NULL)
{
m_buffer->len=CUR_LENGTH;
memcpy(m_buffer->data,"max_buffertest",CUR_LENGTH);
printf("%d,%s
",m_buffer->len,m_buffer->data);
}

内存释放:

free(m_buffer);
m_buffer=NULL;

指针数据包

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

structpoint_buffer
{
intlen;
char*data;
};

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

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

内存申请:

if((p_buffer=(structpoint_buffer*)malloc(sizeof(structpoint_buffer)))!=NULL)
{
p_buffer->len=CUR_LENGTH;
if((p_buffer->data=(char*)malloc(sizeof(char)*CUR_LENGTH))!=NULL)
{
memcpy(p_buffer->data,"point_buffertest",CUR_LENGTH);
printf("%d,%s
",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 标准的定义如下:

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

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

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

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

柔性数组解决引言的例子

//柔性数组
structsoft_buffer
{
intlen;
chardata[0];
};

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

申请内存:

if((softbuffer=(structsoft_buffer*)malloc(sizeof(structsoft_buffer)+sizeof(char)*CUR_LENGTH))!=NULL)
{
softbuffer->len=CUR_LENGTH;
memcpy(softbuffer->data,"softbuffertest",CUR_LENGTH);
printf("%d,%s
",softbuffer->len,softbuffer->data);
}

释放内存:

free(softbuffer);
softbuffer=NULL;

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

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

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

3 总结

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


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    90

    文章

    3708

    浏览量

    96768
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27114

原文标题:柔性数组的定义和应用

文章出处:【微信号:strongerHuang,微信公众号:strongerHuang】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    柔性天线技术原理及核心特性

    柔性天线的定义与工作原理 柔性天线是一种基于柔性基材(如聚酰亚胺、PET或透明导电膜)的无线通信天线,其核心功能是通过无线电波实现信号的接收和传输。其工作原理与传统天线类似,但在结构设
    发表于 12-05 09:10

    C语言的编程技巧

    1、宏定义使用do{...}while(0)‌:当宏定义包含多条语句时,使用do{...}while(0)可以避免语句嵌套问题,确保宏的独立性。‌ ‌2、灵活数组成员‌:C99标准允
    发表于 11-27 06:46

    数组的初体验

    程序也需要容器,只不过该容器有点特殊,它在程序是一块连续的,大小固定并且里面的数据类型一致的内存空间,它还有个好听的名字叫数组。可以将数组理解为大小固定,所放物品为同类的一个购物袋
    发表于 11-25 08:06

    二维数组介绍

    定义是这样的: int array[n][m] 访问: array[a] 那么被访问元素地址的计算方式就是: array + (m * a + b) 这个就是二维数组在内存的本质,其实和一维
    发表于 11-25 07:42

    请问keil+Env怎么把很大的数组定义到SDRAM

    keil+Env怎么把很大的数组定义到SDRAM? RTT自带的SDRAM程序运行正常,能够申请里面的空间。 但是没有办法把很大的数组——ltdc_lcd_framebuf[1280
    发表于 10-11 16:10

    MDK536 + SWM34S平台移植LVGL8.3.3 定义数组使用ALIGN()对齐时编译报错是什么原因导致的?

    lvgl_thread_stack[PKG_LVGL_THREAD_STACK_SIZE]; 会编译报错 img_cogwheel_indexed16.c等一些样例文件定义数组处,例如 const
    发表于 10-09 08:00

    数组程序无法运行怎么解决?

    主控是103,程序定义一个const类型 128k只读数组,放在flash上,程序无法运行,堆栈都初始化不了,在keil编译下正常,在rtthread studio下编译无法运行,求教 是内存管理的问题吗
    发表于 09-15 06:21

    吉事励可编程交流负载箱定义介绍

    和动态改变负载的各种参数,以模拟真实世界的复杂负载特性或特定的测试工况。 以下是其关键定义要素和特点: 核心功能: 模拟负载: 替代实际设备(如电机、变压器、照明、服务器、家用电器等),作为测试对象的“假负载”。
    的头像 发表于 08-04 16:50 517次阅读

    JMC1200T柔性和可扭转波导JUPITER

    JMC1200T柔性和可扭转波导JUPITERJMC1200T是由JUPITER生产的柔性和可扭转波导,主要用于高频率(微波)数据传输的波导组件,兼具柔性和可扭转性能,适用于各种复杂应用环境和动态
    发表于 07-10 09:38

    基于Nanopaint压感油墨系统的柔性传感系统在体育表现监测的应用

    和导电材料的对比,Nanopaint压感油墨技术的优势: 1)工艺先进:可直接打印在导电层上,降低生产和应用成本; 2)柔性设计:具有耐拉伸、弯折特性,并维持稳定的性能; 3)稳定性一致:经过循环
    发表于 05-14 13:18

    如何基于Kahn处理网络定义AI引擎图形编程模型

    本白皮书探讨了如何基于 Kahn 处理网络( KPN )定义 AI 引擎图形编程模型。KPN 模型有助于实现数据流并行化,进而提高系统的整体性能。
    的头像 发表于 04-17 11:31 683次阅读
    如何基于Kahn处理网络<b class='flag-5'>定义</b>AI引擎图形<b class='flag-5'>编程</b>模型

    请问LabView如何直接发送二维数组到DMD上显示?

    LabView如何直接发送二维数组到DMD上显示。 在LabView,调用int LoadData(UCHAR*RowData,long length)遇到两个问题,1 二维数组
    发表于 02-27 07:30

    柔性网线和非柔性网线区别有哪些

    柔性网线和非柔性网线在多个方面存在显著差异,以下是两者的主要区别: 一、定义与结构 柔性网线: 定义
    的头像 发表于 01-09 10:10 1597次阅读

    救助,定义一个大一点的数组导致编译不通过问题。

    本例使用 CSU-IDE V6.0.6 ,单片机为CSU38F20,发现在改大数组的时候编译不通过,请各位大神指教。具体问题描述如下: 正常情况: 串口收发缓冲区定义小一点没有问题,如下
    发表于 01-01 15:43

    数组的下标为什么可以是负数

    最近有同学发来这样一段代码,并提出一个问题,数组的下标为什么可以是负数?     #include int main(){ const char *s = "helloworld"; const
    的头像 发表于 12-20 11:18 867次阅读