Search code examples
cstructflexible-array-member

flexible array member in a nested struct


Is it valid C code to have flexible array members inside nested structs? So is my sample code below guarenteed to work as expected with a sane compiler?

#include <stdio.h>
#include <stdlib.h>

struct d {
    char c;
    int ns[];
};

struct c {
    struct d d;
};

struct b {
    struct c c;
};

struct a {
    int n;
    struct b b;
};

int main() {
    const int n = 10;
    struct a *pa = malloc(sizeof(*pa) + n * sizeof(pa->b.c.d.ns[0]));
    pa->n = n;
    pa->b.c.d.c = 1;
    for (int i = 0; i < n; ++i) {
        pa->b.c.d.ns[i] = i;
    }
    for (int i = 0; i < n; ++i) {
        printf("%d\n", pa->b.c.d.ns[i] + pa->b.c.d.c);
    }
    free(pa);
}

Solution

  • It's not valid per the standard. I'm not sure how reliable it is in practice.

    C11 (ISO/IEC 9899:2011), §6.7.2.1.3 says the following (emphasis mine):

    A structure or union shall not contain a member with incomplete or function type (hence, a structure shall not contain an instance of itself, but may contain a pointer to an instance of itself), except that the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

    Later on, §6.7.2.1.18 clarifies that the above is referring to flexible array members (FAMs):

    As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.

    From some quick experimentation, GCC and Clang both add the trailing padding required to align the FAM properly even when the struct is nested, and only warn about structures with FAMs being members of other structures or arrays if -Wpedantic is passed, so take that as a sign that it'll probably work if you will :). It feels a bit hackish though.

    Note that it probably wouldn't make sense to have the FAM anywhere but at the end. If you do

    struct e {
        struct d d;
        int n;
    } e;
    

    , then e.d.ns[0] and e.n are likely to overlap in memory.