結構體之offsetof宏詳細解析


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

1.1 功能:

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

1.2 解析:

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

首先,(TYPE *)0表示將數字0強制類型轉換為TYPE類型(TYPE為結構體類型)的指針。因此這里的0代表內存地址0,即我們認為內存地址0開始的sizeof(TYPE)個字節內存儲的是一個TYPE類型的變量。

然后,((TYPE *)0)->MEMBER 得到該結構體變量中的MEMBER成員變量,

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

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

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之前的成員,然后再次匯編,對比匯編后代碼的不同,以此來驗證我們的推論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM