Search code examples
arrayscg++flexible-array-member

Error: non-static initialization of a flexible array member in Some Cases but Not Others


I'm using the latest version of g++ to compile some test code involving an array of struct pointers where the structs contain an array. Compilation fails when I try to initialize the array of struct pointers in one line, but if I assign the structs to variables and then take their addresses when creating the array, g++ doesn't complain. I want to understand why. This is my first post, so I apologize if there is missing information.

I searched for similar questions and didn't find anything this specific.

#include <stdint.h>

typedef struct {
    uint16_t num_elements;
    uint8_t elements[];
} HasArray;

HasArray x = {.num_elements = 2, .elements = {51, 17}};
HasArray y = {.num_elements = 4, .elements = {1, 42, 88, 73}};

const HasArray* collection[] = {&x, &y};

// main() somewhere

The above code works, which I assume is because g++ can allocate memory for x and y at compile time (or something similar). If I try to initialize collection like so:

// HasArray cast is necessary so I can take the address, doing `&` on just the `{}`
// didn't work
// Also I would use a macro for these declarations, I know it's clunky
const HasArray* collection[] = {
    &(HasArray){.num_elements = 2, .elements = {51, 17}},
    &(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
};

g++ throws

test.c:16:56: error: non-static initialization of a flexible array member
   16 |     &(HasArray){.num_elements = 2, .elements = {51, 17}},
      |                                                        ^
test.c:17:63: error: non-static initialization of a flexible array member
   17 |     &(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
      |                                                               ^

What makes these situations different? In my brain the second syntax will still allocate memory at compile time, but it seems that's not the case. I understand I could allocate memory at runtime time with malloc(), but the first situation doesn't require it and I know the size of the structs at compile time, so how can I indicate the size to the compiler without creating separate variables for each struct?

Edit: I did in fact mistag the question, whoops. This is for c code, but I have to use g++ to compile it. @ChrisMM provided a pretty good answer in the comments. To get around the error, I will assign the arrays to variables and take their addresses as in the first situation.


Solution

  • First, initialing a flexible array member is a non-standard extension. GCC allows it in cases of objects with static storage duration, i.e. those declared at file scope or with the static keyword, as it can set aside the appropriate amount of space for it at compile time.

    Regarding why this generates an error:

    const HasArray* collection[] = {
        &(HasArray){.num_elements = 2, .elements = {51, 17}},
        &(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
    };
    

    There are ambiguities regarding the storage duration of compound literals prior to C23. In GCC in particular, they always have automatic storage duration. And because GCC only allows flexible array member initialization for objects with static storage duration, you get an error.

    The C23 standard clarifies this by stating that the storage duration of a compound literal is that of the enclosing scope. So if you were to compile with GCC 13 or later which has full C23 support, the above would compile cleanly.