現在的位置: 首頁 > 綜合 > 正文

淺談list.h頭文件之雙向循環鏈表

2018年04月17日 ⁄ 綜合 ⁄ 共 15537字 ⁄ 字型大小 評論關閉

這兩天看了下/usr/src/linux/list.h文件,感受頗多,裡面主要講了兩種鏈表:雙向循環鏈表和哈希鏈表,以及他們的一些基本的操作!下面來和大家分享下我的分析過程:(申明:我以下分析基於的內核版本是:2.6.32-24)


19 struct list_head { 

 20 struct list_head *next, *prev; 
 21 };



這裡定義了一個list_head的結構體,它是雙向循環鏈表的節點結構體,裡面並無數據成員,就只有兩個指向list_head型結構體的指針,分別指向其前驅節點和後繼節點,因此,這個結構體的主要作用就是嵌套在其他結構體的定義中起到鏈接作用。例如:
struct student{
   int id;
   char name[20];
   struct list_head member;
};
member是一個list_head型的結構體,同時又是student型結構體的成員,利用member.prev和member.next就可以訪問到和與之相連的另外兩個student結構體的member成員。 


 23 #define LIST_HEAD_INIT(name) { &(name), &(name) }

 24 
 25 #define LIST_HEAD(name) \
 26     struct list_head name = LIST_HEAD_INIT(name)

這三行的意思是利用兩個宏來定義並初始化一個list_head型的結構體name,首先來看下面這個宏,他定義了name
這個結構體,並利用上面的宏為其賦值,而上面的宏的意思是將一個結構體自身賦給它的前驅節點和後繼節點,這樣便完成了定義並初始化的功能。


28 static inline void INIT_LIST_HEAD(struct list_head *list) 

 29 { 
 30 list->next = list; 
 31 list->prev = list; 
 32 }


雖然上面的兩個宏提供了定義並初始化一個list_head類型的結構體的功能。但是系統還是提供了這個單獨進行初始化的函數。在這裡我們簡單解釋下static和inline的意思:
static修飾的函數就是靜態函數,是對函數作用域的限制,指該函數的作用域僅限於本文件,不能被其他文件使用,因此static具有信息隱藏作用。
inline修飾的函數的意思是這個函數對編譯程序是可見的,編譯程序在調用這個函數時就立即展開該函數,而不是保存現場,執行調用函數,恢復現場等繁瑣操作,這樣可以提高效率。這樣的函數一般稱之為「內聯函數」,但是注意:inline必須和函數定義體放在一起才能使函數成為內聯函數,一般放於頭文件中。



40 #ifndef CONFIG_DEBUG_LIST
 41 static inline void __list_add(struct list_head *new,
 42                   struct list_head *prev,
 43                   struct list_head *next)
 44 {
 45     next->prev = new;
 46     new->next = next;
 47     new->prev = prev;
 48     prev->next = new;
 49 }
 50 #else
 51 extern void __list_add(struct list_head *new,
 52                   struct list_head *prev,
 53                   struct list_head *next);
 54 #endif


這幾句是一個條件編譯的語句,大體的意思就是如果沒有定義CONFIG_DEBUG_LIST這個宏就編譯下面static inline void __list_add()函數,else就使用extern聲明此函數已經在外部文件中定義過了,防止重複定義!

我們來看下這個函數是幹什麼的?其實我的建議是大家在看此類鏈表代碼時找個草稿本在上面一邊看一邊花,這樣很簡單明了,語言解釋反倒麻煩,我就不羅嗦了,它的意思就是在原本相連的prev和next兩個節點之間增加一個new節點。


 64 static inline void list_add(struct list_head *new, struct list_head *head) 
 65 { 
 66 __list_add(new, head, head->next); 
 67 }



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

這兩個函數由上面的__list_add()函數衍生而來,其實後面很多都是如此,複雜的函數都是由前面定義的基本函數組合而來。這兩個函數也不用羅嗦了,大概的意思就是分別在head節點的後面和前面增加一個節點new,在head節點後面增加即增加在鏈表頭部,在head前面增加即增加在鏈表尾部!(注意:循環鏈表的特點!)


 90 static inline void __list_del(struct list_head * prev, struct list_head * next)
 91 {
 92     next->prev = prev;
 93     prev->next = next;
 94 }

這又是一個基本函數,意思是將形參傳來的兩個節點鏈接起來。

