咱們在書寫C程序的時候,有時候須要根據結構體成員變量的地址,獲得結構體的地址,特別是咱們想用C來實現C++的繼承特性的時候。
咱們對問題的分析以下:linux
爲了便於分析,咱們給出一個實例來講明函數
struct father_t { int a; char *b; double c; }f; char *ptr = &(f.b); //而不是 ptr = f.b; 這裏ptr是b的地址,而不是它指向的地址。
根據C語言對struct類型的存儲特性,咱們能夠畫這麼一個圖示:
經過分析圖示,咱們能夠看出,咱們只須要把當前知道的成員變量的地址ptr,減去它在結構體當中相對偏移4就的到告終構體的地址(ptr-4)。
在linux當中對此有一個很好的宏可使用,叫作 container_of, 放在 linux/kernel.h(http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/spa
)當中。它的定義以下所示:設計
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
宏功能:得到一個結構體變量成員在此結構體中的偏移量。指針
1. ( (TYPE *)0 ) 將零轉型爲TYPE類型指針;
2. ((TYPE *)0)->MEMBER 訪問結構中的數據成員;code
3. &( ( (TYPE *)0 )->MEMBER )取出數據成員的地址,即相對於0的偏移量,要的就這個;
4.(size_t)(&(((TYPE*)0)->MEMBER))結果轉換類型,size_t應該最終爲unsigned int類型。
此宏的巧妙之處在於將 0 轉換成(TYPE*),這樣結構體中成員的地址即爲在此結構體中的偏移量。blog
示例:繼承
#include <stdio.h> #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER)) struct _test_ { char x; int y; float z; }; int main(void) { int temp = -1; temp = offsetof(struct _test_, z); printf("temp = %d\n", temp); return 0; }
運行後結構爲:temp = 8。 顯然求出了 結構體成員變量 z 在結構體中的偏移量爲 8。
若是是
這個技巧在linux內核裏面很是常見,經過常量 地址的強轉獲得一個數據類型編譯器
若是是char x,輸出0;若是是y,輸出4.io
若是不要
(int) 或(size_t)會怎麼樣?
編譯不經過,沒法將float *類型 轉成float?
由於
&((TYPE *)0)->MEMBER) 獲得的是一個指針,不能直接賦值給int 類型。
必須先用(int)強制轉化下。
還有一個與這個有關的宏:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
宏功能:從結構體(type)某成員變量(member)指針(ptr)來求出該結構體(type)的首指針。
對上面的定義,分析以下:
這就是從結構體某成員變量指針來求出該結構體的首指針。指針類型從結構體某成員變量類型轉換爲該結構體類型。
#include <stdio.h> #define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER)) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct _test_ { int x; int y; int z; }; void Assignment(struct _test_ *t) { t->x = 1; t->y = 2; t->z = 3; } void GetheadPoint(int *tz) { struct _test_ *p; int temp = -1; p = container_of(tz,struct _test_, z); //根據成員變量的地址得到該結構體的首地址 temp = p->y; //根據首地址得到其中另一個成員變量的值 printf("line31 = %d\n", temp); } int main(void) { int temp = -1; struct _test_ tmp; //定義一個結構體變量 Assignment(&tmp); //給這個變量賦值 GetheadPoint(&tmp.z); //只傳給這個函數一個結構體成員變量的地址 printf("line43 tmp - >x = %d\n", tmp.x); return 0; } 運行結果爲: line31 = 2 line43 tmp - >x = 1