5

How do the two implementations differ:

struct queue {
    int a;
    int b;
    q_info *array;
};

and

struct queue {
    int a;
    int b;
    q_info array[0];
};
17
  • 4
    what clever thing do you want to do with q_info array[0]; ? Commented Oct 10, 2013 at 10:44
  • 3
    They differ in that T array[0] is undefined behavior. Commented Oct 10, 2013 at 10:47
  • 4
    Worth noting that [0] is non-standard (although supported by gcc and msvc). As i recall, C99 allowed usage of some_type some_array[]; for the same purpose. Before this, it was usually achieved by using [1], and manipulating with sizeof later. Commented Oct 10, 2013 at 10:49
  • @H2CO3 standard explicitly allows only greater-than-zero (6.7.5.2), so it isn't. If compiler supports this - it's an extension and still well-defined. Commented Oct 10, 2013 at 10:59
  • @keltar Indeed, 6.7.5.2:1 is in a “Constraints” section, so a standard-conforming compiler is required to issue a diagnostic, but what happens after that is undefined behavior (and, by the way, GCC does not issue a diagnostic). Commented Oct 10, 2013 at 11:56

5 Answers 5

11

The second struct does not use an array of zero elements - this is a pre-C99 trick for making flexible array members. The difference is that in the first snippet you need two mallocs - one for the struct, and one for the array, while in the second one you can do both in a single malloc:

size_t num_entries = 100;
struct queue *myQueue = malloc(sizeof(struct queue)+sizeof(q_info)*num_entries);

instead of

size_t num_entries = 100;
struct queue *myQueue = malloc(sizeof(struct queue));
myQueue->array = malloc(sizeof(q_info)*num_entries);

This lets you save on the number of deallocations, provides better locality of references, and also saves the space for one pointer.

Starting with C99 you can drop zero from the declaration of the array member:

struct queue {
    int a;
    int b;
    q_info array[];
};
Sign up to request clarification or add additional context in comments.

1 Comment

The zero-sized array member was (unfortunately IMHO) never allowed by standard C; prior to C99 if one wanted to avoid Undefined Behavior it was necessary to use an array of size MAX_QUEUE_SIZE and allocate sizeof(queue)-sizeof(elementType)*(MAX_QUEUE_SIZE-num_entries) bytes. A lot of code has been written which uses a size of one and allocates sizeof(queue)+sizeof(elementType)*(num_entries-1) bytes, but that will sometimes allocate more space than needed, and is Undefined Behavior (though fortunately compilers will generally cause it to behave as intended).
2

These are completely different things:

  • The first contains a pointer to an external array.
  • the second is an inline array that happens to have zero elements.

The reason people do this is it's more space-efficient. You simply over-allocate the memory the struct needs, and then pretend the array has more elements then declared - the compiler won't mind (usually).

It also means you have one less pointer to dereference through, and you can allocate and free the memory for the struct and the array all in one.

Obviously, this trick only works when the array is the last element in the struct.

Comments

1

In the first one there is actually a pointer allocated in struct queue, and sizeof(struct queue) == 2 * sizeof(int) + sizeof(q_info*)

In the second one there is no pointer or anything named array really exists in struct queue, and sizeof(struct queue) == 2 * sizeof(int). This is known as a trick to conveniently reference the data before or later using array. I've used this trick in implementing a memory allocator.

Comments

1

For the zero-sized array member, you can, when you allocate the structure, allocate more memory than the size of struct queue (for example malloc(sizeof(struct queue) + sizeof(q_info) * 10)) to have a contiguous area of memory you can use. Then the array will be part of that memory allocated, and for the example allocation you have ten q_info entries in it.

For the pointer, you have to make two allocations, one for the queue structure, and one for the array member. You of course have to call free twice, once for the array pointer and once for the structure.

However, once allocated both can be used the same.

Comments

1
q_info array[0];

decays to a pointer because of the automatic conversion. However, it is not assignable. You cannot say

array = <some address of an object>;

afterwards.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.