102 #ifndef CONFIG_DEBUG_LIST
103 static inline void list_del(struct list_head *entry)
104 {
105     __list_del(entry->prev, entry->next);
106     entry->next = LIST_POISON1;
107     entry->prev = LIST_POISON2;
108 }
109 #else
110 extern void list_del(struct list_head *entry);
111 #endif

還是一個條件編譯語句,我們直接看函數定義,此函數是一個刪除節點函數,它首先利用上面的基本函數將entry所指的節點的前驅節點和後繼節點鏈接起來,然後將entry節點的前驅指針和後繼指針分別指向兩個宏:LIST_POISON1,LIST_POISON2,在poison.h文件中,找到了這兩個宏的定義:
 22 #define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
 23 #define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
POISON_POINTER_DELTA宏又是什麼呢?同樣在在那個文件中:
 11 #ifdef CONFIG_ILLEGAL_POINTER_VALUE
 12 # define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
 13 #else
 14 # define POISON_POINTER_DELTA 0
 15 #endif
這樣我們就知道POISON_POINTER_DELTA其實就是0,那麼LIST_POISON1 ,LIST_POISON2,這兩個宏就很輕鬆理解,其實就是兩個固定的地址。

120 static inline void list_replace(struct list_head *old,
121                 struct list_head *new)
122 {
123     new->next = old->next;
124     new->next->prev = new;
125     new->prev = old->prev;
126     new->prev->next = new;
127 }

這個函數是實現用new節點來替換old節點,但是我們細心便會發現它的缺陷,它雖然實現了替換,但是old節點依然在那條鏈表上,即沒有取消old節點的前驅指針和後繼指針的指向。

129 static inline void list_replace_init(struct list_head *old,
130                     struct list_head *new)
131 {
132     list_replace(old, new);
133     INIT_LIST_HEAD(old);
134 }

因為上面的函數存在缺點,所以出現了這個函數,它在實現替換功能後,使用了INIT_LIST_HEAD()函數,我們回頭看下這個函數的定義,便知道是將old節點的前驅節點和後繼節點全部指向自己!以做到徹底的替換!

140 static inline void list_del_init(struct list_head *entry)
141 {
142     __list_del(entry->prev, entry->next);
143     INIT_LIST_HEAD(entry);
144 }

這個函數是一個刪除節點的函數,首先將entry的前驅節點和後繼節點鏈接起來,再將entry節點的前驅和後繼指針分別指向自己,以做到徹底刪除!後面的組合函數愈來愈多,我的感受是第一次看肯定記不住前面那麼多函數的功能,那麼就即時的回頭去看,慢慢就熟悉,記住了。

151 static inline void list_move(struct list_head *list, struct list_head *head)
152 {
153     __list_del(list->prev, list->next);
154     list_add(list, head);
155 }

這個函數就是先將list節點從原鏈表中刪除,然後將其添加到head鏈表的表頭即head節點的下一個節點!

162 static inline void list_move_tail(struct list_head *list,
163                   struct list_head *head)
164 {
165     __list_del(list->prev, list->next);
166     list_add_tail(list, head);
167 }

類似於上面的函數,先將list節點從原鏈表中刪除,在將其添加到head鏈表的表尾即head的前一個節點!(注意理解雙向循環鏈表!)

174 static inline int list_is_last(const struct list_head *list,
175                 const struct list_head *head)
176 {
177     return list->next == head;
178 }

這個函數是測試list節點是否為head鏈表的表尾節點。是返回1,否則返回0.

184 static inline int list_empty(const struct list_head *head)
185 {
186     return head->next == head;
187 }

這個函數是測試head鏈表是否為空鏈表。是返回1,否則返回0.(此函數存在缺陷,看下面它的仔細版:)

202 static inline int list_empty_careful(const struct list_head *head)
203 {
204     struct list_head *next = head->next;
205     return (next == head) && (next == head->prev);
206 }

看函數名和上面的很相似,它完善了上面函數的缺陷,它檢查空鏈表時更加」仔細「,它判斷空鏈表有兩個條件:
1,上面函數的條件,即head的後繼節點就是本身。
2,head的前驅節點和後繼節點相同。
這兩個條件一起就可以確定此鏈表是否為空鏈表。

212 static inline int list_is_singular(const struct list_head *head)
213 {
214     return !list_empty(head) && (head->next == head->prev);
215 }

這個函數判斷head鏈表是否為單節點鏈表。

217 static inline void __list_cut_position(struct list_head *list,
218         struct list_head *head, struct list_head *entry)
219 {
220     struct list_head *new_first = entry->next;
221     list->next = head->next;
222     list->next->prev = list;
223     list->prev = entry;
224     entry->next = list;
225     head->next = new_first;
226     new_first->prev = head;
227 }

