1 引言

定长数组包

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

// 定长缓冲区

//公众号:c语言与cpp编程

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 总结

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

c语言定义不定长数组初始化_C语言如何定义一组长度不定的数组?相关推荐

  1. qt 二维数组初始化_C语言二维数组的定义、初始化、赋值

    上节讲解的数组可以看作是一行连续的数据,只有一个下标,称为一维数组.在实际问题中有很多数据是二维的或多维的,因此C语言允许构造多维数组.多维数组元素有多个下标,以确定它在数组中的位置.本节只介绍 二维 ...

  2. c语言定义不定长数组初始化_C语言数组初始化的三种方式

    1.{0} 初始化 int arr1[3] = {0}; 使用 {0} 的方式最简洁,一般在定义的时候使用. 2.for 循环初始化 int arr2[3]; for (int i = 0; i &l ...

  3. c++ 数组初始化_C++入门篇(二十九),字符数组在内存中存储的情况

    我们以前说过,字符和字符串是不同的.字符只能是一个,字符串是由若干个字符链接而成的,可是'a'和"a"有区别吗? 其实字符和字符串的区别有点像单词和句子的区别,一个句子可能只有一个 ...

  4. C语言的二维数组初始化的几种方式介绍(私藏大数组初始化方式)

    C语言的二维数组初始化的几种方式介绍 1.直接赋值 2.循环对每个元素赋值 3.借用memset/memset_s初始化为0或-1 4.`数组所有元素初始化为相同值(用于大数组初始化贼方便)` 1.直 ...

  5. 机械优化黄金分割法c语言编程,黄金分割法_机械优化设计_C语言程序.doc

    黄金分割法_机械优化设计_C语言程序黄金分割法_机械优化设计_C语言程序 黄金分割法的优化设计 实验报告 学院:机电工程 机制自动化11-03班 学号:541102010326 姓名:刘点点 1,黄金 ...

  6. c++什么时候数组溢出_C语言,营养丰富的C语言五,变长数组不是动态数组

    大家好,感谢朋友的支持阅读和关注,虽然我提出的这些小知识点看得人很少,但是每涨一个阅读和关注,都能让我开心很久,所以再次感谢一起学习的朋友们. 查余补漏: 在前几次的讲解中,有朋友提出C语言的内存分配 ...

  7. c++ char数组初始化_c专题指针数组与指针的关联

    ----在今天开始写文章之前,让我不由的想起高中里面学的一篇文章中一段话语,是荀子写的<劝学>:积土成山,风雨兴焉:积水成渊,蛟龙生焉:积善成德,而神明自得,圣心备焉.故不积跬步,无以至千 ...

  8. strcmp可以比较数组么_C语言数组越界了,后果很严重,如何避免?

    素材来源:嵌入式ARM所谓的数组越界,简单地讲就是指数组下标变量的取值超过了初始定义时的大小,导致对数组元素的访问出现在数组的范围之外,这类错误也是 C 语言程序中最常见的错误之一.在 C 语言中,数 ...

  9. c语言中申请内存并初始化,c语言中结构体的定义、初始化及内存分配

    #include struct person { char *name; int age; }; int main() { //结构体可以定义在函数内,也可以定义到函数外 //相当于全局变量与局部变量 ...

最新文章

  1. 聚焦联机交易分析一体化,巨杉数据库湖仓一体云产品全线升级
  2. netstat 详解
  3. 【数据结构与算法】之深入解析“我的日程安排表II”的求解思路与算法示例
  4. GitLab 小组中的项目访问权限赋予给用户
  5. 字符串的排序不一定要完全按照字典序(洛谷P1012题题解,Java语言描述)
  6. linux内核驱动之 用户空间和内核空间
  7. python socket 主动断开_python之使用ctrl+c断开多线程(TcpSocketServer连接)出现端口占用的情况...
  8. [区块链] 带你进入Bitcoin开发 - 环境搭建
  9. swiper 定义放多少张图片_小程序瀑布流组件:支持翻页与图片懒加载
  10. 44个实用的Apache Web Server面试问题及答案
  11. keil_lic.exe注册机使用
  12. 电商seo培训之卖家搜索优化步骤
  13. koa2 mysql sequelize_[转]使用nodejs-koa2-mysql-sequelize-jwt 实现项目api接口
  14. Ubuntu 之 Audacity踩坑之旅
  15. hive 启动报错java.net.URISyntaxException: Relative path in absolute URI: ${system:java.io.tmpdir%7D/$%..
  16. 网易2018校园招聘编程题真题集合1魔法币
  17. Tensorflow2.x: TensorFlow Addons介绍
  18. OUC2021软件工程OUC拼车程序小组Gamma阶段博客目录
  19. springSecurity+jwt中实现互踢功能
  20. VS2013新建Win32项目改配置x64位 图文详解

热门文章

  1. USB-C接口手机转接器方案,支持同时听歌跟充电
  2. 亚马逊云服务(AWS) 为机器学习扩圈 触及每一位AI工作者
  3. iphone自适应屏幕亮度_如何在iPhone或iPad上调整屏幕亮度
  4. TX Text Control,ActiveX-COM适用于Visual Basic 6
  5. 华为mate40会是鸿蒙,【华为Mate40Pro评测】从华为Mate40系列窥见EMUI的未来 与鸿蒙OS共筑一致用户体验(全文)_华为 Mate40 Pro_手机评测-中关村在线...
  6. Notes 20180308 : 语句
  7. nuxt.js的ssr渲染
  8. 搜狐快站,一款可视化建站工具。
  9. Opencv教程:人工智能图像处理入门案例实战
  10. flutter 数据持久化之sqflite