What Happens If I Define a 0-Size Array in C/C++

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.

What is the significance of a 0-length array in C? [duplicate]

Neither C nor C++ allow arrays of zero length, so your program is ill-formed.

(E.g. C++11, 8.3.4/1: "[the array size] shall be greater than zero".)

(As one point of rationale: An array of zero length would be tricky and confusing to reconcile with the requirement that each object have a unique address.)

As @sidyll points out, zero-length arrays are available as an extension in GCC.

Array of zero length

Yes this is a C-Hack.

To create an array of any length:

struct someData* mallocSomeData(int size)
{
struct someData* result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE));
if (result)
{ result->nData = size;
}
return result;
}

Now you have an object of someData with an array of a specified length.

Array with size 0 [duplicate]

In C++ it is illegal to declare an array of zero length. As such it is not normally considered a good practice as you are tying your code to a particular compiler extension. Many uses of dynamically sized arrays are better replaced with a container class such as std::vector.

ISO/IEC 14882:2003 8.3.4/1:

If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero.

However, you can dynamically allocate an array of zero length with new[].

ISO/IEC 14882:2003 5.3.4/6:

The expression in a direct-new-declarator shall have integral or enumeration type (3.9.1) with a non-negative value.

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.)

What happens if I initialize an array to size 0?

It will create an empty array object. This is still a perfectly valid object - and one which takes up a non-zero amount of space in memory. It will still know its own type, and the count - it just won't have any elements.

Empty arrays are often useful to use as immutable empty collections: you can reuse them ad infinitum; arrays are inherently mutable but only in terms of their elements... and here we have no elements to change! As arrays aren't resizable, an empty array is as immutable as an object can be in .NET.

Note that it's often useful to have an empty array instead of a null reference: methods or properties returning collections should almost always return an empty collection rather than a null reference, as it provides consistency and uniformity - rather than making every caller check for nullity. If you want to avoid allocating more than once, you can use:

public static class Arrays<T>
{
private static readonly T[] empty = new T[0];

public static readonly T[] Empty { get { return empty; } }
}

Then you can just use:

return Arrays<string>.Empty;

(or whatever) when you need to use a reference to an empty array of a particular type.



Related Topics



Leave a reply



Submit