struct ABC
{
int a;
int b;
int c;
};
+----------+ <------我們需要計算的是這個位址。
| a(4Byte) |
+----------+ <------這個位址是已知的。
| b(4Byte) |
+----------+
| c(4Byte) |
通過上圖可看出,隻需要把目前知道的成員變量的位址ptr,減去它在結構體當中相對偏移量(4),就得到了結構體的首位址(ptr-4).
設計一個type類型的結構體,起始位址為0,編譯器将結構體的起始的位址加上此結構體成員變量的偏移得到此結構體成員變量的偏移位址,由于設計的結構體起始位址為0,是以此結構體成員變量的偏移位址就等于其成員變量在結構體内的距離結構體開始部分的偏移量。
Linux核心中,用兩個非常巧妙地宏實作了,一個是offsetof宏,另一個是container_of宏:
1.offsetof宏(獲得一個結構體變量成員在此結構體中的偏移量)
#define offsetof(struct_type, member_name) ((size_t) & ((struct_type *)0)->member_name )
【分析】:
(1) 該宏中,struct_type為結構體類型,member_name為結構體内的變量名
(2) ((struct_type *)0) 是欺騙編譯器說有一個指向結構struct_type 的指針,其位址值0
(3) &((struct_type *)0)->member_name 是要取得結構體struct_type中成員變量member_name的位址.
(4) 最後将其值強轉為size_t,即轉化為一個常數值,而不是将其當作位址進行使用。
因為基址為0,是以,這時member_name的位址當然就是member_name在struct_type中的偏移了。
2. container_of宏(從結構體[struct_type]某成員變量[member_name]指針[member_addr]來求出該結構體[struct_type]的首指針)
#define container_of(member_addr, struct_type, member_name) ({const typeof( ((struct_type *)0)->member_name ) *__mptr = (member_addr); (struct_type *)( (char *)__mptr - offsetof(struct_type,member_name) );})或者直接定義#define container_of(member_addr, struct_type, member_name) ((struct_type *)( (char *)member_addr - offsetof(struct_type,member_name) ))
(1)typeof( ( (struct_type *)0)->member )為取出member_name成員的變量類型。
(2) 定義__mptr指針member_addr為指向該成員變量的指針(即指向member_addr所指向的變量處)
(3) (char *)__mptr - offsetof(struct_type,member_name)) 用該成員變量的實際位址減去該變量在結構體中的偏移,來求出結構體起始位址。
(4) ({ })這個擴充傳回程式塊中最後一個表達式的值。