Declaring flexible arrays as constants

64 Views Asked by At

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.

2

There are 2 best solutions below

0
Lundin On

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.)

5
Eric Postpischil On

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.