Next: Variable Length, Previous: Hex Floats, Up: C Extensions
Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure which is really a header for a variable-length object:
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length;
In ISO C90, you would have to give contents
a length of 1, which
means either you waste space or complicate the argument to malloc
.
In ISO C99, you would use a flexible array member, which is slightly different in syntax and semantics:
contents[]
without
the 0
.
sizeof
operator may not be applied. As a quirk of the original implementation
of zero-length arrays, sizeof
evaluates to zero.
struct
that is otherwise non-empty.
GCC versions before 3.0 allowed zero-length arrays to be statically initialized, as if they were flexible arrays. In addition to those cases that were useful, it also allowed initializations in situations that would corrupt later data. Non-empty initialization of zero-length arrays is now treated like any case where there are more initializer elements than the array holds, in that a suitable warning about "excess elements in array" is given, and the excess elements (all of them, in this case) are ignored.
Instead GCC allows static initialization of flexible array members.
This is equivalent to defining a new structure containing the original
structure followed by an array of sufficient size to contain the data.
I.e. in the following, f1
is constructed as if it were declared
like f2
.
struct f1 { int x; int y[]; } f1 = { 1, { 2, 3, 4 } }; struct f2 { struct f1 f1; int data[3]; } f2 = { { 1 }, { 2, 3, 4 } };
The convenience of this extension is that f1
has the desired
type, eliminating the need to consistently refer to f2.f1
.
This has symmetry with normal static arrays, in that an array of
unknown size is also written with []
.
Of course, this extension only makes sense if the extra data comes at the end of a top-level object, as otherwise we would be overwriting data at subsequent offsets. To avoid undue complication and confusion with initialization of deeply nested arrays, we simply disallow any non-empty initialization except when the structure is the top-level object. For example:
struct foo { int x; int y[]; }; struct bar { struct foo z; }; struct foo a = { 1, { 2, 3, 4 } }; // Valid. struct bar b = { { 1, { 2, 3, 4 } } }; // Invalid. struct bar c = { { 1, { } } }; // Valid. struct foo d[1] = { { 1 { 2, 3, 4 } } }; // Invalid.