长度大小为零的数组(柔性数组、可变数组)

文章介绍了C语言中的柔性数组特性,它是GNU/GCC扩展的一种概念,用于构建可变长结构体,特别适用于处理不定长数据包。柔性数组与定长数组和指针域相比,可以减少内存浪费,但在内存分配和管理上需要注意。文章通过示例代码对比了三种方法的优缺点,指出柔性数组在内存连续性以及一次性内存分配上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

书山有路勤为径,学海无涯苦作舟

前言

柔性数组(Arrays of Length Zero)是GNU/GCC在C/C++标准下扩展而引出的概念,其主要应用于构造可变长结构体中,嵌入式领域中一般被用于解析不定长数据包情境下。


一、什么是柔性数组

长度大小为0的数组

这种定义是GNU/GCC扩展下引出的新特性,C/C++在C99标准下也对其进行了扩展支持,二者定义可能稍有不同。
GNU/GCC下柔性数组定义

typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  data[0];
}array_zero;

有些编译器并不认可这种定义方式,编译会报错提示数组长度必须大于0

error: #94-D: the size of an array must be greater than zero

取而代之需要采用以下定义,定义一个长度未知的数组

typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  data[];
}array_zero;

二、柔性数组应用及优势对比

现有如下场景

串口交互接收不定长数据包

1、定长数组


由于不清楚有效数据长度,故定义一个比较大的length值。程序申请足够的内存空间,用于存放实际有效数据。

#define MAX_LENGTH   256
typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  data[MAX_LENGTH];
}array_zero;

但往往有效数据长度达不到所定义的MAX_LENGTH,往往会造成内存浪费,优点在于操作简单。

2、指针域


将上面定长数组更换为指针,每次使用时动态开辟length大小的空间,那么就可以减小内存空间浪费,只消耗一个指针域的空间。

typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  *data;
}array_zero;

考虑数据对齐,结构大小大于等于(sizeof(int)×4)。内存消耗上虽然占据优势,但是也造成了使用时需要分配两次内存。相对应的,释放也需要两次。

typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  *data;
}array_zero;

int main(void)
{
    char  CURR_LENGTH=12;
    array_zero *array_test=NULL;
    if((array_test=(array_zero *)malloc(sizeof(array_zero))) != NULL)
    {
        array_test->length=CURR_LENGTH;
        if((array_test->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
        {
            memcpy(array_test->data, "Hello World", CURR_LENGTH);
            printf("%d, %s\n", array_test->length, array_test->data);
        }
    }
    free(array_test->data);
    free(array_test);
    array_test=NULL;
}

唯一可能造成的问题是如果构造函数仅返回结构体指针,上层应用中可能只会释放掉结构体开辟内存,而忽略掉数据内存的释放,从而造成内存泄漏。

3、柔性数组

柔性数组从数据结构上来说不占用内存空间,因为数组名仅仅是标识符号,用来指示结构体内该数据成员地址偏移量,实际表示了一个常量地址。
考虑如下结构体所占大小

typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  data[0];
}array_zero;

使用sizeof输出如下结果,可以看出柔性数组并未占据内存。

This array size is 12

实际使用时也需要为其开辟curr_length大小的空间,不同的是只需要进行一次内存操作。

#pragma pack(1)
typedef struct _array_
{
    char  flag;
    int   state;
    unsigned char length;
    char  data[0];
}array_zero;
#pragma pack()

int main(void)
{
    char  CURR_LENGTH=12;
    array_zero *array_test=NULL;
    if((array_test=(array_zero *)malloc(sizeof(array_zero)+sizeof(char)*CURR_LENGTH)) != NULL)
    {
         array_test->length=CURR_LENGTH;
         memcpy(array_test->data, "Hello World", CURR_LENGTH);
         printf("%d, %s\n", array_test->length, array_test->data);
    }
    free(array_test);
    array_test=NULL;
}

总结


  1. 0为数组不占内存空间,指针变量需要占用指针域空间
  2. 柔性数组在申请内存时同结构体内存连续;指针变量申请内存时地址不连续,需要单独申请和释放
    如有错漏,敬请指正!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值