书山有路勤为径,学海无涯苦作舟
前言
柔性数组(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;
}
总结
- 0为数组不占内存空间,指针变量需要占用指针域空间
- 柔性数组在申请内存时同结构体内存连续;指针变量申请内存时地址不连续,需要单独申请和释放
如有错漏,敬请指正!