container_of
巨集被定義在 <linux/kernel.h> 中。 container_of
需要使用到 offsetof
巨集幫忙計算結構 (struct) 起始位址到結構成員 (member) 間的偏移量 (offset)。只要知道結構中任一成員的偏移量就可以計算出以該結構為型別 (type) 之物件在記憶體中的起始位址。
關於 offsetof
的相關說明可以參考 Linux Kernel 中的 offset 巨集 。
首先,瞭解一下 typeof
的作用。 typeof
是 gcc
的 extension 。 typeof
主要功能是讓編譯器在編譯時期推算運算式 (expression) 之結果的型別。
寫個小程式來驗證一下。
將上述程式編譯並執行後,可得下列結果。
由 C99 spec 可知,一個 int
物件與一個 long
物件相加所得結果之型別為 long
。
C99 spec, 6.3.1.8 Usual arithmetic conversions
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.
第 6 行將 int
變數 a
初始化為 INT_MAX
(即 2147483647
) 。
第 7 行將 long
變數 b
初始化為 1
。
所以第 9 行的運算式 a + b
的結果並沒有產生溢位 (overflow) 。其結果為 2147483648
。且由 a + b
運算結果的大小為 8 bytes 可知, typeof(a + b)
推得 a + b
的型別為 long
。
另外,由於變數 a
與 c
的型別皆為 int
,所以運算式 a + c
的型別也是 int
。因此, a + c
會產生溢位,其結果為 -2147483648
。
container_of
巨集的作用(type *)0)->member
可以找到結構中的 member
成員。因此, typeof( ((type *)0)->member )
就會推得 member
的型別。假設 member
的型別為 int
,則 __mptr
的型別就是 int *
(pointer to int
) 。
因為__mptr
是物件中 member
的記憶體位址,所以 (char *)__mptr - offsetof(type,member)
的運算結果就是以 type
為型別之物件在記憶體中的起始位址。
由 Linux Kernel 中的 offset 巨集 中的討論可知, offsetof(type,member)
的作用是計以 type
為型別之算物件的起始位址到 memeber
間的偏移量。所以, container_of
巨集就是透過物件的某個成員在記憶體中的位址 (ptr
) ,結構的名稱(就是物件的型別, type
) 以及該成員的名稱 (member
) 計算出這個以 type
為型別之物件在記憶體中的起始位址。
container_of
巨集的另一種實作下列程式碼片段是 container_of
巨集的另一種實作。與原本的實作有相同的效果。