零长度数组的妙用

这个零长度数组,基本上在应用开发的时候,很少接触到,今天却遇上了,原因是要使用一个同事开发的内核模块。编译的时候,出现了。

error: ISO C forbids zero-size array 'payload' [-Werror=pedantic]

其报错的结构体大概是这样的:

struct a{
    int i;
    char payload[0];
};

gcc编译的时候,没有给通过。原因是因为加了参数 -pedantic 从上面的 -Werror=pedantic 也可以看出是该错误的原因了。-pedantic 参数的话,简单的说,就是严格遵循ISO C 和 ISO C++的标准。也就是关闭了所有的GNU的扩展了。那么我们就可以说这个零长度的数组是GNU的扩展了。把编译的参数 -pedantic去掉后,确实可以通过。

然后就开始谷歌搜一通吧。看看这个零长度数组,到底有什么用途,因为我也没做内核的开发,从同事的言语中,得知,linux内核代码中,有大量的这种方式的代码。查到了GCC手册。里面说得很详细:

6.17 Arrays of Length Zero

手册是英文的,如果觉得蛋疼,可以看看一个是繁体的翻译,不过是在blogger上的博文,需要爬墙:

C Struct Hack – Structure with variable length array

手册中提供了下面这样的一个结构体:

struct line {
    int length;
    char contents[0];
 };
     
struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

说的是,打算contents在末尾刚好指向this_length存储的数据空间头,这样就方便我们对于this_length数据的读取操作了。其实就是灵活的运用的数组指向的是其后面的连续的内存空间,但是在C90之前,并不支持0长度的数组,所以C Struct Hack用的方法就是

struct line {
    int length;
    char contents[1];
};

但是这样显然会浪费空间,sizeof会返回8的长度(内存对齐)。所以GNU就对其进行了扩展。当使用contents[0]的时候,也就是0长度数组的时候,此次返回的长度就为4了。在C99之后,也加了类似的扩展,只不过用的是char payload[]这种形式(所以如果你在编译的时候确实需要用到-pedantic参数,那么你可以将char payload[0]类型改成char payload[],这样就可以编译通过了,当然你的编译器必须支持C99标准的,如果太古老的编译器,那可能不支持了)。所以结构体的末尾,就是指向了其后面的内存数据。因此我们可以很好的将该类型的结构体作为数据报文的头格式,并且最后一个成员变量,也就刚好是负载或内容了。

手册还提供了另外两个结构体来说明,更容易看懂意思:

struct f1 {
    int x; 
    int y[];
} f1 = { 1, { 2, 3, 4 } };
     
struct f2 {
    struct f1 f1;
    int data[3];
} f2 = { { 1 }, { 5, 6, 7 } };

我把f2里面的2,3,4改成了5,6,7以示区分。如果你把数据打出来。即如下的信息:

f1.x = 1

f1.y[0] = 2

f1.y[1] = 3

f1.y[2] = 4

也就是f1.y指向的是{2,3,4}这块内存中的数据。所以我们就可以轻易的得到,f2.f1.y指向的数据也就是正好f2.data的内容了。打印出来的数据:

f2.f1.x = 1

f2.f1.y[0] = 5

f2.f1.y[1] = 6

f2.f1.y[2] = 7

如果你不是很确认其是否占用空间。你可以用sizeof来计算一下。就可以知道sizeof(struct f1)=4,也就是int y[]其实是不占用空间的。但是这个0长度的数组,必须放在结构体的末尾。如果你没有把它放在末尾的话。编译的时候,会有如下的错误:

main.c:37:9: error: flexible array member not at end of struct
     int y[];
         ^

到这边,你可能会有疑问,如果将struct f1中的int y[]替换成int *y,又会是如何?这就涉及到数组和指针的问题了。有时候吧,这两个是一样的,有时候又有区别。

首先要说明的是,支持0长度数组的扩展,重点在数组,也就是不能用int *y指针来替换。sizeof的长度就不一样了。把struct f1改成这样:

struct f3 {
    int x;
    int *y;
};

在64位下,sizeof(struct f1)=4,而sizeof(struct f3)=16。因为int *y是指针,指针在64位下,是64位的,如果在32位环境的话,sizeof(struct f3)则是8了,sizeof(struct f1)不变。所以int *y是不能替代int y[]的。

 

转载请注明: 转载自elkPi.com

本文链接地址: 零长度数组的妙用

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Scroll to top