Гибкий массив гибких массивов в C - PullRequest
0 голосов
/ 21 февраля 2019

Можно ли использовать вложенные гибкие массивы (гибкий массив гибких массивов) в C?

Я попробовал следующий код для тестирования гибких массивов:

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

typedef struct {
    int x;
    int y;
} node;

typedef struct {
    int len;
    node elem[];
} cell;

int cell_size = 3;

int main(void) {
    cell *set = malloc(sizeof *set + cell_size * sizeof set->elem[0]);
    set->len = cell_size;

    for (int j = 0; j < cell_size; j++) {
        set->elem[j].x = j;
        set->elem[j].y = j * 10;
    }

    printf("set size: %d\n", set->len);
    for (int j = 0; j < cell_size; j++) {
        printf("x: %d, ", set->elem[j].x);
        printf("y: %d\n", set->elem[j].y);
    }

    return 0;
}

Вывод:

set size: 3
x: 0, y: 0
x: 1, y: 10
x: 2, y: 20

Здесь все нормально, как и ожидалось.Выделенное пространство для set составляет 28 байт.

Но когда я попытался изменить этот код, поместив гибкий массив в другой массив:

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


typedef struct {
    int x;
    int y;
} node;


typedef struct {
    int len;
    node elem[];
} cell;


typedef struct {
    int len;
    cell group[];
} obj;


int cell_size = 3;
int obj_size = 4;

int main(void) {

    obj *set = malloc(
        sizeof *set + obj_size * sizeof (
        sizeof (cell) + cell_size * sizeof(node)
    ));
    set->len = obj_size;

    for (int i = 0; i < obj_size; i++) {
        set->group[i].len = cell_size;
        for (int j = 0; j < cell_size; j++) {
            set->group[i].elem[j].x = j;
            set->group[i].elem[j].y = j * 10 + i;
        }
    }

    printf("set size: %d\n", set->len);
    for (int i = 0; i < obj_size; i++) {
        printf("group size: %d\n", set->group[i].len);
        for (int j = 0; j < cell_size; j++) {
            printf("x: %d, ", set->group[i].elem[j].x);
            printf("y: %d\n", set->group[i].elem[j].y);
        }
    }

    return 0;
}

Allocatedпространство для set составляет 20 байт, и вывод неправильный:

set size: 4
group size: 3
x: 3, y: 3
x: 3, y: 0
x: 3, y: 1
group size: 3
x: 3, y: 3
x: 0, y: 3
x: 1, y: 13
group size: 3
x: 3, y: 0
x: 3, y: 1
x: 13, y: 2
group size: 3
x: 0, y: 3
x: 1, y: 13
x: 2, y: 23

Даже если вручную установить malloc на любое разумное значение, вывод все равно будет неправильным.Я думаю, это потому, что компилятор не знает размер group[] члена в верхней структуре (obj).Однако я не получил никаких ошибок или предупреждений компилятора (GCC 6.3.0).

И я не знаю, как установить этот размер и заставить компилятор правильно обрабатывать этот код.

Ответы [ 3 ]

0 голосов
/ 21 февраля 2019

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

Для полнойобсуждение применимых разделов стандарта C см .: Как ведет себя массив структур с гибкими элементами массива?

"So, there is no way to make nested flexible arrays working?"

Ответ: NO

Это может нарушить Стандарт C11 - 6.7.2.1 Спецификаторы структуры и объединения (p3)

"Something similar?"

Ответ: Да

Все, что вам нужно сделать, это сделать node elem[]; node *elem; и затем выделить cell_size * sizeof (node) байт на set->group[i].elem, например,

