这个零长度数组,基本上在应用开发的时候,很少接触到,今天却遇上了,原因是要使用一个同事开发的内核模块。编译的时候,出现了。
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手册。里面说得很详细:
手册是英文的,如果觉得蛋疼,可以看看一个是繁体的翻译,不过是在blogger上的博文,需要爬墙:
手册中提供了下面这样的一个结构体:
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[]的。