结构指针的动态数组——我真的需要为 ea 元素显式分配 space 吗?

Dynamic array of pointer to structs - do I really need to explicitly allocate space for ea element?

我有一个这样定义的结构:

typedef struct
{
    int num;
    char letter;
}* Foo;

像这样的数组:

Foo* items = malloc(sizeof(Foo) * 4);

根据我的理解和这个问题的(已接受的)答案 Dynamic array of pointers to structs,我希望上面的行只为 4 个 Foo 项目保留内存,但不会初始化它 - 即,如果我尝试访问 items[i]->num,应该会出现某种错误。 另外,为了将项目插入到这个数组中,我应该这样做:

items[0] = malloc(sizeof(*items[0]));

然而,我做了一点测试,似乎下面的代码打印 1a 就好了:

Foo* items = malloc(sizeof(Foo) * 2);
items[0]->num = 4;
items[0]->letter = 'a';
printf("items[0] = {num=%d, char=%c}\n", items[0]->num, items[0]->letter);

我很困惑。这是预期的行为吗?

发生这种情况是因为您为 Foo 数组和可能的一个元素预留了足够的 space 但这不是预期的行为,在这种情况下没有预期的行为,因为您所做的调用未定义的行为。

如果向结构中添加更多字段,这将失败,因为那样 2 * sizeof(void *) 就不够了。要测试它,请尝试像这样向结构添加 2 个指针1

typedef struct
{
    int num;
    char letter;
    void *pointers[2];
} *Foo;

如果你愿意,你可以做对,这是另一个原因 typedef 一个指针 ,这会起作用

typedef struct
{
    int num;
    char letter;
    void *pointers[2];
} Foo;

Foo *foo_pointer = malloc(N * sizeof(Foo));
/*                                 ^ this would be wrong if `Foo' were a pointer */
if (foo_pointer == NULL)
    please_abort_this_do_not_continue_because_malloc_has_failed();
foo_pointer[0].num = 1;
foo_pointer[0].letter = 'a';

1写这个真的很烦我,因为typedef指针从来都不是一个好主意

如果您想使用结构,您必须分配结构并将其指针保存到 items 的元素。否则,item 的元素是垃圾,访问它可能会导致错误。

由于删除了 malloc 并将 items[0]->numitems[0]->letter 的值直接传递给 printf.[=16= 的优化,第二个测试可能工作正常]

您的初始 malloc:

Foo* items = malloc(sizeof(Foo) * 4);

正在创建一个包含 4 个指针的数组,因为 Foo 是指针类型。所以你的第二个 malloc:

items[0] = malloc(sizeof(*items[0]));

有道理,因为您正在为该指针分配一个结构。

但是,您正在执行的分配会导致未定义的行为,因为您没有执行第二个 malloc,因此尚未将 space 分配给 items[0]。 C 不会阻止您写入不应该写入的内存位置。一旦你这样做,任何事情都可能发生。

这里有点令人困惑的一件事是您使用 typedef 来定义指针类型。这可能会导致很多混乱,因为通过查看它是指针的类型并不明显。在这种情况下,由于您定义 Foo 的方式,您可能不需要额外的指针间接层。

因此,如果您这样定义结构:

typedef struct
{
    int num;
    char letter;
} Foo;

那么这就可以安全地完成了:

Foo* items = malloc(sizeof(Foo) * 2);
items[0].num = 4;
items[0].letter = 'a';
printf("items[0] = {num=%d, char=%c}\n", items[0].num, items[0].letter);

现在 malloc 创建结构数组而不是结构指针数组,因此不再需要额外的 malloc 层。