Член гибкого массива во вложенной структуре

Это допустимый код 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);
}

1 ответ

Решение

Это не действует в соответствии со стандартом. Я не уверен, насколько это надежно на практике.

C11 (ISO / IEC 9899: 2011), §6.7.2.1.3, говорит следующее (выделено мной):

Структура или объединение не должны содержать члена с неполным или функциональным типом (следовательно, структура не должна содержать своего экземпляра, но может содержать указатель на свой экземпляр), за исключением того, что последний член структуры с более чем один именованный член может иметь неполный тип массива; такая структура (и любое объединение, содержащее, возможно, рекурсивно член, являющийся такой структурой) не должно быть членом структуры или элементом массива.

Позже, §6.7.2.1.18 поясняет, что вышесказанное относится к гибким элементам массива (FAM):

В особом случае последний элемент структуры с более чем одним именованным элементом может иметь тип неполного массива; это называется членом гибкого массива.

После нескольких быстрых экспериментов 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);
}
Другие вопросы по тегам