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

C++CArrays

C++ Problem Overview


Just curious, what actually happens if I define a zero-length array int array[0]; in code? GCC doesn't complain at all.

Sample Program

#include <stdio.h>

int main() {
	int arr[0];
	return 0;
}

Clarification

I'm actually trying to figure out if zero-length arrays initialised this way, instead of being pointed at like the variable length in Darhazer's comments, are optimised out or not.

This is because I have to release some code out into the wild, so I'm trying to figure out if I have to handle cases where the SIZE is defined as 0, which happens in some code with a statically defined int array[SIZE];

I was actually surprised that GCC does not complain, which led to my question. From the answers I've received, I believe the lack of a warning is largely due to supporting old code which has not been updated with the new [] syntax.

Because I was mainly wondering about the error, I am tagging Lundin's answer as correct (Nawaz's was first, but it wasn't as complete) -- the others were pointing out its actual use for tail-padded structures, while relevant, isn't exactly what I was looking for.

C++ Solutions


Solution 1 - 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.

Solution 2 - C++

As per the standard, it is not allowed.

However it's been current practice in C compilers to treat those declarations as a flexible array member (FAM) declaration:

> C99 6.7.2.1, §16: 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.

The standard syntax of a FAM is:

struct Array {
  size_t size;
  int content[];
};

The idea is that you would then allocate it so:

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

You might also use it statically (gcc extension):

Array a = { 3, { 1, 2, 3 } };

This is also known as tail-padded structures (this term predates the publication of the C99 Standard) or struct hack (thanks to Joe Wreschnig for pointing it out).

However this syntax was standardized (and the effects guaranteed) only lately in C99. Before a constant size was necessary.

  • 1 was the portable way to go, though it was rather strange.
  • 0 was better at indicating intent, but not legal as far as the Standard was concerned and supported as an extension by some compilers (including gcc).

The tail padding practice, however, relies on the fact that storage is available (careful malloc) so is not suited to stack usage in general.

Solution 3 - C++

In Standard C and C++, zero-size array is not allowed..

If you're using GCC, compile it with -pedantic option. It will give warning, saying:

>zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

In case of C++, it gives similar warning.

Solution 4 - C++

It's totally illegal, and always has been, but a lot of compilers neglect to signal the error. I'm not sure why you want to do this. The one use I know of is to trigger a compile time error from a boolean:

char someCondition[ condition ];

If condition is a false, then I get a compile time error. Because compilers do allow this, however, I've taken to using:

char someCondition[ 2 * condition - 1 ];

This gives a size of either 1 or -1, and I've never found a compiler which would accept a size of -1.

Solution 5 - C++

Another use of zero-length arrays is for making variable-length object (pre-C99). Zero-length arrays are different from flexible arrays which have [] without 0.

Quoted from gcc doc:

> Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure that 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 C99, you would use a flexible array member, which is slightly different in syntax and semantics: > > * Flexible array members are written as contents[] without the 0. > * Flexible array members have incomplete type, and so the sizeof operator may not be applied.

A real-world example is zero-length arrays of struct kdbus_item in kdbus.h (a Linux kernel module).

Solution 6 - C++

I'll add that there is a whole page of the online documentation of gcc on this argument.

Some quotes:

> Zero-length arrays are allowed in GNU C.

> In ISO C90, you would have to give contents a length of 1

and

> 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

so you could

int arr[0] = { 1 };

and boom :-)

Solution 7 - C++

Zero-size array declarations within structs would be useful if they were allowed, and if the semantics were such that (1) they would force alignment but otherwise not allocate any space, and (2) indexing the array would be considered defined behavior in the case where the resulting pointer would be within the same block of memory as the struct. Such behavior was never permitted by any C standard, but some older compilers allowed it before it became standard for compilers to allow incomplete array declarations with empty brackets.

The struct hack, as commonly implemented using an array of size 1, is dodgy and I don't think there's any requirement that compilers refrain from breaking it. For example, I would expect that if a compiler sees int a[1], it would be within its rights to regard a[i] as a[0]. If someone tries to work around the alignment issues of the struct hack via something like

typedef struct {
uint32_t size;
uint8_t data[4];  // Use four, to avoid having padding throw off the size of the struct
}
a compiler might get clever and assume the array size really is four:
; As written
foo = myStruct->data[i];
; As interpreted (assuming little-endian hardware)
foo = (((uint32_t)myStruct->data) >> (i << 3)) & 0xFF;
Such an optimization might be reasonable, especially if myStruct->data could be loaded into a register in the same operation as myStruct->size. I know nothing in the standard that would forbid such optimization, though of course it would break any code which might expect to access stuff beyond the fourth element.

Solution 8 - C++

Definitely you can't have zero sized arrays by standard, but actually every most popular compiler gives you to do that. So I will try to explain why it can be bad

#include <cstdio>

int main() {
    struct A {
        A() {
            printf("A()\n");
        }
        ~A() {
            printf("~A()\n");
        }
        int empty[0];
    };
    A vals[3];
}

I am like a human would expect such output:

A()
A()
A()
~A()
~A()
~A()

Clang prints this:

A()
~A()

GCC prints this:

A()
A()
A()

It is totally strange, so it is a good reason not to use empty arrays in C++ if you can.

Also there is extension in GNU C, which gives you to create zero length array in C, but as I understand it right, there should be at least one member in structure prior, or you will get very strange examples as above if you use C++.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlex KoayView Question on Stackoverflow
Solution 1 - C++LundinView Answer on Stackoverflow
Solution 2 - C++Matthieu M.View Answer on Stackoverflow
Solution 3 - C++NawazView Answer on Stackoverflow
Solution 4 - C++James KanzeView Answer on Stackoverflow
Solution 5 - C++DukeView Answer on Stackoverflow
Solution 6 - C++xanatosView Answer on Stackoverflow
Solution 7 - C++supercatView Answer on Stackoverflow
Solution 8 - C++CPPCPPCPPCPPCPPCPPCPPCPPCPPCPPView Answer on Stackoverflow