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
參考文獻
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.