linux內核中offsetof與container_of的宏定義linux
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)函數
/**
* 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) );})this
offsetof含義:獲取結構體中某個成員變量相對於結構體首地址的內存位置偏移;spa
container_of含義:根據結構體中某個成員變量的內存地址獲取結構體的內存首地址。指針
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)內存
1)、參數TYPE爲結構體的類型定義,MEMBER爲結構體成員變量名稱;編譯器
2)、實現方法比較取巧:將0強制轉化TYPE類型的指針,而後對MEMBER變量取址;it
3)、代碼示例:編譯
typedef structast
{
int a;
char b;
void *c;
} off_struct_s;
printf("offset a:%d b:%d c:%d\n", offsetof(off_strcut_s, a), offsetof(off_strcut_s, b), offsetof(off_strcut_s, c));
最終結果爲:offset a:0 b:4 c:8
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
1)、參數ptr爲結構體成員的內存地址,type爲結構體類型定義,member爲結構體成員變量名稱;
2)、typeof爲gun編譯器系列的內置函數,函數返回當前變量的類型;
3)、const typeof( ((type *)0)->member ) *__mptr = (ptr); 這行定義了一個member類型的指針變量__mptr,而且值初始化爲ptr(注意:這裏type也是0強制轉換);
4)、(type *)( (char *)__mptr - offsetof(type,member) );
這行則是將結構體成員變量的內存地址減去此成員變量與結構體首地址的偏移(offsetof前面已講解),即爲結構體的內存首地址;
5)、代碼示例:
off_struct_s stru;
void *ptr;
ptr = (void *)(container_of(&stru.c, off_struct_s, c));
printf("stru_addr:%p container_addr:%p\n", &stru, ptr);
運行結果必然是&stru與ptr的內存地址是同樣的,這個例子只是作一個證實,實際使用時確定是先不知道結構體首地址的,須要由結構體變量地址計算結構體首地址。
其實總結起來很簡單,要想根據一個結構體變量的指針計算出結構體自己的指針,只須要當前變量指針減去變量到結構體首的偏移(base = ptr - offset)。