結構體之offsetof宏詳細解析

一、#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)     (include/linux/stddef.h)linux

1.1 功能:ide

返回結構體TYPE中MEMBER成員相對於結構體首地址的偏移量,以字節爲單位。函數

1.2 解析:指針

此類複雜表達式的解析應該採用從內向外、逐層理解的方式。ip

首先,(TYPE *)0表示將數字0強制類型轉換爲TYPE類型(TYPE爲結構體類型)的指針。所以這裏的0表明內存地址0,即咱們認爲內存地址0開始的sizeof(TYPE)個字節內存儲的是一個TYPE類型的變量。內存

而後,((TYPE *)0)->MEMBER 獲得該結構體變量中的MEMBER成員變量,get

而 &(((TYPE*)0)->MEMBER) 使用取地址符&取得了MEMBER成員變量的地址,(size_t)加在前面表示將MEMBER成員變量的地址強制類型轉換爲size_t(即unsigned int),並將結果做爲宏的返回值。編譯器

可見,offsetof宏返回的是MEMBER成員在內存中的實際地址。又由於整個結構體的起始地址是0,所以MEMBER成員的實際地址在數值上就等於MEMBER成員相對於結構體首地址的偏移量。io

1.3 擴展思考:編譯

1.3.1 使用offsetof宏會影響內存0地址處的值嗎?

答案是不會,從1.3.2可知offsetof宏的運算是在C編譯器編譯時完成的,所以內存的0地址在機器指令中根本未被操做,固然不會影響其值了。

1.3.2offsetof宏返回的MEMBER相對於結構體首地址的偏移量是如何獲得的?->符號如何能正確尋址到結構體中某個成員變量?

想探究struct如何經過->精確尋址每個成員,最好的辦法就是將C代碼彙編爲.S的彙編語言代碼,經過觀察彙編代碼能夠看到C編譯器對代碼處理的具體細節。咱們的示例代碼以下:

 

#include"stdio.h"

 

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

 

typedefstruct st

{

    int a;

    int c;     //將該行加上或去掉,對比獲得的彙編代碼的差異

    short d;   //將該行加上或去掉,對比獲得的彙編代碼的差異

    char b;

}st;

 

intgetoffsetof(void)

{

    return myoffsetof(struct st, b);

}

將以上代碼保存爲offsetof.c,而且使用arm-linux-gcc offsetof.c –S執行彙編,則會獲得offsetof.s文件,內容以下:

        .file  "offsetof.c"

        .text

        .align 2

        .global getoffsetof

        .type  getoffsetof, %function

getoffsetof:

        @ Function supports interworking.

        @ args = 0, pretend = 0, frame = 0

        @ frame_needed = 1, uses_anonymous_args= 0

        mov    ip, sp        // 這三行

        stmfd  sp!, {fp, ip, lr, pc}   // 是函數

        sub    fp, ip, #4           // 棧幀保存

        mov    r3, #10          // #10便是offsetof宏計算獲得的值

        mov    r0, r3        // 將返回值置於R0中

        sub    sp, fp, #12      // 函數棧幀

        ldmfd  sp, {fp, sp, lr}    // 恢復

        bx     lr            // 函數返回

        .size  getoffsetof, .-getoffsetof

        .ident "GCC: (GNU) 4.1.2"

以上彙編代碼中mov r3, #10一句能夠看出,offsetof宏計算member的偏移量是C編譯器在編譯階段完成的,而並不須要CPU在運行時去計算得出。

能夠嘗試着更改struct st中成員b以前的成員,而後再次彙編,對比彙編後代碼的不一樣,以此來驗證咱們的推論。

相關文章
相關標籤/搜索