阅读这个跳表实现时,我遇到了以下代码片段:
typedef struct nodeStructure{
keyType key;
valueType value;
node forward[1]; /* variable sized array of forward pointers */
};
对我来说,
forward [1]
看起来表示一个元素的数组。而注释称其为“可变大小的数组”。我是否误解了什么,还是我正在阅读的源代码中有错误?
这被称为结构体技巧。它是C99引入的可变数组成员的旧形式。
过去曾经使用它来模拟结构体中最后一个成员的变量数组,但它在C中不是一个严格符合的构造。
这是C语言中的一种编程范式,有时会用到。在分配结构时,你需要分配sizeof(struct nodeStructure + numNodes * sizeof(node))。
这样可以让你为结构体拥有多个前向节点,即使它只被声明为一个节点。虽然有点丑陋,但它确实可行。
通常,在这种情况下,还会有一个名为“count”或类似名称的字段,以便你知道节点后面有多少额外条目。
这是老版本的C编译器(C99之前)常用的技巧:当forward
是struct
的最后一个元素时,编译器允许您解除引用超出声明长度的元素;然后,您可以像这样为额外的node
元素分配足够的内存: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
,但这样你就需要将其与nodeStructure
分别进行malloc
和free
,从而不必要地使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);
编辑(回应Adam Rosenfield的评论):C99允许您定义没有大小的数组,例如:node forward[];
这被称为灵活的数组成员,它在C99标准的6.7.2.1.16节中定义。
forward
被定义为一个1元素数组,所以forward[10]
在数组之外,因此应该在技术上触发UB。 - cHaoforward[10]
在过去和现在都是未定义的行为,但九十年代曾经是如此简单的时代。 - Pascal Cuoqnode forward[];
- Adam Rosenfield forward
的惯用和符合标准的定义将是 node forward [];
。具有这样一个成员的类型,例如 struct nodeStructure
,则称为不完整类型。您可以定义指向它的指针,但不能定义此类型的变量或获取其大小,所有操作 node forward [0]
或 node forward [1]
都允许,尽管这些操作明显与程序员的意图不匹配。
numNodes
的值根本不会被访问(因为指向此节点的指针的保留方式:该节点仅从具有相同数组的跳表级别中访问)。 - ovgolovin