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開發人員考慮的真周到。
流雲非晚