思考TAILQ的tqe_prev不是指向前一个节点的目的

在sys / queue.h中定义了一个数据结构TAILQ。 它在Linux内核中非常stream行。 它的定义是这样的:

#define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } 

我对这个代码有点困惑:tqe_prev指向前一个节点的tqe_next有什么好处? 如果是我,我会有直接指向前一个节点的tqe_prev,类似于指向下一个节点的tqe_next。

我想到的一个原因是,当我们插入一个节点时,我们直接操作指针来更新,我们不需要先通过它自己的节点。 但是呢? 还有其他的好处吗

我想知道我们如何能够在队列后面旅行? 当我们有一个指向节点的指针时,因为它的tqe_prev没有指向前一个节点,所以我们没有办法通过队列直到头部。 或者这样的落后旅行是由TAILQdevise不支持的?

哦,有趣。 我不知道这个技术有其他用户(我自己想出来)。

这样做的原因是,可能没有“前一个节点”:第一个元素没有前导,但它有一个指向它的指针。

这简化了几个操作。 例如,如果你想删除一个只有一个指针的节点,你可以这样做:

 void delete(struct node *p) { *p->tqe_prev = p->tqe_next; if (p->tqe_next) { p->tqe_next->tqe_prev = p->tqe_prev; } free(p); } 

如果你有一个指向前一个节点的指针,你必须这样写:

 void delete(struct node *p) { if (p->tqe_prev) { p->tqe_prev->tqe_next = p->tqe_next; } else { ??? } if (p->tqe_next) { p->tqe_next->tqe_prev = p->tqe_prev; } free(p); } 

…但现在你卡住了:你不能写??? 部分不知道列表的根源在哪里。

类似的参数适用于insert操作。


向后遍历对于这种结构的确不是优先考虑的。 但是,如果必须的话,可以这样做(但只有当你知道你不在根目录时,也就是说你知道实际上有一个前一个节点):

 #include <stddef.h> struct node *prev(struct node *p) { return (struct node *)((unsigned char *)p->tqe_prev - offsetof(struct node, tqe_next)); } 

我们知道p->tqe_prevstruct node .tqe_next插槽的地址。 我们把这个地址转换成(unsigned char *)所以我们可以做字节型的指针运算。 我们在struct node结构(宏<stddef.h> offsetof )中减去.tqe_next的(字节)偏移量。 这给了我们struct node结构开始的地址,我们最终将其转换为正确的类型。