2

I'm trying to declare some constant data structures in my code, ideally without having to declare subelements (this is a follow-on from a previous question about multiple lists of strings: different length arrays) ... as per my previous question I can get it to work by declaring bits individually, but can't declare it all in one go.

I should add, I need a pointer to the data structure (the pointer will be in a different linker section so I can discover them automatically)...

This works...

struct cpl {
    int x;
    char *a;
    char *b;
    char *list[];
};

const struct cpl one = { 1, "thisa", "thisb", { "one", "two", NULL }};
const struct cpl *xx = &one;

But this generates an error about non-static initialisation of a flexible array member..

const struct cpl *xx = &(const struct cpl){ 2, "thisa", "thisb", { "new", "item", "here", NULL } };

I suspect the answer (assuming it is possible) is similar to before, by adding some kind of casting to it, but I've tried everything I can think of.

In this case I probably can get by with the first version, but I'd rather not if I can avoid it.

Any help appreciated.

4
  • 1
    Please post a complete error message.
    – user694733
    Commented Feb 20 at 10:31
  • To be honest, I am a bit surprised that even the first one works. Unfortunately C17 draft I have says very little about flexible array member initialization, so I don't know if it is or is not supposed to work.
    – user694733
    Commented Feb 20 at 10:34
  • Edit the question to provide a minimal reproducible example. The code in the question is not a minimal reproducible example for two reasons: (a) It does not contain #include <stdlib.h> or something else to define NULL. (b) When NULL is defined, and the structure definition is included with the second piece of code, GCC 13.2 compiles each piece of code without complaint. Possibly you are using the code inside a function, which changes the semantics. Commented Feb 20 at 13:02
  • Also state the compiler you use and its version. GCC has an extension for initializing flexible array members, and that is likely what you are using since the first piece of code compiles for you. The version matters because, if you are using GCC 13.1 or later, you can put static in the compound literal. Commented Feb 20 at 13:03

2 Answers 2

1

Flexible array members were probably intended to mainly be used for dynamic memory allocation, but there's nothing saying that they can't be used in other contexts. However, they cannot be initialized and that is the problem here. It may be possible to initialize them through some gcc extension.

Which in turn means you can't declare a const struct with internal linkage and use a flexible array member at the same time, because there is then no standard way to assign a value to it. If we could live with not having it const and internal linkage both at once and instead only access it through a const qualified pointer, there are some possible dirty work-arounds... what follows next in this answer isn't really recommended practice.

If we replace the struct with a union allocating enough memory, then put an anonymous struct inside that union, then it is probably well-defined (or at least implementation-defined behavior) to use it.

typedef union {
  struct
  {
    int x;
    char *a;
    char *b;
    char *list[];
  };
  unsigned char lots_of_memory[100];
} cpl;

Initialize everything but the flexiable array member (and by all means keep internal linkage if we fancy):

static cpl one = { .x=1, .a="thisa", .b="thisb" };

Init the flexible array member in run-time:

memcpy(one.list, (char*[]){ "one", "two", NULL }, sizeof(char*[3]));

Only access this through a const qualified pointer, as replacement for the lost const-ness:

const cpl *xx = &one;

Full program:

#include <string.h>
#include <stdio.h>

typedef union {
  struct
  {
    int x;
    char *a;
    char *b;
    char *list[];
  };
  unsigned char lots_of_memory[100];
} cpl;

int main (void)
{
  static cpl one = { .x=1, .a="thisa", .b="thisb" };
  memcpy(one.list, (char*[]){ "one", "two", NULL }, sizeof(char*[3]));
  const cpl *xx = &one;

  for(size_t i=0; xx->list[i]!=NULL; i++)
  {
    puts(xx->list[i]);
  }
}

Quite ugly but I can't really come up with any reasons why this code would not be well-defined. (If someone does, please leave a comment.)

0
1

In drafts of the upcoming C standard and in GCC 13.1 and later, you can use static in the compound literal.

Failing that, a way to declare the structure you want is to overlay the structure in a union with another structure that has a regular array. This is almost fully defined by the C standard:

#include <stdlib.h>

//  Define a macro that lays out the structure members.
#define cplTemplate(n)  \
    int x; char *a; char *b; char *list[n]

//  Declare struct cpl to be the structure with a flexible array member.
struct cpl { cplTemplate(); };

//  Declare a function to provide a dummy use of the pointer.
void bar(const struct cpl *);

void foo(void)
{
    /*  There are several ideas here:

            We create a const union compound literal.

            The first member of the union, a, is a structure with a normal
            array member.  The second member of the union, b, is a struct cpl,
            with a flexible array member.

            We initialize the first member of the union.

            Then we take the address ("&" before the compound literal) of the
            b member (".b" after the compound literal).
    */
    const struct cpl *xx = & (const union {
        struct { cplTemplate(3); } a;
        struct cpl                 b;
        }) { .a = { 1, "thisa", "thisb", { "one", "two", NULL } } } .b;

    /*  Pass xx to bar to avoid the compiler complaining about an unused
        variable.
    */
    bar(xx);
}

The part that is not defined is that a compiler might lay out the two structures differently. However, normal general-purpose compilers will lay them out identically, except for the trailing bytes. Notably, their corresponding members will have the same offsets, so reinterpreting the structures through the union will yield the desired behavior.

5
  • This isn't actually a flexible array member though? It's using hard-coded struct size.
    – Lundin
    Commented Feb 20 at 16:01
  • @Lundin: struct cpl is a structure with a flexible array member. Look at its definition; there is no size. The a member of the union is a structure with a regular array. The b member is a struct cpl. Commented Feb 20 at 16:12
  • In order to get rid of possible layout difference problem and use of preprocessor, couldn't you use struct instead of union: struct { struct cpl a; char * b[3]; }? You would have to take address of .a and change the initializer slightly to assign items to b, but it should be fully defined?
    – user694733
    Commented Feb 21 at 8:28
  • 1
    @user694733: One issue with that is that a structure does not necessarily end where its flexible array member begins. Consider struct { struct { double d; char *f[]; } a; char *b[3] }. When double has an alignment requirement of eight bytes and char * is four bytes, the normal layout is that f will start at offset 8 and end at 12 but the size of the structure will be 16 bytes, because the compiler has to add four bytes of padding for alignment. So b[0] will not be the same memory as f[0]. Given the types in your particular structure, you could reason this will not happen in normal… Commented Feb 21 at 12:53
  • 1
    … C implementations due to their sizes and alignment requirements, but then you are still relying on implementation-dependent layout, not the C standard. Another problem is that the compiler may be entitled to treat the members of the structure as separate objects (I would have to look into aliasing rules and other rules more carefully), so optimization could cause the compiler to ignore b’s initialization when accessing a. With a union, reinterpretation of the bytes is guaranteed. Commented Feb 21 at 12:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.