轉載至:
http://www.runoob.com/cprogra...
https://blog.csdn.net/encoura...html
C 庫宏 offsetof(type, member-designator) 會生成一個類型爲 size_t 的整型常量,它是一個結構成員相對於結構開頭的字節偏移量。成員是由 member-designator 給定的,結構的名稱是在 type 中給定的。佈局
下面是 offsetof() 宏的聲明。.net
offsetof(type, member-designator)
該宏返回類型爲 size_t 的值,表示 type 中成員的偏移量。指針
下面的實例演示了 offsetof() 宏的用法。code
#include <stddef.h> #include <stdio.h> struct address { char name[50]; char street[50]; int phone; }; int main() { printf("address 結構中的 name 偏移 = %d 字節。\n", offsetof(struct address, name)); printf("address 結構中的 street 偏移 = %d 字節。\n", offsetof(struct address, street)); printf("address 結構中的 phone 偏移 = %d 字節。\n", offsetof(struct address, phone)); return(0); }
讓咱們編譯並運行上面的程序,這將產生如下結果:htm
address 結構中的 name 偏移 = 0 字節。 address 結構中的 street 偏移 = 50 字節。 address 結構中的 phone 偏移 = 100 字節。
寫一個宏計算出結構體成員的偏移量。
假設有以下一個結構體,要計算成員c的在結構體中的偏移量。blog
typedef struct Type_t{ char a; // 0 int b; // 4~7 double c; // 8~16 };
注意,上述的結構體必須考慮字節對齊的問題。內存
咱們能夠聲明一個Type_t結構的變量type,而後將成員c的地址減去成員a的地址就是c的偏移量了。get
Type_t type; offset = (unsigned long)(&(type.c)) - (unsigned long)(&(type.a)); // 其中,(&(type.a)) 與 (&(type))是等價的
將上面整理爲宏就是:編譯器
#define OFFSET(TYPE, MEMBER, OFF) \ TYPE temp; \ OFF = (unsigned long)(&(temp.MEMBER)) - (unsigned long)(&(temp));
這個宏建立了一個類型爲TYPE的臨時變量temp,而後求出MEMBER成員的偏移量放在OFF裏。
完整代碼以下:
#include <stdio.h> #include <string.h> #define OFFSET(TYPE, MEMBER, OFF) \ TYPE temp; \ OFF = (unsigned long)(&(temp.MEMBER)) - (unsigned long)(&(temp)); typedef struct Type_t{ char a; int b; double c; }; int main(void) { int offset = 0; Type_t type; offset = (unsigned long)(&(type.c)) - (unsigned long)(&(type)); OFFSET(Type_t, c, offset); printf("offset = %d\n", offset); getchar(); }
若是可以讓(unsigned long)(&(type))的值爲0,即&(type) == 0的時候,那麼offset的值就是簡單的:
offset = (unsigned long)(&(type.c));
若是說&(type) == 0,那麼type.c就能夠等價於((Type_t *)0)->c。可是這個語句是不能單獨存在的,由於對NULL指針訪問成員c是非法的。能夠經過在該語句以前加上&符號,即獲取成員c的地址就沒問題了。所以,對應的宏以下:
#define OFFSET(TYPE, MEMBER) ((unsigned long)(&(((TYPE *)0)->MEMBER)))
ANSI C標準容許任何值爲0的常量被強制轉換成任何一種類型的指針,而且轉換結果是一個NULL指針,所以((Type_t)0)的結果就是一個類型爲Type_t的NULL指針。若是利用這個NULL指針來訪問Type_t的成員固然是非法的,但&(((Type_t)0)->c)的意圖並不是想存取c字段內容,而僅僅是計算當結構體實例的首址爲((Type_t)0)時c字段的地址。聰明的編譯器根本就不生成訪問m的代碼,而僅僅是根據Type_t的內存佈局和結構體實例首址在編譯期計算這個(常量)地址,這樣就徹底避免了經過NULL指針訪問內存的問題。