理解Linux双向链表
原文:
http://blog.youkuaiyun.com/leisure512/article/details/5188986
我截取其中一部分,并加了图解。
Linux内核中双向链表hlist_head,它的定义:
struct hlist_node *first;
};
struct hlist_node *next, **pprev;
};
显然,这个双向链表不是真正的双向链表,因为表头只有一个first域,为什么这样设计?代码中的注释解释:为了节约内存,特别适合作为Hash表的冲突链,但Hash表很大时,那么表头节约下来的内存就相当客观了,虽然每个表头只节约一个指针。
同时,表头的不一致性也会带来链表操作上的困难,显然就是在表头和首数据节点之间插入节点时需要特别处理,这也就是为什么会设计二级指针pprev的原因。看看代码
static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next){
n->pprev=next->pprev;
n->next=next;
next->pprev=&n->next;
*(n->pprev)=n;
}
解释:指针n指向新节点,指针next指向将要在它之前插入新节点的那个节点。
看上面的代码,就可以看到二级指针pprev的威力了!有没有看到,当next就是第一个数据节点时,这里的插入也就是在表头和首数据节点之间插入一个节点,但是并不需要特别处理!而是统一使用*(n->pprev)来访问前驱的指针域(在普通节点中是next,而在表头中是first)。这太经典了!
我详细解释下面的代码:
static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
{
n->pprev=next->pprev; 1
n->next=next; 2
next->pprev=&n->next; 3
*(n->pprev)=n; 4
}