1

是否可以在 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

这里的一切都和预期的一样好。分配的空间为set28字节。

但是当我尝试像这样修改此代码以将灵活数组放入其他数组时:

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

分配的空间为set20字节,输出错误:

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

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

4

3 回答 3

2

正确...没有办法制作“嵌套的灵活数组”。

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

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

对于 all ij在数组边界内。

于 2019-02-20T21:25:44.370 回答
2

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

有关适用的 C 标准部分的完整讨论,请参阅:具有灵活数组成员的结构数组如何表现?

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

答案:没有

这将违反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)

如果您还有其他问题,请仔细查看并告诉我。

于 2019-02-20T21:45:57.293 回答
1

您不能拥有在编译时大小未知的对象的灵活数组。并且具有灵活数组成员的结构的大小不是。唯一已知的是它的尺寸没有柔性构件。

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

#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;
}
于 2019-02-20T21:56:49.620 回答