offsetof
C語言的offsetof()巨集,是定義在stddef.h。用於求出一個struct或union資料類型的給定成員的size_t類型的位元組偏移值。offsetof()巨集有兩個參數,分別是結構名與結構內的成員名。不能聲明為C原型。[1]
實現
傳統實現依賴於編譯器對指標不是很挑剔。它假定結構的位址為0,然後獲得成員的偏移值:
#define offsetof(st, m) ((size_t)&(((st *)0)->m))
上述定義在C11語言標準下是未定義行為,[2] 因為它對空指標做了解除參照(dereference)。GCC現在定義該巨集為:[3]
#define offsetof(st, m) __builtin_offsetof(st, m)
這種內建對C++的class或struct也適用。[4]
用途
Linux核心使用offsetof()來實現container_of(),這允許類似於mixin類型以發現包含它的結構:[5]
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of()巨集被用於從指向內嵌成員的指標獲得外包的結構的指標。如鏈結串列my_struct:
struct my_struct {
const char *name;
struct list_node list;
};
extern struct list_node * list_next(struct list_node *);
struct list_node *current = /* ... */
while(current != NULL){
struct my_struct *element = container_of(current, struct my_struct, list);
printf("%s\n", element->name);
current = list_next(&element->list);
}
Linux核心實現container_of()時,使用了GNU C擴充statement expressions.[6]下述實現也能確保類型安全:
#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))
粗看起來,上述的不尋常的?:條件運算子是不適當的。可以寫成更簡單的形式:
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
這種寫法忽略了檢查ptr的類型是否是member的類型,而Linux核心的實現需要這種安全檢查。而?:條件運算子要求如果運算元是一個類型的兩個指標值,那麼它們應當是相容的類型。所以第三個運算元雖然不會被使用,但編譯器要檢查(ptr)
與&((type *)0)->member
是不是相容的指標類型。
局限性
C++03要求offsetof限於POD類型。C++11要求offsetof限於標準布局類型,[7] 但仍存在未定義行為。特別是虛繼承情形。[8] 下述代碼用gcc 4.7.3 amd64編譯器,產生的結果是有問題的:
#include <stddef.h>
#include <stdio.h>
struct A
{
int a;
virtual void dummy() {}
};
struct B: public virtual A
{
int b;
};
int main()
{
printf("offsetof(A,a) : %zu\n", offsetof(A, a));
printf("offsetof(B,b) : %zu\n", offsetof(B, b));
return 0;
}
Output is:
offsetof(A,a) : 8 offsetof(B,b) : 8
參考文獻
- ^ offsetof reference. MSDN. [2010-09-19]. (原始內容存檔於2011-10-10).
- ^ Does &((struct name *)NULL -> b) cause undefined behaviour in C11?. [2015-02-07]. (原始內容存檔於2015-02-07).
- ^ GCC offsetof reference. Free Software Foundation. [2010-09-19]. (原始內容存檔於2010-07-24).
- ^ what is the purpose and return type of the __builtin_offsetof operator?. [2012-10-20]. (原始內容存檔於2014-12-15).
- ^ Greg Kroah-Hartman. container_of(). Linux Journal. June 2003 [2010-09-19]. (原始內容存檔於2010-02-13).
- ^ Statements and Declarations in Expressions. Free Software Foundation. [2016-01-01]. (原始內容存檔於2016-01-05).
- ^ offsetof reference. cplusplus.com. [2016-04-01]. (原始內容存檔於2016-03-30).
- ^ Steve Jessop. Why can't you use offsetof on non-POD structures in C. Stack Overflow. July 2009 [2016-04-01]. (原始內容存檔於2019-10-19).