弹性数组成员的非静态初始化?

时间:2018-12-29 13:46:30

标签: c

#include <stdio.h>
#include <limits.h>

typedef struct item {
    int low; int high; char label[16];
} Item;

typedef struct item_coll {
    size_t length; Item *items[];
} ItemColl;

char *find_first_in_range(ItemColl *ic, int rlow, int rhigh) {
    for (size_t i = 0; i < ic->length; i++)
        if (ic->items[i]->low >= rlow && ic->items[i]->high <= rhigh)
            return &ic->items[i]->label[0];
    return NULL;
}

int main() {
    struct item fruits[] = {
        {10, 20, "Apple"},
        {12, 14, "Pear"},
        {8, 12, "Banana"},
        {2, 4, "Grape"},
        {15, 35, "Watermelon"}
    };

    struct item_coll basket = {5, fruits};

    printf("%s", find_first_in_range(&basket, 21, 22));

    return 0;
}

这给了我app.c:28:32: error: non-static initialization of a flexible array member struct item_coll basket = {5, fruits};

并且错误指向fruits

这是什么意思?对我来说很好。

2 个答案:

答案 0 :(得分:2)

如果这是考试,并且您必须使用灵活数组成员,那么正如我在评论中所指出的那样,并且正如@rici在他的回答中所解释的那样,FAM的目的是为给定{{1 }}然后,您可以在一次分配中为结构本身分配存储,并为一定数量的FAM类型分配存储。这提供的优点是对结构进行单分配/无单分配,而不是单独分配,然后为一些所需的类型分配。

(在FAM之前,有一种称为 struct hack ,其中出于相同的目的使用了大小为type的数组)

1对于您如何处理和分配FAM至关重要。在您的情况下,您的FAM为type(在代码中键入item *items[];-item的指针数组),因此您为结构分配了指针,然后分配了Item个指针到X

要初始化item的每个成员,必须将有效的地址分配给类型items的结构(也可以单独分配,复制到新块,然后将该块的起始地址分配给item中的指针。在您的情况下,您有一个 struct items数组,称为item。要分配给fruits,必须将每个结构的地址分配给items中的每个元素(请记住,您存储的是指针,而不是结构的items -并且您必须确保item在使用fruits的期间内仍在范围内

将这些片段放在一起,您可以执行以下操作:

basket

注意,我只是使用了typedef并删除了结构标签本身-由您自己决定。此外,您应该验证#include <stdio.h> #include <stdlib.h> #include <limits.h> typedef struct { int low, high; char label[16]; } item; typedef struct { size_t length; item *items[]; } item_coll; char *find_first_in_range(item_coll *ic, int rlow, int rhigh) { for (size_t i = 0; i < ic->length; i++) if (ic->items[i]->low >= rlow && ic->items[i]->high <= rhigh) return ic->items[i]->label; return NULL; } int main() { item fruits[] = { {10, 20, "Apple"}, {12, 14, "Pear"}, { 8, 12, "Banana"}, { 2, 4, "Grape"}, {15, 35, "Watermelon"} }; size_t nfruits = sizeof fruits/sizeof *fruits; /* avoid magic-numbers */ /* allocate storage for basket + nfruits pointers */ item_coll *basket = malloc (sizeof *basket + nfruits * sizeof *basket->items); if (!basket) { /* validate allocation succeeded */ perror ("malloc-basket+5_item_coll"); return 1; } basket->length = nfruits; /* assign length */ for (size_t i = 0; i < nfruits; i++) /* assign addresses to structs */ basket->items[i] = &fruits[i]; char *label = find_first_in_range (basket, 12, 15); /* save return */ if (label) /* validate not NULL before printing */ printf ("%s\n", label); free (basket); /* don't forget to free the memory you allocate */ return 0; } 的返回值是否不是find_first_in_range在打印之前。)

使用/输出示例

还要注意,我将NULL的范围括起来

high/low

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,$ ./bin/fam_initialization Pear 是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

valgrind

始终确认已释放已分配的所有内存,并且没有内存错误。

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

答案 1 :(得分:1)

我强烈怀疑您打算使用此定义:

typedef struct item_coll {
    size_t length; Item *items;
} ItemColl;

即长度和项目数组,而不是指向项目的指针数组。

按照书面规定,指针数组没有任何指定的长度,这实际上导致struct不完整;编译器无法告诉hiw big它是什么,因此您实际上无法定义一个。为了方便起见,C11允许这种声明。未指定长度的数组(即“灵活数组成员”)必须位于struct的最后,并且struct本身不能在另一个struct中使用。

您可以将具有灵活数组成员的结构用作具体动态分配对象的基础,该对象的大小可以计算为sizeof (Struct) + n * sizeof (ArrayElement)。作为优化,这有时是有用的,并且是合法的;我要说的是,它的明显优势必须与它所造成的代码复杂性权衡。