這個函數是將head鏈表的頭結點至entry節點之間的節點連在list節點後面,即組成以list節點為頭結點的新鏈表。

243 static inline void list_cut_position(struct list_head *list,
244         struct list_head *head, struct list_head *entry)
245 {
246     if (list_empty(head))
247         return;
248     if (list_is_singular(head) &&
249         (head->next != entry && head != entry))
250         return;
251     if (entry == head)
252         INIT_LIST_HEAD(list);
253     else
254         __list_cut_position(list, head, entry);
255 }

這個函數的功能和上面的函數相同,但是判斷的更加仔細,首先它排除了空鏈表的情況,下來又否定了單鏈表而且節點不是entry的情況,然後又判斷了entry和head指向同一節點的情況,這種情況下按照功能,只需要初始化list鏈表即可。最後在排除晚其他不安全情況後進行了「切割移植」操作。

257 static inline void __list_splice(const struct list_head *list,
258                  struct list_head *prev,
259                  struct list_head *next)
260 {
261     struct list_head *first = list->next;
262     struct list_head *last = list->prev;
263 
264     first->prev = prev;
265     prev->next = first;
266 
267     last->next = next;
268     next->prev = last;
269 }

這個函數的意思是將list鏈表的全部節點(頭結點list除外)插入在prev和next節點之間。

276 static inline void list_splice(const struct list_head *list,
277                 struct list_head *head)
278 {
279     if (!list_empty(list))
280         __list_splice(list, head, head->next);
281 }

這個函數的意思就是在list是非空鏈表的情況下,將其插在head鏈表的頭部,即head節點的後面。此函數不安全,因為在插入後還能通過list節點訪問到其餘的節點。

288 static inline void list_splice_tail(struct list_head *list,
289                 struct list_head *head)
290 {
291     if (!list_empty(list))
292         __list_splice(list, head->prev, head);
293 }

與上面的函數類似,在list是非空鏈表的情況下,將其插在head鏈表的尾部,即head節點的前面。不安全,原因同上!

302 static inline void list_splice_init(struct list_head *list,
303                     struct list_head *head)
304 {
305     if (!list_empty(list)) {
306         __list_splice(list, head, head->next);
307         INIT_LIST_HEAD(list);
308     }
309 }

這個函數的意思就是在list是非空鏈表的情況下,將其插在head鏈表的頭部,即head節點的後面。然後對list節點進行初始化,排除不安全因素。

319 static inline void list_splice_tail_init(struct list_head *list,
320                      struct list_head *head)
321 {
322     if (!list_empty(list)) {
323         __list_splice(list, head->prev, head);
324         INIT_LIST_HEAD(list);
325     }
326 }

這個函數的意思就是在list是非空鏈表的情況下,將其插在head鏈表的尾部,即head節點的前面。然後對list節點進行初始化,排除不安全因素。

334 #define list_entry(ptr, type, member) \
335     container_of(ptr, type, member)

真正精彩的地方到了,這個宏以及下面的幾個宏的定義,我感覺很精彩!有些地方初次理解起來比較費力,我在這裡為大家慢慢分享下我的理解,不一定對,有錯誤的地方希望大家賜教!不羅嗦了,我們看上面這個宏,這其實就是一個宏定義的轉移,利用container_of這個宏定義了上句的list_entry這個宏,我們還是不懂意思,不急。我們在kernel.h裡面找到container_of這個宏的定義:
653 #define container_of(ptr, type, member) ({          \
654     const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
655     (type *)( (char *)__mptr - offsetof(type,member) );})
這個裡面又用到了offsetof的宏定義,我們在stddef.h裡面找到它的定義:
24 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
下來,我們來綜合分析下,我們首先分析offsetof的意思:我們再看一遍它的定義,((size_t) &((TYPE *)0)->MEMBER)
最外面的括弧裡面分為兩部分:(size_t)    和    & ((TYPE *)0)->MEMBER,我們首先看& ((TYPE *)0)->MEMBER,它由兩部分組成,&和 ((TYPE *)0)->MEMBER,我們先看後者((TYPE *)0)->MEMBER,他首先將0強制類型轉換成指向TYPE類型的結構體的指針,然後引用其MEMBER成員。可能有些人不懂,這樣,我們來隨便定義一個TYPE類型的結構體:
struct TYPE {
int a;
char b;
int c;
struct list_head MEMBER;
};
這樣看起來是不是一目了然,現在我們來看offsetof的意思,很簡單就是:首先將0強制類型轉換成指向TYPE類型的結構體的指針,然後取其MEMBER成員的地址,最後再強制類型轉換成size_t行,即(unsigned int)型!那麼有人會問了,為什麼要使用0呢?要它的MEMBER成員的地址有什麼用呢?用處很大,而且很巧!我們需要慢慢品嘗:
首先,我們回顧下,什麼叫指針?指針就是一個內存單元的地址!那麼好,我們上面說到了,將0強制轉換成TYPE類型結構體的指針,那麼我們來說說,這個所指的結構體的起始地址是多少?理所當然是0.那麼很好,既然它的起始地址是0,那麼它的MEMBER成員的地址,其實就是它在這個結構體的偏移量!理解了嗎?OK!offsetof(TYPE, MEMBER)的意義我們知道了,就是求TYPR類型的結構體中MEMBER成員的偏移量!它的作用,我們繼續往下看:
知道了offsetof的作用,我們返回去看container_of這個宏的定義:
#define container_of(ptr, type, member) ({          \
              const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
              (type *)( (char *)__mptr - offsetof(type,member) );})
