AndreKR
AndreKR

Reputation: 33727

What does it mean to have an incomplete array in a struct?

When I have an array in a struct, the meaning is totally clear to me: When the struct is defined, memory for the whole array is reserved and when I copy the struct, all the array contents is copied.

typedef struct {
    uint8_t type;
    struct {
        uint8_t length;
        uint8_t data[5];
    } m;
} s;

But when I use uint8_t data[], what does that mean? I guessed it might be the same as uint8_t *data but it isn't. When I try to assign to it like this:

s the_s;
uint8_t something[] = {1, 2, 3};
the_s.m.data = something;

the compiler gives me

cannot assign array type objects

Upvotes: 1

Views: 2148

Answers (3)

supercat
supercat

Reputation: 81347

An incomplete array type is a reference to the location where the first item of an array would have been allocated, but does not actually allocate space for that item. On some older C compilers, one could get a similar effect by declaring an array of size zero, although doing so has never been legal in any "official" version of the C standard. The primary use for such declarations is for structures which will be allocated using malloc, calloc, or other similar mechanism; the code which allocates space for the struct will allocate enough extra space to handle the desired number of array items.

Before incomplete array declarations became legal in C, a common workaround was to declare an array of size 1, and then subtract one from the number of elements to be appended to the struct. For example:

struct {
  int this,that,whatever;
  char name[1];
} MYSTRUCT;

void test(char *new_name, int new_name_length)
{
  MYSTRUCT *ms = malloc(sizeof(MYSTRUCT)+new_name_length-1);
  memcpy(ms->name, new_name, new_name_length);
}

This approach had a couple of icky aspects, however:

  1. There's no guarantee that the formula used wouldn't allocate unnecessary extra space. For example, if sizeof(int)==4, sizeof(mystruct) might get padded out to 20 bytes because of int-alignment requirements. If name_length is four, the total size allocated should be 24 bytes, but the malloc would ask for 27.
  2. Because the maximum legal subscript for an array is the smaller of the dimensioned size or the allocated space, accessing `ms->name[i]` for any value of `i` other than zero would technically be undefined behavior. It would thus be perfectly legal for a compiler optimize `ms->name[i]` as `ms->name[0]`. In practice, I wouldn't be surprised if every published C compiler in existence would refrain from performing that optimization when subscripting a single-element array at the end of an indirectly-accessed struct (because whether or not the common implementation of the 'struct hack' is legal, a lot of production code uses it.

If C compilers had simply not included code to reject zero-size arrays, and the standard had specified that the maximum subscript value for any array is (unsigned int)(size-1), the struct hack would have been much cleaner. Unfortunately, the standard wasn't written that way.

Upvotes: 3

unwind
unwind

Reputation: 400159

Your assumption that data[] is the same as *data is simply incorrect.

An unspecified array length can be used when dynamically allocating a structure that ends in an array, but it never means you get an assignable pointer.

Upvotes: 2

ouah
ouah

Reputation: 145919

An array of an incomplete type as the last member of a struct is a C99 feature called the flexible array member feature.

In this statement

the_s.m.data = something;

you are trying to assign an array but in C, arrays cannot be assigned.

Upvotes: 4

Related Questions