这个零长度数组,基本上在应用开发的时候,很少接触到,今天却遇上了,原因是要使用一个同事开发的内核模块。编译的时候,出现了。
1
|
error
:
ISO
C
forbids
zero
-
size
array
'payload'
[
-
Werror
=
pedantic
]
|
其报错的结构体大概是这样的:
1
2
3
4
|
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
手册中提供了下面这样的一个结构体:
1
2
3
4
5
6
7
|
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用的方法就是
1
2
3
4
|
struct
line
{
int
length
;
char
contents
[
1
]
;
}
;
|
但是这样显然会浪费空间,sizeof会返回8的长度(内存对齐)。所以GNU就对其进行了扩展。当使用contents[0]的时候,也就是0长度数组的时候,此次返回的长度就为4了。在C99之后,也加了类似的扩展,只不过用的是char payload[]这种形式(所以如果你在编译的时候确实需要用到-pedantic参数,那么你可以将char payload[0]类型改成char payload[],这样就可以编译通过了,当然你的编译器必须支持C99标准的,如果太古老的编译器,那可能不支持了)。所以结构体的末尾,就是指向了其后面的内存数据。因此我们可以很好的将该类型的结构体作为数据报文的头格式,并且最后一个成员变量,也就刚好是负载或内容了。
手册还提供了另外两个结构体来说明,更容易看懂意思:
1
2
3
4
5
6
7
8
9
|
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长度的数组,必须放在结构体的末尾。如果你没有把它放在末尾的话。编译的时候,会有如下的错误:
1
2
3
|
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改成这样:
1
2
3
4
|
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[]的。