Linux核心中的常用巨集container_of
Container_of在Linux核心中是一個常用的巨集,用於從包含在某個結構中的指標獲得結構本身的指標,通俗地講就是通過結構體變數中某個成員的首地址進而獲得整個結構體變數的首地址。
Container_of的定義如下:
#define container_of(ptr, type, member) ({ \ const typeof( ( (type *) 0 )->member ) * __mptr = (ptr); \ (type *) (
(char *)__mptr - offsetof (type, member) ); })
typeof():得到一個變數的型別
如:下面兩種方法的定義一樣
typeof(int) a;
int a;
offsetof是巨集定義
#define offsetof(TYPE, MEMBER) ( (size_t) &( (TYPE *)0 )->MEMBER ) //&是取地址符,不是與
其實它的語法很簡單,只是一些指標的靈活應用,它分兩步:
第一步,首先定義一個臨時的資料型別(通過typeof( ((type *)0)->member )獲得)與ptr相同的指標變數__mptr,然後用它來儲存ptr的值。
第二步,用(char *)__mptr減去member在結構體中的偏移量,得到的值就是整個結構體變數的首地址(整個巨集的返回值就是這個首地址)。
其中的語法難點就是如何得出成員相對結構體的偏移量?
其中程式碼難以理解的地方就是它靈活地運用了0地址。如果覺得&( (struct test_struct *)0 )->ch這樣的程式碼不好理解,那麼我們可以假設在0地址分配了一個結構體變數struct test_struct a,然後定義結構體指標變數p並指向a(struct test_struct *p = &a),如此我們就可以通過&p->ch獲得成員ch的地址。由於a的首地址為0x0,所以成員ch的首地址為0x4。
例項程式碼:
#include <stdio.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
struct test_struct {
int num;
char ch;
float fl;
};
int main(void)
{
struct test_struct my_test = {1, '2', 3};
printf("&my_test.num = [%ld]\n", &my_test.num);
printf("&my_test.ch = [%ld]\n", &my_test.ch);
printf("&my_test.fl = [%ld]\n", &my_test.fl);
printf("container_of(struct test_struct, num) = %ld\n", container_of(&my_test.num, struct test_struct, num));
printf("container_of(struct test_struct, ch) = %ld\n", container_of(&my_test.ch, struct test_struct, ch));
printf("container_of(struct test_struct, fl) = %ld\n", container_of(&my_test.fl, struct test_struct, fl));
return 0;
}
執行結果:
&my_test.num = [140733208695616] &my_test.ch = [140733208695620] &my_test.fl = [140733208695624] //雖然ch位字元型,但還是分配了4個位元組 container_of(struct test_struct, num) = 140733208695616 container_of(struct test_struct, ch) = 140733208695616 container_of(struct test_struct, fl) = 140733208695616