4

在嵌套结构中具有灵活的数组成员是有效的 C 代码吗?那么下面的示例代码是否可以保证与理智的编译器一起按预期工作?

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

2 回答 2

8

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

C11 (ISO/IEC 9899:2011), §6.7.2.1.3 说如下(强调我的):

结构或联合不应包含不完整或函数类型的成员(因此,结构不应包含自身的实例,但可能包含指向自身实例的指针),除非结构的最后一个成员具有超过一个命名成员可能有不完整的数组类型;这种结构(以及任何可能递归地包含这种结构的成员的联合)不应是结构的成员或数组的元素。

稍后,第 6.7.2.1.18 节澄清了上面提到的灵活数组成员(FAM):

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为灵活数组成员

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

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

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

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

于 2015-03-30T20:22:03.857 回答
0

尝试这样的事情;

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);
}
于 2019-01-22T20:52:50.217 回答