歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> container_of 宏、offsetof 宏 分析

container_of 宏、offsetof 宏 分析

日期:2017/3/3 14:02:56   编辑:Linux技術

container_of 是Linux中常用的宏,其作用就是根據結構體成員變量的地址獲取結構體的地址。

container_of 在include/linux/kernel.h 中定義:

[code] /**
   * container_of - cast a member of a structure out to the containing structure
   * @ptr:        the pointer to the member.
   * @type:       the type of the container struct this is embedded in.
   * @member:     the name of the member within the struct.
   *
   */
  #define container_of(ptr, type, member) ({ \
          const typeof(((type *)0)->member) * __mptr =(ptr); \
          (type *)((char *)__mptr - offsetof(type, member)); })
這麼一個宏定義,可以說是把C語言的指針運用的出神入化。這個宏可以分三步來解讀。

第一步:定義了一個與 ptr 相同類型的指針 __mptr,這個指針類型通過 typeof(((type *)0)->member) 來獲得,然後將__mptr 賦值為 ptr。

typeof關鍵字是C語言中的一個新擴展,這個特性在linux內核中應用非常廣泛,其作用就是通過一個變量獲取它的類型。

要獲取結構體中member成員的類型,首先將零地址強制轉換為結構體類型指針( (type *)0 ),再通過這種指針得到這個結構體的 member 成員變量((type*)->member),然後通過這個變量得到它的類型(typeof(((type *)0)->member) )。

第二部:用(char *)__mptr減去member在結構體中的偏移量,得到的值就是整個結構體變量的首地址。

member成員在結構體中的偏移地址通過 offsetof 宏得到:

[code]#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
這個宏首先將零地址強制轉換為結構體類型指針((type )0),再用這個指針得到MEMBER成員(((type *0)->MEMBER)),然後獲取這個成員的地址(&(&((TYPE )0)->MEMBER)),最後將這個地址強轉為size_t類型。因為這時候結構體首地址為0,所以成員變量的地址就是在結構體中斷地址偏移。

第三步:用大括號將兩條語句括起來,作為一個整體的語句塊,在這個語句塊外面在包一層小括號,使這個語句塊的值可以被外部使用。

通過分析這個宏我才發現,一個語句塊也是有返回值的,還能被外部使用,例如以下語句也是可以的:

[code]int a = ({ int b = 2; int c = 3; b + c;});
總感覺為了實現container_of這個宏真是大費周章啊,用的著那麼麻煩嗎?自己寫的話一行代碼搞定:

[code]#define container_of(ptr, type, member) (type *)((char *)ptr - offsetof(type, member))
乍一看似乎沒毛病,仔細研究還是沒毛病,但Linux內核中的定義方法自然有它的道理。當傳入的指針ptr類型和結構中member成員類型不一樣時,內核中的定義方法編譯時會有一個指針類型不匹配的警告,而自己的定義方法悄無聲息的編譯通過了。不得不說,Linux開發人員考慮的真周到。

流雲非晚

Copyright © Linux教程網 All Rights Reserved