可变大小结构体如何定义详解

一、背景

  工作中,在通信协议中常常看到TLV格式数据,不同的type id对应的字符串长度大小不一样。那么该怎么去定义一个结构体去管理这些数据呢?怎么去定义一种可变大小的结构体?本文将讲解如何定义可变大小结构体。

二、定义可变大小结构体

1、方法一:使用指针

1 typedef struct _S_HB_TIME_REPORT_INFO
2 {
3     uint16_t msg_id;
4     uint16_t msg_buf_len;
5     char *p_msg_buf;
6 }__attribute__((packed)) S_HB_TIME_REPORT_INFO;

  如上定义的结构体,成员变量p_msg_buf为一个指针,指向一个不确定长度的字符串,长度由msg_buf_len决定。在使用时可以动态给p_msg_buf分配msg_buf_len的内存,也可以p_msg_buf指向一个已知地址的字符串。

  缺点:1)动态分配内存使用malloc,在使用之后容易忘记free,此时会产生内存泄漏。 2)如果你使用S_HB_TIME_REPORT_INFO结构体的次数比较多,那么就要malloc很多次,此时很容易产生内存碎片。我的建议是,能不使用malloc就尽量不要用malloc。我的通常做法是让p_msg_buf指向一个已知地址的字符串。 3)不管是malloc分配内存还是静态指向一个已知地址的字符串,结构体里面的成员变量char *p_msg_buf是与结构体分离的,不利于操作。

2、方法二:使用柔性数组

  2.1 什么是柔性数组?

  柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。

       2.2 柔性数组有什么用途 ?

  它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题。

  3.3 用法 

  在一个结构体的最后 ,申明一个长度为空的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!对于柔性数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等。

1 typedef struct _S_HB_TIME_REPORT_INFO
2 {
3     uint16_t msg_id;
4     uint16_t msg_buf_len;
5     char msg_buf[0];
6 }__attribute__((packed)) S_HB_TIME_REPORT_INFO;

  我们可以用sizeof(S_HB_TIME_REPORT_INFO) 求得数组大小为4,说明柔性数组是不占用内存的。这样的变长数组常用于网络通信中构造不定长数据包,不会浪费空间浪费网络流量,比如我要发送1024字节的数据,如果用定长包,假设定长包的长度为2048,就会浪费1024个字节的空间,也会造成不必要的流量浪费。

  所以建议使用柔性数组去定义可变大小结构体。

猜你喜欢

转载自www.cnblogs.com/shiyk/p/9371019.html