C中灵活数组的灵活数组

Flexible array of flexible arrays in C

是否可以在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 分配的 space 是 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;
}

set 分配的 space 是 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 设置为任何合理的值,输出仍然不正确。我认为这是因为编译器不知道顶层结构 (obj) 中 group[] 成员的大小。但是我没有收到任何编译器错误或警告 (GCC 6.3.0)。

而且我不知道如何设置这个大小并使编译器正确处理这段代码。

正确...没有办法使"nested flexible arrays".

数组的所有 个成员必须具有相同的大小。你必须

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

对于数组边界内的所有 ij

具有灵活数组成员的结构不能用作另一个结构或数组中的成员。您只能拥有一个 FAM。否则,如果您尝试声明一个 FAM 数组,则结构数组的每个 FAM 成员都将指向相邻数组成员之间的内存位置——给您带来您所看到的不正确的覆盖结果。

有关适用的 C 标准部分的完整讨论,请参阅:

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

答案:否

那将违反 C11 Standard - 6.7.2.1 Structure and union specifiers(p3)

"Something similar?"

回答:是

您需要做的就是使 node elem[]; node *elem; 然后为每个 set->group[i].elem 分配 cell_size * sizeof (node) 个字节,例如

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

这将为每个 set->group[i].elem 分配 cell_size * sizeof (node) 做你想做的事,例如

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

例子Use/Output

$ ./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

内存Use/Error检查

$ 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)

检查一下,如果您还有其他问题,请告诉我。

您不能拥有在编译时大小未知的对象的灵活数组。而具有灵活数组成员的结构的大小不是。唯一知道的是它没有灵活成员的大小。

但在您的用例中,所有 flexible 数组都将具有相同的大小,即使它仅在 运行 时间已知。因此,您可以用指针替换内部结构的灵活数组,为包括其灵活数组的结构分配足够的 space 以及为所有节点分配足够的 space 并在该空闲 [=17] 中分配指针=].代码可以是:

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