Array of Zero Length

What is the purpose of a zero length array in a struct?

It's the pre-C99 version of a flexible array member, offered by GCC as an extension.

The C99 way is to define the flexible array member with empty brackets,

__u8  supervisor_stack[];

It's used to store data whose amount is not constant contiguous to the struct. Memory is allocated in the form

struct foo *ptr = malloc(sizeof *ptr + whatever_is_needed);

In paragraph 18 of 6.7.2.1, the standard (draft N1570) describes them:

As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. In most situations,
the flexible array member is ignored. In particular, the size of the structure is as if the
flexible array member were omitted except that it may have more trailing padding than
the omission would imply. However, when a . (or ->) operator has a left operand that is
(a pointer to) a structure with a flexible array member and the right operand names that
member, it behaves as if that member were replaced with the longest array (with the same
element type) that would not make the structure larger than the object being accessed; the
offset of the array shall remain that of the flexible array member, even if this would differ
from that of the replacement array. If this array would have no elements, it behaves as if
it had one element but the behavior is undefined if any attempt is made to access that
element or to generate a pointer one past it.

What happens if I define a 0-size array in C/C++?

An array cannot have zero size.

ISO 9899:2011 6.7.6.2:

If the expression is a constant expression, it shall have a value greater than zero.

The above text is true both for a plain array (paragraph 1). For a VLA (variable length array), the behavior is undefined if the expression's value is less than or equal to zero (paragraph 5). This is normative text in the C standard. A compiler is not allowed to implement it differently.

gcc -std=c99 -pedantic gives a warning for the non-VLA case.

How to use zero length array in C

A zero-length array at the end of a struct, or anywhere else, is actually illegal (more precisely a constraint violation) in standard C. It's a gcc-specific extension.

It's one of several forms of the "struct hack". A slightly more portable way to do it is to define an array of length 1 rather than 0.

Dennis Ritchie, creator of the C language, has called it "unwarranted chumminess with the C implementation".

The 1999 revision of the ISO C Standard introduced a feature called the "flexible array member", a more robust way to do this. Most modern C compilers support this feature (I suspect Microsoft's compiler doesn't, though).

This is discussed at length in question 2.6 of the comp.lang.c FAQ.

As for how you access it, whichever form you use, you can treat it like you'd treat any array. The name of the member decays to a pointer in most contexts, allowing you to index into it. As long as you've allocated enough memory, you can do things like:

CommandHeader *ch;
ch = malloc(computed_size);
if (ch == NULL) { /* allocation failed, bail out */ }
ch.len = 42;
ch.payload[0] = 10;
ch.payload[1] = 20;
/* ... */

Obviously this is only a rough outline.

Note that sizeof, when applied to the type CommandHeader or an object of that type, will give you a result that does not include the flexible array member.

Note also that identifiers starting with underscores are reserved to the implementation. You should never define such identifiers in your own code. There's no need to use distinct identifiers for the typedef name and the struct tag:

typedef struct CommandHeader
{
UINT16 len;
UINT8 payload[0];
} CommandHeader;

I'd also suggest using the standard types uint16_t and uint8_t, defined in <stdint.h> (assuming your compiler supports it; it's also new in C99).

(Actually the rules for identifiers starting with underscores are slightly more complex. Quoting N1570, the latest draft of the standard, section 7.1.3:

  • All identifiers that begin with an underscore and either an uppercase letter or another
    underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers
    with file scope in both the ordinary and tag name spaces.

And there are several more classes of reserved identifiers.

But rather than working out which identifiers are safe to use at file scope and which are safe to use in other scopes, it's much easier just to avoid defining any identifiers that start with an underscore.)

Why does Java allow arrays of size 0?

It signifies that it is empty. I.e. you can loop over it as if it had items and have no result occur:

for(int k = 0; k < strings.length; k++){
// something
}

Thereby avoiding the need to check. If the array in question were null, an exception would occur, but in this case it just does nothing, which may be appropriate.

sizeof() a struct with a zero length array member

An array size of 0 is not legal. Section 6.7.6.2p1 of the C standard regarding Array Declarators states:

In addition to optional type qualifiers and the keyword static , the [
and ] may delimit an expression or
* . If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression
is a constant expression, it shall have a value greater than zero.

The element type shall not be an incomplete or function type. The
optional type qualifiers and the keyword static shall appear
only in a declaration of a function parameter with an array
type, and then only in the outermost array type derivation.

So because this violates a constraint, this definition invokes undefined behavior.

That being said, some compilers allow a zero length array as the last member of a struct as an extension. GCC does this. In this case, it works the same as a flexible array member.

The standard compliant way to do this is to leave the size blank:

struct foo {
char a;
char b;
char c;
char d[];
};

In both cases, the flexible array member is not included in the size of the structure, which is why you get 3 for the size instead of 4 (although the presence of padding in a struct is up to the implementation). This also means that such a struct cannot be a member of an array (at least not without some fishy manual pointer manipulation).

The way you would use such a struct would be to dynamically allocate space for it plus a number of elements of the last member. For example:

struct foo *my_foo = malloc(sizeof(*my_foo) + (sizeof(char) * number_of_elements));

zero length arrays

If you have a function that takes an array, and you want to give it an array with nothing in it, you pass an zero-length array.

If you read an array from an external source, and it happens to not have any items, you'll get an zero-length array.



Related Topics



Leave a reply



Submit