文章轉自點擊開啟連結
Content
0. 引子
1. 舉例
(1) 代碼
(2) 檢查結果
(3) 為什麼從0開始。
(4) 從非0地址開始的結果
2. 小結
0. 引子
在linux-2.26.23版的核心代碼中,./include/linux/stddef.h檔案中有如下定義。
00020: #undef offsetof00021: #ifdef compiler_offsetof00022: #define offsetof(TYPE,MEMBER) compiler_offsetof(TYPE,MEMBER)00023: #else00024: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)00025: #endif00026: #endif /* KERNEL */00027:00028: #endif
compiler_offsetof不是筆者關心的內容,第二個宏定義即為本文討論的方法。
這個宏定義很好解釋:將0地址強制轉換為TYPE類型指標,並取得MEMBER,然後擷取該MEMBER地址,最後將該地址強制轉換為表示大小的整數。該整數即為該成員的位移量。
1. 舉例
(1) 代碼
00001: / **00002: * test the offset of a member of a struct00003: */00004:00005: #include <stdio.h>00006:00007: typedef struct00008: {00009: int x;00010: int y;00011: int z;00012: } Point;00013:00014: //#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)- >MEMBER)00015: #define offsetof(TYPE, MEMBER) ((size_t) &(((TYPE*)0)- >MEMBER))00016:00017: void test1()00018: {00019: int x = (size_t) &((Point*)0)- >x;00020: int y = (size_t) &((Point*)0)- >y;00021: int z = (size_t) &(((Point*)0)- >z);00022: printf("offset: x = %d, y = %d, z = %d\n", x, y, z);00023: }00024:00025: void test2()00026: {00027: printf("Point.x offset = %d\n", offsetof(Point, x));00028: printf("Point.y offset = %d\n", offsetof(Point, y));00029: printf("Point.z offset = %d\n", offsetof(Point, z));00030: }00031:00032: int main(int argc, char** argv)00033: {00034: test1();00035: printf("\n");00036: test2();00037: return 0;00038: }00039:
在該例子中,筆者借用Linux核心的方法,兩種方式實現
(2) 檢查結果
# ./offsetofoffset: x = 0, y = 4, z = 8Point.x offset = 0Point.y offset = 4Point.z offset = 8
無需說明,這個結果是正確的。
(3) 為什麼從0開始。
從第1節敘述的該方法的思想中,可以看出,從0開始的目的是不需要在獲得某個成員的地址後減去結構體的起始地址。
下面我們看看從非0開始的結果。
(4) 從非0地址開始的結果
修改test1()函數,如下。
00017: void test1()00018: {00019: int x = (size_t) &((Point*)1000)- >x;00020: int y = (size_t) &((Point*)1000)- >y;00021: int z = (size_t) &(((Point*)1000)- >z);00022: printf("offset: x = %d, y = %d, z = %d\n", x, y, z);00023: }
結果如下。
# ./offsetofoffset: x = 1000, y = 1004, z = 1008Point.x offset = 0Point.y offset = 4Point.z offset = 8
可以看出,如果從非0開始,實際上獲得的是該成員的實際地址(當然,該實際地址是相對於給定的起始地址來說的,並非真實的記憶體位址)。該實際地址減去給定的起始地址後也可得成員的位移量。
2. 小結
本文借用Linux核心的方法,舉例敘述了擷取結構體成員位移量的方法。Linux核心中還有N多好的結構和演算法,筆者以後慢慢講述。
遇到問題要思考,且是深入思考。學習核心要學習其設計思想和方法,記錄是要整理自己的思路,以備後忘。
去年和最近面試過很多人,問到這個題目,但極少人能答上來,這種問題可是基本功啊。還不知道如何擷取結構體成員位移量的同學,敲打鍵盤,試一下吧。:)