嵌套结构中的灵活数组成员

flexible array member in a nested struct

在嵌套结构中使用灵活的数组成员是否有效?那么我下面的示例代码是否可以保证在正常的编译器下按预期工作?

#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);
}

根据标准,它无效。我不确定它在实践中有多可靠。

C11 (ISO/IEC 9899:2011), §6.7.2.1.3 说了以下内容(强调我的):

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.

稍后,§6.7.2.1.18 阐明上述内容指的是灵活数组成员 (FAM):

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.

通过一些快速实验,GCC 和 Clang 都添加了正确对齐 FAM 所需的尾部填充,即使 struct 是嵌套的,并且仅在以下情况下警告具有 FAM 的结构是其他结构或数组的成员-Wpedantic 已通过,因此请将其视为一个标志,如果您愿意的话,它可能会起作用:)。不过感觉有点黑。

请注意,将 FAM 放在最后的任何地方可能没有意义。如果你这样做

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

,那么e.d.ns[0]e.n很可能在内存中重叠了

尝试这样的事情;

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

struct a {
    int n;
    int d_fam[];
};

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