C 奇怪的可变大小数组声明

C 奇怪的可变大小数组声明,c,arrays,C,Arrays,在阅读过程中,我遇到了以下代码片段: typedef struct nodeStructure{ keyType key; valueType value; node forward[1]; /* variable sized array of forward pointers */ }; 在我看来,forward[1]表示一个元素数组。注释称之为可变大小的数组 我是否误解了某些东西,或者这只是我正在阅读的源代码中的一个错误?它被称为结构黑客。它是C99中引入的

在阅读过程中,我遇到了以下代码片段:

typedef struct nodeStructure{
    keyType key;
    valueType value;
    node forward[1]; /* variable sized array of forward pointers */
    };
在我看来,
forward[1]
表示一个元素数组。注释称之为可变大小的数组

我是否误解了某些东西,或者这只是我正在阅读的源代码中的一个错误?

它被称为结构黑客。它是C99中引入的灵活数组成员的旧形式


这在过去曾被用于模拟结构最后一个成员中的变量数组,但在C中它不是严格一致的构造。

这是旧C编译器(C99之前)的常见技巧:编译器允许您在
结构
的最后一个元素
时,取消引用超过
正向
声明长度末尾的元素;然后可以
malloc
为额外的
节点
元素提供足够的内存,如下所示:

nodeStructure *ptr = malloc(sizeof(nodeStructure)+4*sizeof(node));
for (int i = 0 ; i != 5 ; i++) { // The fifth element is part of the struct
    ptr->forward[i] = ...
}
free(ptr);
该技巧允许您在结构中嵌入可变大小的数组,而无需单独的动态分配。另一种解决方案是声明
node*forward
,但随后需要
malloc
free
将其与
nodeStructure
分开,不必要地将
malloc
的数量增加一倍,并可能增加内存碎片:

以下是没有黑客攻击时上述片段的外观:

typedef struct nodeStructure{
    keyType key;
    valueType value;
    node *forward;
};

nodeStructure *ptr = malloc(sizeof(nodeStructure));
ptr->forward = malloc(5*sizeof(node));
for (int i = 0 ; i != 5 ; i++) {
    ptr->forward[i] = ...
}
free(ptr->forward);
free(ptr);

EDIT(回应Adam Rosenfield的评论):C99允许您定义无大小的数组,如下所示:
节点转发[]这称为灵活数组成员,它在C99标准的第6.7.2.1.16节中定义。

这是C中的程序范例,您有时会看到。在分配结构时,您将分配sizeof(struct nodeStructure+numNodes*sizeof(node))

这允许您为结构拥有多个转发节点,即使它只声明有一个转发节点。这是一个有点丑陋的黑客,但它的工作


通常,当您执行此操作时,还会有一个名为“count”或类似的字段,以便您知道节点后有多少额外条目。

数据结构实现很可能是针对C90标准编写的,C90标准没有灵活的数组成员(添加在C99中)。当时,在结构的末尾使用1甚至0大小的(*)数组以允许访问动态可变数量的元素是很常见的

注释不应解释为表示C99样式的可变长度数组;此外,在C99中,成员
forward
的惯用和标准一致的定义是
node forward[]。具有此类成员的类型(如
struct nodeStructure
)称为不完整类型。您可以定义指向它的指针,但不能定义这种类型的变量或确定其大小,
node forward[0]
node forward[1]
允许的所有操作,尽管这些操作可能与程序员的意图不符


(*)标准禁止使用0大小的数组,但GCC将其作为扩展来使用。

@这种晦涩的语法有什么用?它有什么好处吗?@ovgolovin请看编辑,我添加了一个关于这个技巧好处的信息(更少的分配)。请您写一下如何通过
节点*转发
(如果不太长)进行分配。我认为这不仅对我有帮助,而且对其他学习C的新手也有帮助。@ovgolovin当然,没问题-请看一看。C标准允许你这么做?我一直认为这是一种黑客行为,因为编译器对结构的安排非常相似。由于
forward
被定义为一个1元素数组,
forward[10]
位于数组之外,因此从技术上讲应该触发UB。我们花了几分钟试图查找计数。但是没有。最终我意识到,关于ve
numNodes
的值根本不会被访问(因为指向该节点的指针是如何保留的:该节点只能从数组中的跳过列表级别访问)。