typedef struct {
    int len;
    node *elem;
} cell;
...
    obj *set = malloc (sizeof *set + obj_size * sizeof (cell));
    ...
    for (int i = 0; i < obj_size; i++) {
        set->group[i].len = cell_size;
        set->group[i].elem = malloc (cell_size * sizeof (node));

Это выделит cell_size * sizeof (node) для каждого set->group[i].elem, делающего то, что вы хотите, например,

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

typedef struct {
    int x;
    int y;
} node;

typedef struct {
    int len;
    node *elem;
} cell;


typedef struct {
    int len;
    cell group[];
} obj;


int cell_size = 3;
int obj_size = 4;

int main (void) {

    obj *set = malloc (sizeof *set + obj_size * sizeof (cell));
    set->len = obj_size;

    for (int i = 0; i < obj_size; i++) {
        set->group[i].len = cell_size;
        set->group[i].elem = malloc (cell_size * sizeof (node));
        for (int j = 0; j < cell_size; j++) {
            set->group[i].elem[j].x = j;
            set->group[i].elem[j].y = j * 10 + i;
        }
    }

    printf("set size: %d\n", set->len);
    for (int i = 0; i < obj_size; i++) {
        printf("group size: %d\n", set->group[i].len);
        for (int j = 0; j < cell_size; j++) {
            printf("x: %d, ", set->group[i].elem[j].x);
            printf("y: %d\n", set->group[i].elem[j].y);
        }
        free (set->group[i].elem);
    }

    free (set);

    return 0;
}

Пример использования / вывода

$ ./bin/fam_array2
set size: 4
group size: 3
x: 0, y: 0
x: 1, y: 10
x: 2, y: 20
group size: 3
x: 0, y: 1
x: 1, y: 11
x: 2, y: 21
group size: 3
x: 0, y: 2
x: 1, y: 12
x: 2, y: 22
group size: 3
x: 0, y: 3
x: 1, y: 13
x: 2, y: 23

Использование памяти / проверка ошибок

$ valgrind ./bin/fam_array2
==7686== Memcheck, a memory error detector
==7686== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7686== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==7686== Command: ./bin/fam_array2
==7686==
set size: 4
group size: 3
x: 0, y: 0
x: 1, y: 10
x: 2, y: 20
group size: 3
x: 0, y: 1
x: 1, y: 11
x: 2, y: 21
group size: 3
x: 0, y: 2
x: 1, y: 12
x: 2, y: 22
group size: 3
x: 0, y: 3
x: 1, y: 13
x: 2, y: 23
==7686==
==7686== HEAP SUMMARY:
==7686==     in use at exit: 0 bytes in 0 blocks
==7686==   total heap usage: 5 allocs, 5 frees, 168 bytes allocated
==7686==
==7686== All heap blocks were freed -- no leaks are possible
==7686==
==7686== For counts of detected and suppressed errors, rerun with: -v
==7686== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 21 февраля 2019

Вы не можете иметь гибкий массив объектов, размер которых неизвестен во время компиляции.И размер структуры, имеющей гибкий член массива, не является.Единственное, что известно, это его размер без гибкого элемента.

Но в вашем случае все массивы Flexible будут иметь одинаковый размер, даже если он известен только во время выполнения.,Таким образом, вы можете заменить гибкий массив внутренней структуры указателем, выделить достаточно места для структуры, включая ее гибкий массив, плюс достаточно места для всех узлов и назначить указатели в этом свободном пространстве.Код может быть:

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


typedef struct {
    int x;
    int y;
} node;


typedef struct {
    int len;
    node *elem;
} cell;


typedef struct {
    int len;
    cell group[];
} obj;


int cell_size = 3;
int obj_size = 4;

int main(void) {

    obj *set = malloc(
        sizeof *set + obj_size * (
            sizeof(cell) + cell_size * sizeof(node)
            ));
    set->len = obj_size;

    // nodes will exist in the allocated buffer after the cells
    node *node_start = (node *)(((char *) set) + sizeof *set + obj_size * sizeof(cell));

    for (int i = 0; i < obj_size; i++) {
        set->group[i].len = cell_size;
        // assign the elem pointers in the free space
        set->group[i].elem = node_start + i * cell_size;
        for (int j = 0; j < cell_size; j++) {
            set->group[i].elem[j].x = j;
            set->group[i].elem[j].y = j * 10 + i;
        }
    }

    printf("set size: %d\n", set->len);
    for (int i = 0; i < obj_size; i++) {
        printf("group size: %d\n", set->group[i].len);
        for (int j = 0; j < cell_size; j++) {
            printf("x: %d, ", set->group[i].elem[j].x);
            printf("y: %d\n", set->group[i].elem[j].y);
        }
    }
    free(set);
    return 0;
}
0 голосов
/ 21 февраля 2019

Правильно ... нет способа создать "вложенные гибкие массивы".

Все члены массива должны иметь одинаковый размер.Вы должны иметь

sizeof arr[i] == sizeof arr[j]

Для всех i, j в границах массива.

...