Home > database >  Is there a standard way to statically initialize flexible array members in C?
Is there a standard way to statically initialize flexible array members in C?

Time:01-06

I need a statically created data structure in memory comprised of a table of string vectors, effectively:

typedef struct {
    char *argv[];
} Entry;

const Entry Table[] = {
    {"a"},
    {"a", "b", "c"}
};

But trying to compile this results in error: initialization of flexible array member in a nested context

Apparently this is possible in GCC, according GCC Manual: 6.18 Arrays of Length Zero. This may be possible following C 2018 6.7.2.1 18, although in regard to that I read elsewhere

There cannot be an array of structures that contain a flexible array member.

Is there a standard way to achieve this behavior? If not, is there a preferred way?

CodePudding user response:

You can't do it with a flexible array member.

Instead, you can use char **argv and initialize it using compound literals.

typedef struct {
    char **argv;
} Entry;

const Entry table[] = {
    { (char *[]) { "a", NULL } },
    { (char *[]) { "a", "b", "c", NULL } }
};

I added NULL to each of the arrays so the application can tell their lengths (the real argv has this as well).

CodePudding user response:

You do not store the count of elements of argv. How would you know that the first one has 1 element, and the second one has 3? You have to store that, for example, in another array member.

You can define a structure that has the proper exact same static memory layout as the array of structures with flexible array members that you want to have. Then you can then alias the structure with a pointer to Entry, and use that as an array of entries. Following example does that:

#include <stdlib.h>
#include <assert.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdio.h>

typedef struct {
    unsigned len;
    char *argv[];
} Entry;

// sane iterating
static_assert(alignof(Entry) == alignof(char *), "");

typedef struct {
    unsigned len;
    char *argv[1];
} Entry_1;
static_assert(alignof(Entry_1) == alignof(Entry), "");
static_assert(sizeof(Entry_1) == sizeof(Entry)   1 * sizeof(char *), "");

typedef struct {
    unsigned len;
    char *argv[3];
} Entry_3;
static_assert(alignof(Entry_3) == alignof(Entry), "");
static_assert(sizeof(Entry_3) == sizeof(Entry)   3 * sizeof(char *), "");

typedef struct {
    Entry_1 e1;
    Entry_3 e3;
} Entry_init_1_3;
static_assert(offsetof(Entry_init_1_3, e3) == sizeof(Entry_1), "");

const Entry_init_1_3 entry_init_1_3 = {
    { 1, { "a", } },
    { 3, { "b", "c", "d", } },
};

static const Entry *const table = (const void *)&entry_init_1_3;
//                                ^^^^^^^^^^^^^^^ I think I know what I am doing.
static const Entry *const table_end = (const Entry*)((const char*)table   sizeof(entry_init_1_3));

int main() {
    unsigned entry_idx = 0;
    for (const Entry *it = table;
            it != table_end;
            it = (const Entry *)(
                (const char *)it   sizeof(Entry)   it->len * sizeof(char *)
            ),
              entry_idx
    ) {
        for (unsigned x = 0; x < it->len;   x) {
            fprintf(stderr, "table[%d].argv[%d] = %s\n",
                entry_idx,
                x,
                it->argv[x]
            );
       }
    }
}

Code outputs:

table[0].argv[0] = a
table[1].argv[0] = b
table[1].argv[1] = c
table[1].argv[2] = d

Most notable inotify() Linux system call returns an array of flexible array members.

Is there a standard way to statically initialize flexible array members in C?

No.

If not, is there a preferred way?

Don't use flexible array members. Use pointers, as presented in the other answer.

  • Related