它的定義分兩句話,先看第一句:const typeof( ((type *)0)->member ) *__mptr = (ptr);我們從左開始慢慢看,首先看typeof()裡面的東西:((type *)0)->member ,它什麼意思呢?相比大家都知道了,它就是以0為起始地址的type類型的結構體的member成員(還沒理解的,分析過程見上面的offsetof分析過程!)。那麼typeof的含義是什麼呢?
typeof的含義都能看出來,就是提取類型的意思!那麼等號左面const typeof( ((type *)0)->member ) *__mptr的意思就是:定義一個指向以0為起始地址的type類型的結構體的member成員的類型的指針_mptr,那麼第一句的意思很明了,就是定義這麼一個指針,並將ptr指針的值賦給它!那麼ptr是什麼呢?根據這個宏的形參,我們知道它是指向type類型結構體中member成員的指針,那麼現在_mptr也指向了type類型結構體中member成員。
好,我們看下一句:(type *)( (char *)__mptr - offsetof(type,member) );offsetof宏的定義我們知道了,-mptr
的意思我們也知道了,那麼這句話應該不難,就是先將指針_mptr強制類型轉換成(char *),再用他減去type類型的結構體中member成員的偏移量,最後將結果強制轉換成(type *)類型!可能有些人不理解這句話背後的意思,別急,我們慢慢分析,由於_mptr是指向type類型結構體中member成員的指針,那麼她的值就是type類型結構體中member成員的地址,而offsetof(type,member)的意思是type類型的結構體中member成員的偏移量,那麼用它的地址減去它的偏移量,是什麼結果呢?這是個數學問題!用一個結構體中一個成員的地址減去它在這個結構體中的偏移量,等於?很簡單,等於這個結構體的起始地址!好!那麼有人會問,為什麼要轉換成(char
*)類型再減呢?
這是一個關於指針加減的問題!就是如果前面的指針不是(char *)等單位元組類型,假設是(int *)類型,那麼用它家去一個偏移量,就等於減去了(偏移量*sizeof(int)個單元,這是為什麼呢?因為指針在進行加減時是以減數類型的大小為基準進行加減的,就是說如果是(int *)類型,那麼它實際會以int類型為單元減去偏移量個!不就是減去了(偏移量*sizeof(int)個單元嗎?這樣就錯了 !為此我專門寫了一個簡單的函數進行驗證:
  1 #include<stdio.h>
  2 int main()
  3 {
  4     struct student
  5     {
  6         int a;
  7         char b;
  8         int c;
  9         int d;
 10     };
 11     struct student *p;
 12     struct student q;
 13     p=&q;                                                                         //用p指向這個結構體!
 14     printf("&struct=%u\n",(unsigned long)p);                        //列印這個結構體的地址!
 15     printf("&q.d   =%u\n",(unsigned long)&(q.d));                  //列印這個結構體中d變數的地址
 16     printf("   true=%u\n",(unsigned long)(char *)&(q.d)-12);   //列印d變數轉換成(char *)後減去它的偏移量到的結構體的地址!(是正確的!)
 17     printf("  false=%u\n",(unsigned long)(&(q.d)-12));        //列印沒有轉換進行減法得到的值,是錯誤的!而且正是我們分析的減了偏移量*sizeof(int),所以是錯的!
 18 }
運行結果是:(有編譯警告,先不管!直接運行!)
[wjl@wjl-desktop ~/c/test 21:38:58 #27]$ ./test
&struct=3213925676
&q.d   =3213925688
   true=3213925676        //3213925688-12
  false=3213925676        //3213925676-12*4
大家仔細觀察這個程序便會發現指針加減的問題!驗證我上面的說法!ok!
那麼到此為止,我們分析接近尾聲了,container_of(ptr, type, member)宏的意思出來了,就是已知指向type類型的結構體中member成員的指針ptr的情況下,我們求出了這個結構體的起始地址!要這個地址有什麼用呢?很多人可能都想到了,指針!好,我們往下看!

345 #define list_first_entry(ptr, type, member) \
346     list_entry((ptr)->next, type, member)

這個宏的意思是不是很簡單了,就是已知type類型的結構體中member成員的指針後,我們 可以求得它所在的鏈表的下一個指針所指的member所在的type類型的結構體的起始地址!

353 #define list_for_each(pos, head) \
354     for (pos = (head)->next; prefetch(pos->next), pos != (head); \
355             pos = pos->next)

這個宏的意思是:從head節點開始(不包括head節點!)遍歷它的每一個節點!
在這裡我們說下:prefetch(pos->next)這句話,它的意思就是告訴cpu下一個要使用的內存!以提高效率!

367 #define __list_for_each(pos, head) \
368     for (pos = (head)->next; pos != (head); pos = pos->next)

這個函數和上面的函數大同小異,功能相同,只是它沒有預取下一個要遍歷的節點!所以效率上差點!所以它比較適合節點數比較少的鏈表!

375 #define list_for_each_prev(pos, head) \
376     for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
377             pos = pos->prev)

看完上面的函數,這個就比較簡單,它也是從head節點開始(不包括head節點)向前遍歷每一個節點!即從鏈表的尾部開始遍歷!

385 #define list_for_each_safe(pos, n, head) \
386     for (pos = (head)->next, n = pos->next; pos != (head); \
387         pos = n, n = pos->next)

這個應該也沒有難度,只是操作更加安全,它用n先將下一個要遍歷的節點保存起來,防止程序中刪除本節點後,無法找到下一個節點,而出現錯誤!

395 #define list_for_each_prev_safe(pos, n, head) \
396     for (pos = (head)->prev, n = pos->prev; \
397          prefetch(pos->prev), pos != (head); \
398          pos = n, n = pos->prev)

這個應該也沒有難度,只是操作更加安全,它用n先將下一個要遍歷的節點保存起來,防止程序中刪除本節點後,無法找到下一個節點,而出現錯誤!

406 #define list_for_each_entry(pos, head, member)              \
407     for (pos = list_entry((head)->next, typeof(*pos), member);  \
408          prefetch(pos->member.next), &pos->member != (head);    \
409          pos = list_entry(pos->member.next, typeof(*pos), member))

又是一個很長的宏定義!我們一句一句看for()裡面的三條語句,第一句:pos = list_entry((head)->next, typeof(*pos), member),list_entry(ptr,type,member)宏的意思是:已知指向type類型的結構體中member成員的指針ptr的情況下,我們求出了這個結構體的起始地址,所以這裡我們進行類比分析,我們已知一個結構體的類型是typeof(*pos)類型!為什麼是這個類型呢?因為pos是指向某一個結構體的指針,那麼*pos不就是這個結構體嗎?
繼而typeof(*pos)不就是這個結構體的類型嗎?OK!那麼這條語句的意思就是一個結構體的指針pos,以及指向它中的member成員的指針(head->next),我們求出這個結構體的起始地址,並賦予pos!即讓pos指向與之相連的下一個結構體!這裡有些人可能納悶了,結構體怎麼相連了?還記得我們剛開始分析struct list_head這個類型時舉的例子嗎?
struct student{
   int id;
   char name[20];
   struct list_head member;
};
我們來看這個例子,結構體雖然不能相連,但是結構體中的member成員卻是list_head類型的結構體,他是可以組成鏈表的!第三句和第一句相同!不羅嗦了!
總之,這個宏的意思就是已知指向某個結構體的指針pos,以及指向它中member成員的指針head,從下一個結構體開始向後遍歷這個結構體鏈。

417 #define list_for_each_entry_reverse(pos, head, member)          \
418     for (pos = list_entry((head)->prev, typeof(*pos), member);  \
419          prefetch(pos->member.prev), &pos->member != (head);    \
420          pos = list_entry(pos->member.prev, typeof(*pos), member))

原理同上,作用就是:已經指向某個結構體的指針pos,以及指向它中member成員的指針head,從下一個結構體開始向前遍歷這個結構體鏈。

430 #define list_prepare_entry(pos, head, member) \
431     ((pos) ? : list_entry(head, typeof(*pos), member))

這個宏比較簡單,它就是判斷pos這個指針是否為空,為空的話給它賦值list_entry(head, typeof(*pos), member)這條語句求出來的結構體的地址!具體的求法看上面的分析!

442 #define list_for_each_entry_continue(pos, head, member)         \
443     for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
444          prefetch(pos->member.next), &pos->member != (head);    \
445          pos = list_entry(pos->member.next, typeof(*pos), member))

這句話的分析基本和上面那個相同,它的意思就是:已知指向某個結構體的指針pos,以及指向它中的member成員的head指針,從它的下一個結構體開始向後遍歷這個結構體鏈!

456 #define list_for_each_entry_continue_reverse(pos, head, member)     \
457     for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
458          prefetch(pos->member.prev), &pos->member != (head);    \
459          pos = list_entry(pos->member.prev, typeof(*pos), member))

