12

Recently I came across a structure definition,

struct arr {
    int cnt;
    struct {
        int  size;
        int *name;
    } list[0];
};

and now I don't know the reason for list[0] being declared. What I am interested in is why is this used. Does it have any advantage? If yes, what is it?

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
hue
  • 1,759
  • 3
  • 27
  • 37
  • 2
    Zero-size objects are illegal in C. Either use `[1]` and waste a tiny bit of space (or calculate to make up for it), or use `[]` (but then your code requires a C99 compiler). – R.. GitHub STOP HELPING ICE Jan 14 '11 at 17:24
  • Here's the section from the gcc manual on Arrays of Length Zero: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html – Pierz Jun 29 '16 at 17:01

3 Answers3

18

The use is for dynamic-length arrays. You can allocate the memory using malloc(), and have the array reside at the end of the structure:

struct arr *my_arr = malloc(sizeof *my_arr + 17 * sizeof *my_arr->list);
my_arr->cnt = 17;
my_arr->list[0].size = 0;
my_arr->list[1].name = "foo";

Actually being able to use 0 for the length is (as pointed out in a comment) a GCC extension. In C99, you can leave out the size literal altogether for the same effect.

Before these things were implemented, you often saw this done with a length of 1, but that complicates the allocation a bit since you must compensate when computing the memory needed.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 5
    To add to that, being a 0-length array, `list` doesn't contribute to the size of the structure. `sizeof(struct arr) == 4` – Jeff Mercado Jan 14 '11 at 11:56
  • 4
    The ability to use a length `0` is a GCC feature. The C99 equivalent is to leave the length out completely, as in `struct { ... } list[];` – Bart van Ingen Schenau Jan 14 '11 at 12:05
  • struct list { int size; int *name; }; struct arr { int cnt; struct list *list_ptr; }; is this an equivalent? – hue Jan 14 '11 at 12:34
  • @hue: No. In this case you have a *pointer* in your struct. In the original question you have an *array*. These are two completely different things. – AnT stands with Russia Jan 14 '11 at 13:12
  • 3
    @hue: no - that would have your list array stored and malloc'ed elsewhere in memory. This has the list immediately following the structure. – ijw Jan 14 '11 at 13:12
  • yeah I get it now..Thanks guys.. But what is the advantage of defining a structure like the one in the question? – hue Jan 14 '11 at 13:53
  • 4
    @hue: Er... You just got two answers that describe that advantage. It lets you allocate the memory for the struct and for variable-length the array at the end of the struct as *one continuous memory block*, with one call to `malloc`. If you used a pointer, you'd either have to allocate memory separately (two `malloc` calls, likely non-continuous) or use some other tricks (to achieve proper alignment etc.) – AnT stands with Russia Jan 14 '11 at 18:12
11

It is called "struct hack". You can search for it on SO or on the Net.

Note that formally it is always illegal to declare arrays of size 0 in C. The code you provided formally is not even compilable. Most C compilers will accept 0-sized array declaration as an extension though, specifically because it is often used in "lazy" version of "struct hack" (it can rely on sizeof to determine how much memory to allocate, since 0-sized array supposedly does not affect the total size of the struct).

An arguably better implementation of struct hack uses an array of size 1

struct arr {
    int cnt;
    struct {
        int  size;
        int *name;
    } list[1];
};

It is "better" because it is formally compilable at least. In order to allocate memory for a struct with N elements in the list, standard offsetof macro is used

arr *a = malloc(offsetof(arr, list) + N * sizeof a->list);

In C99 version of the language specification the "struct hack" is supported through size-less array declaration (with empty []), since 0-sized array declarations are illegal in C99 as well.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Is there anything in the standard that would forbid a compiler which sees a declaration `unsigned char foo[1];` from compiling all accesses to `foo[x]` as though they were `foo[0]`? While people answering "struct-hack" questions regard as academic the issue of whether the size-1-array version is "undefined behavior", the aforementioned optimization could be useful, especially in some embedded-systems contexts, if it's legal. Would it be (adding in the caveat that a compiler might want to explicitly disable it for the last element of an indirectly-accessed struct)? – supercat Jan 13 '12 at 23:54
  • @supercat if I understand your question, it would silently cause some strange behavior, that &(foo[999]) == &(foo[0]) for example. It's assumed that a[b] === (&a[0])+b – Steven R. Loomis Feb 27 '13 at 06:46
  • @StevenR.Loomis: The assumption you only make only holds if `b` is less than the number of elements in `a`. If a pointer yielded by an array within a structure may only be indexed within the *smaller* of either the array's *declared* size or the allocated space, then the scenario you describe would be Undefined Behavior. If I were in charge of standards, I would specify that accessing an array outside its specified range would be Undefined Behavior *except* in the specific case that a single-element array at the end of an struct may use a non-zero index if that struct is... – supercat Feb 27 '13 at 15:52
  • ...the last item in a section of memory received by `malloc`, `calloc`, etc. Such a rule would require that compilers continue to support old code using the "struct hack", but would permit most of the optimizations that struct array enforcement would permit (just just the single-element case I mentioned, but also many other scenarios involving aliasing, etc. For example, given `struct {int a[1], b;} foo; foo.b=3; x=foo.a[something()]; foo.b=0;` should the compiler be required to store the `3` into memory before accessing `foo.a[]`, or should it be allowed to omit the store? – supercat Feb 27 '13 at 15:57
  • @supercat but the runtime doesn't know how many elements are "in" a, my assumption was just a restatement of how I understand C to interpret array subscripts. I would actually say that the scenario is defined behavior (I'm utilizing it right now). Now the *value* of b[999] or b[-100] may be undefined, but the *behavior* I would hold is defined. I would not expect the compiler to detect range errors here. – Steven R. Loomis Feb 27 '13 at 17:19
  • @StevenR.Loomis: Declaring that a sequence of actions has "undefined behavior" does not mean compilers should consistently trap it. It would merely mean that in cases where compilers happen to notice that something cannot legitimately happen, they would be free to do anything they would consider advantageous based on the assumption that it won't. – supercat Feb 27 '13 at 18:56
0

Another advantage is if your structure describes on-disk/on-network data. If cnt is 0, the data size may only be the length of cnt.

I'm here just to confirm what I dreaded, that list[0] is not valid.

Steven R. Loomis
  • 4,228
  • 28
  • 39