linux 內核鏈表解析2

我用一個程序來說明在struct person 中增加了struct list_head 變量後怎麼來操作這樣的雙向鏈表。

 

  1. #include <stdio.h>   
  2. #include "list.h"   
  3. struct person   
  4. {   
  5.     int age;   
  6.     int weight;   
  7.     struct list_head list;   
  8. };   
  9. int main(int argc, char* argv[])   
  10. {   
  11.     struct person *tmp;   
  12.     struct list_head *pos, *n;   
  13.     int age_i, weight_j;   
  14.     // 定義並初始化一個鏈表頭   
  15.     struct person person_head;   
  16.     INIT_LIST_HEAD(&person_head.list);   
  17.        
  18.     for(age_i = 10, weight_j = 35; age_i < 40; age_i += 5, weight_j += 5)   
  19.     {   
  20.         tmp =(struct person*)malloc(sizeof(struct person));   
  21.         tmp->age = age_i;   
  22.         tmp->weight = weight_j;   
  23.         // 把這個節點鏈接到鏈表後面   
  24.         // 這裏因爲每次的節點都是加在person_head的後面,所以先加進來的節點就在鏈表裏的最後面   
  25.         // 打印的時候看到的順序就是先加進來的就在最後面打印   
  26.         list_add(&(tmp->list), &(person_head.list));   
  27.     }   
  28.     // 下面把這個鏈表中各個節點的值打印出來   
  29.     printf("/n");   
  30.     printf("=========== print the list ===============/n");   
  31.     list_for_each(pos, &person_head.list)   
  32.     {   
  33.         // 這裏我們用list_entry來取得pos所在的結構的指針   
  34.         tmp = list_entry(pos, struct person, list);   
  35.         printf("age:%d,  weight: %d /n", tmp->age, tmp->weight);   
  36.     }   
  37.     printf("/n");   
  38.     // 下面刪除一個節點中,age爲20的節點   
  39.     printf("========== print list after delete a node which age is 20 ==========/n");   
  40.     list_for_each_safe(pos, n, &person_head.list)   
  41.     {   
  42.         tmp = list_entry(pos, struct person, list);   
  43.         if(tmp->age == 20)   
  44.         {   
  45.             list_del_init(pos);   
  46.             free(tmp);   
  47.         }   
  48.     }   
  49.     list_for_each(pos, &person_head.list)   
  50.     {   
  51.         tmp = list_entry(pos, struct person, list);   
  52.         printf("age:%d,  weight: %d /n", tmp->age, tmp->weight);   
  53.     }   
  54.     // 釋放資源  
  55.     list_for_each_safe(pos, n, &person_head.list)   
  56.     {   
  57.         tmp = list_entry(pos, struct person, list);   
  58.         list_del_init(pos);   
  59.         free(tmp);   
  60.     }   
  61.        
  62.     return 0;   
  63. }  

 

編譯:

 

linux 下的 可以:gcc -g -Wall main.c -o test

windows 下的可以建一個控制檯工程,把main.c 和list.h 加到工程中編譯。

 

運行test 後的輸出如下:

=========== print the list ===============

age:35, weight: 60

age:30, weight: 55

age:25, weight: 50

age:20, weight: 45

age:15, weight: 40

age:10, weight: 35

 

========== print list after delete a node which age is 20 ==========

age:35, weight: 60

age:30, weight: 55

age:25, weight: 50

age:15, weight: 40

age:10, weight: 35

 

我們看到,這就是一個非常好和有效的雙向鏈表,我們不需要爲每一種結構去定義相關的函數,如遍歷、增加和刪除等函數,我們只需要簡單的在結構中增加struct list_head 的一個變量,我們的結構立馬就變成了一個雙向鏈表,而且,我們對鏈表的操作也不用自己寫,直接調用已經定義好的函數和宏,一切就那麼簡單和有效。

 

文章寫到這裏是不是應該結束了呢,沒有,我還不想結束,還想在繼續說。

 

四、 一個結構多個鏈表

 

在上面,我們看到人的結構是這樣的:

struct person

{

int age;

int weight;

struct list_head list;

};

 

它的鏈表圖形看起來如下圖所示:

但我們知道,一個人,他的熟悉還有很多,例如他有各種各樣的衣服,各種不同的鞋子等。所以,我定義了兩個這樣的結構:

 

struct clothes

{

int size; // 衣服有各種大小

Color color; // 衣服有各種顏色,這裏假設有一種 Color 的類型

};

 

struct shoot

{

Kind kind; // 鞋子有各種類型,秋、冬、運動、休閒等,同樣假設已經定義過 Kind 這樣的類型

Color color; // 鞋子也有各種顏色

};

 

那麼這個人的定義可能就是這樣的:

struct person

{

int age;

int weight;

struct clothes clo;

struct shoot sht;

};

 

在這裏,我有意 clo  sht 這兩個變量放在 list 後面,其實,代表鏈表的 list 在結構中的位置在哪裏是沒什麼關係的, list_entry 也一樣可以將結構的指針找出來。

 

這裏有一個問題是,一個人不止一件衣服,也不止一雙鞋子,所以我們應該把他擁有的衣服和鞋子應該加上,那麼怎麼加呢?這裏應該把衣服和鞋子的結構也變成鏈表,這不就解決了。

 

把結構改一下,變成了這樣:

 

struct clothes

{

struct list_head list;

int size; // 衣服有各種大小

Color color; // 衣服有各種顏色,這裏假設有一種 Color 的類型

};

 

struct shoot

{

struct list_head list;

Kind kind; // 鞋子有各種類型,秋、冬、運動、休閒等,同樣假設已經定義過 Kind 這樣的類型

Color color; // 鞋子也有各種顏色

};

 

現在鞋子和衣服都是鏈表了,都可以把它們連接起來。那我們的結構是不是還應該這樣定義:

struct person

{

int age;

int weight;

struct clothes clo;

struct shoot sht;

};

 

如果是,那麼我們應該怎麼定義這個頭節點。在前面我們看到,定義一個 person_head 的頭節點是這樣的:

 

// 定義並初始化一個鏈表頭

struct person person_head;

INIT_LIST_HEAD (&person_head.list);

 

難道我們應該這樣定義嗎?

 

// 定義並初始化一個鏈表頭

struct person person_head;

INIT_LIST_HEAD (&person_head.list);

INIT_LIST_HEAD (&person_head.col.list);

INIT_LIST_HEAD (  person_head.sht.list);

 

那麼增加一件衣服進去呢,代碼看起來是這樣的:

 

 

struct clothes tmp =(struct clothes*)malloc(sizeof(struct clothes));

...

list_add(&(tmp->list), &(person_head.clo.list));

 

這樣會不會有點麻煩,其實,如果我們可以認真想一想,我們會發現,既然 struct peron 是一個含有 list_head 的結構,它可以把它的類型節點鏈接在後面,那麼 struct clothes 也是一個含有 list_head 的結構,它們本質也沒什麼區別,應該也可以鏈接在它後面的。所以我們的 struct person 的結構應該變成這樣:

struct person

{

int age;

int weight;

struct list_head clo;

struct list_head sht;

};

 

那麼我們鏈接節點後的圖形如下圖所示:

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

由上面,我們可以知道,有了 struct list_head 結構,我們可以爲我們的結構體增加多個子節點鏈表。

 

發佈了192 篇原創文章 · 獲贊 151 · 訪問量 97萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章