原文地址:[Linux 系统编程] 0长数组 作者:crazyhadoop
零长数组
在标准 C 或者 C++ 中由于不支持 0 长度的数组,所以 int array[0];
这样的定义是非法的。不过有些编译器(如GCC)的扩展功能支持 0 长度的数组。
在 C 中,0 长度的数组的主要用途是用来作为结构体的最后一个成员,
然后用它来访问此结构体对象之后的一段内存(通常是动态分配的内存)。
由于其非标准性,在程序中尽量避免使用 0 长度的数组。作为替换,可以
使用 C99 标准中的不完整数组来替换 0 长度的数组定义。如:
typedef struct _X {
int a;
char array[]; //不完整数组,因此不可以计算sizeof(X),无法编译通过
} X;
在GNU的gcc-4.4.0的官方指南中的5.14节Arrays of Length Zero一节中,写道:
struct line {
int length;
char contents[0];
};
//...ommit code here
struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
这个用法主要用于变长Buffer,struct line的大小为4,
结构体中的contents[0]不占用任何空间,甚至是一个指针的空间都不占
(待会儿下面的示例代码会验证),contents在这儿只是表示一个常量指针,
这个特性是用编译器来实现的,即在使用thisline->contents的时候,这个指针
就是表示分配内存地址中的某块buffer,比如malloc (sizeof (struct line) + this_length)
返回的是0x8f00a40,thisline->contents指向的位置就是(0x8f00a40 + sizeof(struct line)),
而这儿sizeof(struct line)仅仅是一个int的四字节。
对于这个用法,我们定义的结构体指针可以指向任意长度的内存buffer,这个技巧在
变长buffer中使用起来相当方便。为什么不把最后的contents直接定义为一个指针呢?
这儿的差别是这样的,如果定义为一个指针,它需要占用4Bytes,并且在申请好内存后
必须人为赋地址才可以。如果使用这个用法,这个常量指针不占用空间,并且无需赋值。
但是,方便并不是绝对的,在释放分配的内存的时候,由于函数free会认为*thisline 只是
指向一个4字节的指针,即只会释放length的空间,而对于后面占据大头的buffer却视而不见,
这个就需要人为干预;而对于后面的声明指针的方式,则可以直接用Free(thisline->contents)
的方式释放掉分配的内存。
如果将零长数组array换成指针*array来使用的话,指针必须重新分配一段内存之后才能使用,
那么当想要用socket发送结构体指针的时候,并不会将指针array申请的内存发送过去,
因为是不连续的,所以接受socket发送来的数据后,会发现该数据并不是自己想要的。
struct t
{
int a;
int arr[0];
};
int main()
{
printf("%d\n",sizeof(struct t));//4, arr[0] 不占用任何空间
}