offsetof巨集:結構體成員相對結構體的偏移位置 container_of:根據結構體成員的地址來獲取結構體的地址
offsetof 巨集
原型:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
複製程式碼
(TYPE *)0
非常巧妙,告訴編譯器有一個指向結構體 TYPE 的指標,其地址是0,然後取該指標的 MEMBER 地址 &((TYPE *)0)->MEMBER
,因為基址是0,所以這時獲取到的 MEMBER 的地址就是相當於在結構體 TYPE 中的偏移量了。
Example:
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
struct TYPE{
int mem;
int member;
};
int main()
{
struct TYPE type;
printf("&type = %p\n", &type);
printf("&type.member = %p\n", &type.member);
printf("&((struct type *)0)->member = %lu\n", ((size_t)&((struct TYPE *)0)->member) );
printf("offsetof(struct TYPE member) = %zd\n", offsetof(struct TYPE, member));
return 0;
}
/*
result:
&type = 0x7ffc1104a110
&type.member = 0x7ffc1104a114
&((struct type *)0)->member = 4
offsetof(struct TYPE member) = 4
*/
複製程式碼
container_of 巨集
原型:linux-4.18.5
/**
* 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) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
複製程式碼
網上所見更多是底下這個版本:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
複製程式碼
第一部分:void *__mptr = (void *)(ptr);
或const typeof( ((type *)0)->member ) *__mptr = (ptr);
兩個的差別在於 __mptr 的型別一個是 void * ,一個是 type *。
void * 較為容易理解,下面來看看 type *:
關於 typeof 關鍵字其作用是返回變數的型別,簡單理解就是如下,詳細可參見GCC typeof在kernel中的使用——C語言的“編譯時多型”
int a;
typeof(a) b; //這等同於int b;
typeof(&a) c; //這等同於int* c;
複製程式碼
因此const typeof( ((type *)0)->member ) *__mptr = (ptr);
的作用就是通過 typeof 獲取結構體成員 member 的型別,然後定義一個這個型別的指標變數 __mptr 並將其賦值為 ptr。
第二部分:(type *)( (char *)__mptr - offsetof(type,member) )
,通過offsetof
巨集計算出 member 在 type 中的偏移,然後用 member 的實際地址__mptr
減去偏移,得到 type 的起始地址。從上面關於offsetof
巨集的 Example 也可以驗證這一點:
&type.member = 0x7ffc1104a114 - &((struct type *)0)->member = 4 = &type = 0x7ffc1104a110