這個函數和上面的函數基本相同,只是它是向前遍歷,即從結構體鏈的尾部開始遍歷!

469 #define list_for_each_entry_from(pos, head, member)             \
470     for (; prefetch(pos->member.next), &pos->member != (head);  \
471          pos = list_entry(pos->member.next, typeof(*pos), member))

這個函數和上面的函數基本相同,只是它是從前向後遍歷,而且從pos所指的節點開始!(包括pos所指的節點!)

480 #define list_for_each_entry_safe(pos, n, head, member)          \
481     for (pos = list_entry((head)->next, typeof(*pos), member),  \
482         n = list_entry(pos->member.next, typeof(*pos), member); \
483          &pos->member != (head);                    \
484          pos = n, n = list_entry(n->member.next, typeof(*n), member))

這個安全的函數和上面的安全函數大同小異!原理相同,都是先保存下一個要遍歷的節點!防止本節點被刪除,下一節點無法訪問,導致錯誤!

496 #define list_for_each_entry_safe_continue(pos, n, head, member)         \
497     for (pos = list_entry(pos->member.next, typeof(*pos), member),      \
498         n = list_entry(pos->member.next, typeof(*pos), member);     \
499          &pos->member != (head);                        \
500          pos = n, n = list_entry(n->member.next, typeof(*n), member))

