Linux双向链表实现
代码位于include/linux/list.h,链表初始化:
struct list_head {
struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
初始化后,链表状态图,框图左侧为prev指针,右侧为next指针:
利用list_add和list_add_tail函数插入新节点,前者插入新节点到链表头部,也就是head的next指针所指的位置,后者插入新节点到链表尾部,即head的prev指针所指位置,代码:
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
首次插入新节点new1,无论是list_add还是list_add_tail函数,插入之后的链表框图相同:
继续调用list_add, list_add_tail函数插入第二个节点new2的框图如下:
双向链表循环代码:
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
循环处理首先由head的next指针开始,一直循环到节点head为止。处理的顺序与list_add_tail函数插入节点的顺序相反,与list_add插入节点的顺序相同。
list_for_each_entry循环遍历链表的所有元素,Linux源代码如下:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
其中pos,是含有list_head类型的member成员的type类型结构体指针。但是链表头head并没有包含在type类型的结构体内,所以访问包含head的type类型指针其实是在访问一个不合理的地址,一种情况发生在list_for_each_entry遍历一个空表的时候;另一种情况是判断是否为链表的最后一个元素的时候。