1. 程式人生 > >詳細解析:如何確定域在結構中的位元組偏移的 C語言的程式碼的實現

詳細解析:如何確定域在結構中的位元組偏移的 C語言的程式碼的實現

     如何確定域在結構中的位元組偏移?

ANSI C 在 <stddef.h> 中定義了 offsetof() 巨集, 用 offsetof(struct s, f) 可以計算出域 f 在結構 s 中的偏移量。如果出於某種原因, 你需要自己實現這個功能, 可以使用下邊這樣的程式碼:

         #define offsetof(type,f) ( (size_t) ( (char*)&((type*)0)->f - (char*)(type*)0) )

    這種實現不是 100% 的可移植; 某些編譯器可能會合法地拒絕接受。

    注:上述文字來源於書籍《你必須知道的495個C語言問題》。

    上述程式碼對於大神來說可能一眼或者兩眼就能看懂,但是對於C語言還是像我一樣還在懵逼狀態的小白來說就同樣懵逼,精闢的程式碼沒有解釋,讓小白們體會不到C語言的強大之處,遍尋(稍微翻了翻)Internet無果後,我決定自己用自己的理解來解釋一下,還是不理解的下方評論留言。

    理清問題,要用拆的思想,由區域性到整體,由內而外。(裝逼的方法論。。。。)

    首先這句話的意思是將

        ( (sie_t) ( (char*)&((type*)0)->f - (char*)(type*)0) )

    用巨集定義#define功能實現用

        offsetof(type,f)

    來代替。 也就是我們呼叫 offsetof(type, f) 就是使用了 ((size_t) ((char *)&((type *)0)->f - (char *)(type *)0)) 這句精妙的語句。實現的功能就是上文所說的可以計算出域 f 在結構 s 中的偏移量(這裡偏移量的單位是位元組

)。我們下面來解釋這段程式碼。

1. (type*)0    //將0強制轉換為指向type型結構體的指標,即假裝地址0處有一個type的結構
2. (char*)(type*)0)  //將1.處的結構體指標強制轉換成字元型指標,這樣做的目的是為了使指標(也是一種地址)做減法時得出的偏移量的單位是位元組型
3. ((type*)0)->f    //這段程式碼的結果是得到地址在0處的type結構體的成員f的值
4. &((type*)0)->f   //在3.的基礎上加上了取地址符&,得到的是地址在0處的type結構體的成員f的值的地址
5. (char*)&((type*)0)->f  //在4.的基礎上加上(char*) ,目的同2.處
6. (char*)&((type*)0)->f - (char*)(type*)0)   // 這裡其實是上面的5. -2.(結構體成員地址 - 結構體基地址),
                                            //得到的就是我們所求的偏移量(以位元組為單位)
7. 在6. 整體上又加了強制型別轉換(size_t),目的是為了使結果為無符號整數。 

補充一下size_t的來源。

百度百科:

    size_t是標準C庫中定義的,應為unsigned int,在64位系統中為 long unsigned int。

一個基本的無符號整數的C / C + +型別, 它是sizeof操作符返回的結果型別, 該型別的大小可選擇。因此,它可以儲存在理論上是可能的任何型別的陣列的最大大小。 換句話說,一個指標可以被安全地放進為size_t型別(一個例外是類的函式指標,但是這是一個特殊的情況下)。 size_t型別通常用於迴圈、陣列索引、大小的儲存和地址運算。 雖然size_t可以儲存一個指標,它的目的是更好地使用另一個unsinged整數型別uintptr_t形式。 在某些情況下,使用size_t型別是更為有效,比習慣性使用無符號型別的程式會更安全。size_t是在基於無符號整數memsize型別的C / C + +的標準庫中定義的。 C語言中,此型別位於標頭檔案stddef.h中,而在C++中,則位於cstddef中。