Try   HackMD

Linux Kernel 中的 offset 巨集

說明

#define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)

offsetof 巨集被定義在 include/linux/stddef.h 中。它的主要功能是用來計算某一個結構的成員距離該結構之起始位址的偏移量 (offset) 。其中 TYPE 為結構的名稱,MEMBER 為結構成員的名稱。
(TYPE *)0 將數值 0 強制轉型為 TYPE * 型別。 0 則被視為結構 (TYPE) 的起始位址。而 &((TYPE *)0)->MEMBER 就可以得到 MEMBER 的位址。因為起始位址已被設定為 0 ,所以 MEMBER 的位址就是結構起始點到 MEMBER 的偏移量。

範例

寫個範例研究一下 offsetof 巨集的行為。

#include <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define my_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)1000)->MEMBER)

struct test {
    int a;
    int b;
    int c;
};

int main()
{
    printf("sizeof(int): %ld\n", sizeof(int));

    printf("--- offsetof -------------\n");
    printf("-------- a: %ld\n", offsetof(struct test, a));
    printf("-------- b: %ld\n", offsetof(struct test, b));
    printf("-------- c: %ld\n", offsetof(struct test, c));

    printf("--- my_offsetof -------------\n");
    printf("-------- a: %ld\n", my_offsetof(struct test, a));
    printf("-------- b: %ld\n", my_offsetof(struct test, b));
    printf("-------- c: %ld\n", my_offsetof(struct test, c));

    return 0;
}

在範例程式中定義了 offsetofmy_offsetof 兩個巨集,並分別將起始位址設定為 01000
將程式編譯並執行後,可得下面的結果。

$ gcc test.c -o test
$ ./test 
sizeof(int): 4
--- offsetof -------------
-------- a: 0
-------- b: 4
-------- c: 8
--- my_offsetof -------------
-------- a: 1000
-------- b: 1004
-------- c: 1008

結論

因此,我們可以得知 &((TYPE *)0)->MEMBER 的功能就是在計算由結構起始位置到 MEMBER 間的偏移量。