原理同上,不羅嗦了!只是增加了一個指向結構體的指針,在for()裡面的第一句就賦值給他讓它指向pos的下一個結構體!也是處於安全考慮!

512 #define list_for_each_entry_safe_from(pos, n, head, member)             \
513     for (n = list_entry(pos->member.next, typeof(*pos), member);        \
514          &pos->member != (head);                        \
515          pos = n, n = list_entry(n->member.next, typeof(*n), member))

和上面基本一樣,只是上面是從pos所指的下一個結構體開始,這個是從pos所指的結構體開始!

527 #define list_for_each_entry_safe_reverse(pos, n, head, member)      \
528     for (pos = list_entry((head)->prev, typeof(*pos), member),  \
529         n = list_entry(pos->member.prev, typeof(*pos), member); \
530          &pos->member != (head);                    \
531          pos = n, n = list_entry(n->member.prev, typeof(*n), member))

和上一個基本相似,只是這個函數是向前開始遍歷結構體,即從鏈的尾部開始遍歷!

到此為止,list.h中的雙向循環鏈表逐句分析結束!可能有很多錯誤的地方,希望大家指正!
在此和大家分享一下我的第一次看源碼的心得吧,剛開始,記得是星期一的時候,陳老師剛說了這個事情,我就開始看了,說實話,我剛開始打開list.h有種頭暈的感覺,那麼多的代碼,英文的注釋!太恐怖了,但是當我看晚第一個結構體list_head的時候,我的興趣上來了,它為什麼這麼定義呢?我往下看,不懂的地方不管,大概瀏覽一邊,我就知道這個頭文件是個定義了很多鏈表操作的頭文件,那麼第二遍,第三遍,第四遍.......慢慢的,一個一個函數我懂了,一邊網上查,一邊自己拿著筆畫函數的鏈表,進行模擬操作,這樣我們就可以清楚的知道,每一步的含義!就這樣,list.h中的雙向鏈表部分已經基本清楚了!
希望

抱歉!評論已關閉.