在讲述柔性数组(Flexible Array)之前,首先要介绍一下不完整类型 (Incomplete Type)。不完全类型是暂时没有完全定义好的类型,缺乏足够的信息(例如长度、类型)去描述一个完整的类型。在C/C++中不完全类型有三种不同形式:void、未指定长度的数组以及具有非指定内容的结构和类。

比如通过使用不完整类型可以在头文件中隐藏结构体定义的具体细节,如:

1
2
3
4
5
6
// ring_buffer.h
typedef struct _ring_buffer_type ringbuf_t;

int ring_buffer_create(ringbuf_t **rcb, size_t size);

int ring_buffer_write(ringbuf_t * rcb, uint8_t *pdata, size_t len);
1
2
3
4
5
6
// ring_buffer.c
struct _ring_buffer_type
{
uint8_t* data;
size_t size;
};

我们在C++中经常使用的“向前声明”也是不完整类型的具体应用。

关于柔性数组所使用的不完整类型则是“未指定长度的数组”:

1
int str[];

C99 将柔性数组纳入了标准之中,根据 C99 规定,柔性数组需要满足如下条件:

  1. 柔性数组只应用在结构体中。
  2. 结构体中不能只有柔性数组一个成员,而且柔性数组必须是结构体的最后一个成员。
  3. 由于柔性数组没有指定大小,因此不占用存储空间。

如下面结构体中的 data 数组就是柔性数组:

1
2
3
4
struct Package {
int len;
char* data[];
}

由于 data 数组的长度为0,因此不占用存储空间:

1
sizeof(Package) == sizeof(int)

包含柔性数组成员的结构体需要使用 malloc() 函数进行内存的动态分配,并且分配的内存通常大于结构体的大小,多余的空间自动分配给了柔性数组,这样就可以用柔性数组来存储额外的数据了。

1
2
3
4
5
6
7
8
9
10
11
12
const char data[20] = "hello";
const int dataLen = strlen(data);

Package* pck = (Package*)malloc(sizeof(Package) + dataLen);
if (pck) {
pck->len = dataLen;
memcpy(pck->data, data, dataLen);

// ...

free(pck);
}