用C编写一个简单的单链列表,我在Github上找到了这个存储库:https://github.com/clehner/ll.c,同时在寻找一些示例。
有以下功能(_list_next(void *)
):
struct list
{
struct list *next; // on 64-bit-systems, we have 8 bytes here, on 32-bit-systems 4 bytes.
void *value[]; // ISO C99 flexible array member, incomplete type, sizeof may not be applied and evaluates to zero.
};
void *_list_next(void *list)
{
return list ? ((struct list *)list)[-1].next : NULL; // <-- what is happening here?
}
您能解释一下它是如何工作的吗?
似乎他在将空指针投射到列表指针,然后下标该指针。那是如何工作的以及在那里到底发生了什么?
我不了解[-1]
的目的。
答案 0 :(得分:1)
这是未定义的行为,恰好在作者尝试过的系统上起作用。
要了解发生了什么,请注意_ll_new
的返回值:
void * _ll_new(void *next, size_t size)
{
struct ll *ll = malloc(sizeof(struct ll) + size);
if (!ll)
return NULL;
ll->next = next;
return &ll->value;
}
作者为您提供value
的地址,而不是节点的地址。但是,_list_next
需要地址struct list
:否则将无法访问next
。因此,要成为next
成员,您需要通过向后退一个成员来查找其地址。
这是在list
处对[-1]
进行索引的想法-它获得与此特定地址next
关联的value
的地址。但是,此操作会将数组索引超出其有效范围,这是未定义的行为。
其他函数也可以这样做,但是它们使用指针算术而不是索引。例如,_ll_pop
使用
ll--;
达到相同的结果。
一种更好的方法是使用类似于container_of
macro的东西。