6.链表的修改:
其实内核差不多都是把这些函数封装起来了,其实链表的修改也是这样的,代码其实很简单,没有什么解释的,就是把旧的结点的next和prev与原链表断开,再与新的结点的next和prev连接起来。如下函数:
- static inline void list_replace(struct list_head *new,struct list_head *old)
- {
- new->next=old->next;
- new->next->prev=new;
- new->prev=old->prev;
- new->prev->next=new;
- }
- static inline void list_replace_init(struct list_head *new,struct list_head *old)
- {
- list_replace(new,old);
- INIT_LIST_HEAD(old);
- }
上面的函数调用了第一个函数,最后调用了初始化函数,让不用的结点指向自己,是安全的删除。
7.链表的移动
链表的移动就是将一个结点移动到另一个结点,内核实现这个功能使用的是先把要移动的位置上的结点分离出来,再将要移动的结点添加到head之后进去就OK了:
- static inline void list_move(struct list_head *list, struct list_head *head)
- {
- __list_del_entry(list);
- list_add(list, head);
- }
- static inline void list_move_tail(struct list_head *list,
- struct list_head *head)
- {
- __list_del_entry(list);
- list_add_tail(list, head);
- }
第一个函数调用的是list_add_tail(list,head),第二个函数调用的是list_add_tail(list,head),这两个函数的区别我就不细说了,前面提到过。
8.链表的合并
链表的合并也比较简单,先看一下具体的实现函数:
- static inline void __list_splice(const struct list_head *list,struct list_head *prev,struct list_head *next)
- {
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- first->prev = prev;
- prev->next = first;
- last->next = next;
- next->prev = last;
- }
这个函数首先列举出来的这段代码和上面的每一个功能的第一个函数一样,都是被后面的函数调用的。先看它的参数,list是一个头结点,要把它后面的那一串链表插入到别的链表中,prev和next就是链表要插入的位置的前后结点,在函数体中,先用first和last记录这串要插入的链表的头和尾,把first和要插入位置的前面prev相连,把last和要插入位置的后面next相连,就像插入一个大结点一样把链表插入到规定位置。
后面调用这个函数的代码如下:
- static inline void list_splice_init(struct list_head *list,struct list_head *head)
- {
- if(!list_empty(list)){
- __list_splice(list,head,head->next);
- INIT_LIST_HEAD(list);
- }
- }
- static inline void list_splice_tail_init(struct list_head *list,struct list_head *head)
- {
- if(!list_empty(list)){
- __list_splice(list,head->prev,head);
- INIT_LIST_HEAD(list);
- }
- }
第一个函数是把链表插入到head之后,head->next之前,就是说把链表插到头结点后,类似于头插法,第二个函数是把链表插入到head->prev之后,head之前,就是说把链表插到链表的最后,类似于尾插法。当然,在插入链表之前,要保证待插的那个链表不是空链表。最后一步初始化的作用就是释放了不用的那个链表头。
9.将链表一分为二
能将链表合并就能将其一分为二,这个函数是将head后至entry之间(包括entry)的所有结点都“切开”,让他们成为一个以list为头结点的新链表。我们先从宏观上看,如果head本身是一个空链表则失败;如果head是一个单结点链表而且entry所指的那个结点又不再这个链表中,也失败;当entry恰好就是头结点,那么直接初始化list,为什么?因为按照刚才所说的切割规则,从head后到entry前事实上就是空结点。如果上述条件都不符合,那就可以“切割”了。上述的条件如下函数所写:
- static inline void list_cut_position(struct list_head *list,
- struct list_head *head, struct list_head *entry)
- {
- if (list_empty(head))
- return;
- if (list_is_singular(head) &&
- (head->next != entry && head != entry))
- return;
- if (entry == head)
- INIT_LIST_HEAD(list);
- else
- __list_cut_position(list, head, entry);
- }
调用的切割函数如下:
- static inline void __list_splice(const struct list_head *list,
- struct list_head *prev,
- struct list_head *next)
- {
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- first->prev = prev;
- prev->next = first;
- last->next = next;
- next->prev = last;
- }
10.测试函数
测试函数其实理解起来挺简单的,一看代码就能明白到底是要测试什么,如下代码就测试的就是list是否是最后一个结点:
- static inline int list_is_last(const struct list_head *list,
- const struct list_head *head)
- {
- return list->next == head;
- }
下面的函数是测试head链表是否只有一个结点:这个链表既不能是空而且head前后的两个结点都得是同一个结点。
- static inline int list_is_singular(const struct list_head *head)
- {
- return !list_empty(head) && (head->next == head->prev);
- }
11.判空函数
- static inline int list_empty(const struct list_head *head)
- {
- return head->next == head;
- }
- static inline int list_empty_careful(const struct list_head *head)
- {
- struct list_head *next = head->next;
- return (next == head) && (next == head->prev);
- }
第二个比第一个函数“仔细”在哪里?前者只是认为只要一个结点的next指针指向头指针就算为空,但是后者还要去检查头节点的prev指针是否也指向头结点。另外,这种仔细也是有条件的,只有在删除节点时用list_del_init(),才能确保检测成功。
仔细去分析这些代码收获真的挺大的,建议大家都去好好的分析一下list.h,会对你有帮助的!
阅读(2175) | 评论(0) | 转发(0) |