內核鏈表list_entry()宏詳解

#define list_entry(ptr, type, member) \
       ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

    ptr是指向list_head類型鏈表的指針,type爲一個結構,而member爲結構type中的一個域,類型爲list_head,這個宏返回指向type結構的指針。在內核代碼中大量引用了這個宏,因此,搞清楚這個宏的含義和用法非常重要。

   
設有如下結構體定義:
typedef struct xxx
{
     ……(結構體中其他域,令其總大小爲size1)
     type1 member;
     ……(結構體中其他域)
}type;


定義變量:
   type a;
   type * b;
   type1 * ptr;
執行:
   ptr=&(a.member);
   b=list_entry(ptr,type,member);
則可使b指向a,得到了a的地址。

如何做到的呢?

先看&((type *)0)->member:
        把“0”強制轉化爲指針類型,則該指針一定指向“0”(數據段基址)。因爲指針是“type *”型的,所以可取到以“0”爲基地址的一個type型變量member域的地址。那麼這個地址也就等於member域到結構體基地址的偏移字節數。
再來看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):

       (char *)(ptr)使得指針的加減操作步長爲一字節,(unsigned long)(&((type *)0)->member)等於ptr指向的member到member所在結構體基地址的偏移字節數。二者一減便得出該結構體的地址。轉換爲 (type *)型的指針,大功告成。

--------------------------------------------------------------------------------------------------

/**

* list_entry - get the struct for this entry

* @ptr:   the &struct list_head pointer.

* @type:   the type of the struct this is embedded in.

* @member:   the name of the list_struct within the struct.

*/

#define list_entry(ptr, type, member) \

   ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

--------------------------------------------------------------------------------------------------

指針ptr指向結構體type中的成員member;通過指針ptr,返回結構體type的起始地址,如圖2。

  

type

        |----------|

        | |

          | |

          |----------|

ptr--> | member --|

          |----------|

          | |

        | |

         |----------|

        圖2 list_entry()宏的示意圖

爲了便於理解,在此給予進一步說明。

例如my_list結構:

struct my_list{

void *mydata;

struct list_head list;

}; struct list_head *pos;  

則list_entry(pos, mylist, list)宏,就可以根據pos的值,獲取mylist的地址,也就是指向mylist的指針,這樣,我們就可以存取mylist->mydata字段了。

可爲什麼能夠達到這樣的效果?

list_entry(pos, mylist, list) 展開以後爲:

((struct my_list *)((char *)(pos) - (unsigned long)(&((struct my_list *)0)->list)))

這看起來會使大多數人眩暈,但仔細分析一下,實際很簡單。

((size_t) &(type *)0)->member)把0地址轉化爲type結構的指針,然後獲取該結構中member成員的指針,並將其強制轉換爲size_t類型。於是,由於結構從0地址開始定義,因此,這樣求出member的成員地址,實際上就是它在結構中的偏移量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章