【題目】:
根據一個結構體對象某成員的地址,計算出該結構體對象的首地址。
【例如】:
struct A
{
int x;
int y;
int z;
}
#define GET_HEADER_ADDR(MEMBER) (你來實現)
void main()
{
struct A myTest;
printf("addr is %d",GET_HEADER_ADDR(myTest.z) );
}
不看下面的答案,你來嘗試實現上面這個宏吧。
【答案與分析過程】
Linux內核中,用兩個非常巧妙地宏實現了,一個是offsetof宏,另一個是container_of宏,下面講解一下這兩個宏。
1. offsetof宏
【定義】:#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
【功能】: 獲得一個結構體變量成員在此結構體中的偏移量。
【例子】:
struct A
{
int x ;
int y;
int z;
};
void main()
{
printf("the offset of z is %d",offsetof( struct A, z ) );
}
// 輸出結果為 8
【分析】:
該宏,TYPE為結構體類型,MEMBER 為結構體內的變量名。
(TYPE *)0) 是欺騙編譯器說有一個指向結構TYPE 的指針,其地址值0
(TYPE *)0)->MEMBER 是要取得結構體TYPE中成員變量MEMBER的地址. 因為基址為0,所以,這時MEMBER的地址當然就是MEMBER在TYPE中的偏移了。
2. container_of宏(即實現了題目中的功能)
【定義】:
#define container_of(ptr, type, member) ({const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
【功能】:
從結構體(type)某成員變量(member)指針(ptr)來求出該結構體(type)的首指針。
【例子】:
struct A
{
int x ;
int y;
int z;
};
struct A myTest;
int *pz = myTest.z;
struct A* getHeaderPtr( int *pz )
{
return container_of( pz , struct A, z );
}
【分析】:
(1) typeof( ( (type *)0)->member )為取出member成員的變量類型。
(2) 定義__mptr指針ptr為指向該成員變量的指針(即指向ptr所指向的變量處)
(3) (char *)__mptr - offsetof(type,member)) 用該成員變量的實際地址減去該變量在結構體中的偏移,來求出結構體起始地址。
(4) ({ })這個擴展返回程序塊中最後一個表達式的值。
本文出自 “對影成三人” 博客