双向链表 struct list_head 个人认识

强大的原理小弟不才,未能见识,只是因为在驱动代码中struct list_head把我搞的云里雾里的,不时痛定思定的花了几个时候来推敲,哎,在明白原理后才恍然大悟,原来代码都是用来实现原理的,所以,有时候"内功"修为还是很有必要的,话不多说,分解如下:


INIT_LIST_HEAD(&nandr->devs)

   list_add_tail(&dev->list, &nandr->devs)


其中参数都是struct list_head {
struct list_head *next, *prev;
};

初始化的时候是把 next和prev都指向了 head。

list_add_tail调用的是如下函数:

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}


相比而言,__list_add只是多添加了一个参数head->prev,就是这个参数,所有的玄妙都在于此。

进入__list_add可以看到如下代码:

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;
}


代码如此简单,但是很容易陷入的指针指向问题,我们知道lisd_add_tail就是在链表尾添加一个struct list_head,从第一个开始分析,刚开始的时候next和prev都指向head,

所以在第一次执行__list_add时:

__list_add的三个参数是new,head->prev,head,(其中head->prev=head)

     next->prev = new;     => head->prev=new。
new->next = next;  =>new->=head

new->prev = prev;  =>new->prev=head。
prev->next = new; 这一句是在这里写出来很容易误解,从第三句(也就上一句代码)可以发现,new和head的prev都是指向prev,意思是每一次添加的新元素的prev都是指向head的,所以__list_add的第二个参数其实都是head,而prev->next=new相当于head->next=new,整个链表是个环形。


好,如果还没明白的话可以看第二次添加,

第一次添加后head->prev=new,head->next=new,new->next=head,new->prev=head。


为了区分new,假设第二次添加的是ayu,

__list_add(new, head->prev, head); 这个时候head应该是new,因为表尾是new了。

__list_add(new, head->prev, head)=>__list_add(ayu,new->prev,new) 这个new不是__list_add代码中的new,它是我们传递给__list_add的参数。

next->prev = new;  =>new->prev=ayu。

new->next = next;  => ayu->next=new。
new->prev = prev;  =>ayu->prev=new->prev=head
prev->next = new; => head->=new。




可能描述的不太清晰,最后总结一下:

双向链表最后一个元素的指向是last->prev=head,last->next=(last-1)

当添加一个量时,比如new,那么最后一个元素的指向和new都发生了哪些变化,因为都是加到表尾所以new->prev=head,new->next=last,而last的变化时last->prev=new,而last->next没有改变还是last->next=(last-1)。当然添加一个元素后,表头head的next需要更新prev->next=nex(指向最新的表尾)






 


你可能感兴趣的:(嵌入式)