C語言offsetof()巨集,是定義在stddef.h。用於求出一個structunion資料類型的給定成員的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++的classstruct也適用。[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.