詳解兩個重要宏offsetof和containe…

本文解析關於結構體的兩個重要宏offsetof( TYPE , MEMBER)和container_of(ptr , type , member)

實例1、#define offsetof( TYPE , MEMBER) (int *)&((TYPE *)0)->MEMBER

(備註:    ->的優先級比&高)
參數說明:TYPE爲結構體變量類型;MEMBER爲結構體中任意一個元素
作       用:得出該MEMBER元素相對於結構體首地址的偏移量
分層解析:
    (1) ((TYPE*)0):將0強制轉換成指向TYPE類型結構體變量的指針,該結構體變量地址被強制變爲0x0;
    (2) ((TYPE*)0)->MEMBER:指向結構體中的MEMBER元素;
    (3) (int *)&((TYPE *)0)->MEMBER:取MEMBER元素的地址並強制轉換成指向int類型數據的指針。
實       質:將結構體變量地址強制變成0x0,當得出其中某個元素地址後,由於結構體內元素地址是邏輯連續的,該元素地址被強制轉換成(int *)後即可被理解爲偏移量。(見代碼示例1)

實例2、#define container_of(ptr , type , member) ({  const typeof(((type *)0)->member) *__mptr = (ptr) ; (type *)((char *)__mptr - offsetof(type , member ) );   })

參數說明:ptr爲指向member元素的指針;type爲結構體變量類型;member爲結構體中任意一個元素
作       用:通過結構體中某一元素的地址得出結構體(變量)首(的)地址;
分層解析:
    (1) typeof(((type *)0)->member)得出member元素的數據類型;
    (2) const typeof(((type *)0)->member) *__mptr定義一個和member同數據類型的指針,指針指向常量;
    (3) __mptr = (ptr) 將指向member元素的指針賦給新定義的__mptr指針;
    (4) (char *)__mptr - offsetof(type , member )該元素地址減去自身相對結構體地址偏移量得出初始地址;
    (5) (type *)((char *)__mptr - offsetof(type , member ) )將初始地址強制轉換成結構體變量地址。
實       質:當傳參數ptr指針時,編譯器只傳ptr的值,並沒傳它的數據類型,所以需要通過typeof()函數獲取它的類型;得出member元素的地址和類型後,減去它的偏移量,即可得出初始地址,即爲結構體首地址。(見代碼示例2)

代碼示例(1)
#include
#define offset(TYPE,MEMBER) ((int)&(((TYPE *)0)->MEMBER))
struct mystruct
{
char a;
};
int main ( )
{
struct  mystruct s1;
int offsetof_a = offset(struct mystruct,a);
printf("offsetof_a = %d\n",offsetof_a);
}

代碼示例(2)
#include
#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 mystruct
{
char a;
short c;
};
int main ()
{
struct mystruct s1;
struct mystruct *p = NULL;
short *p1 = &s1.c;
printf("s1的指針 = %p\n",&s1);   //此句可得到結構體首地址,前提是定義了變量s1;

//但在Linux內核中,往往不知道結構體變量的,那該如何得到呢?請看下面
//現在要通過p來計算得到s1的指針
p1 = container_of(p,struct mystruct,c);
printf("ps的指針 = %p\n",p1);   //地址值和&s1一樣
}


(原創,如轉載,請說明本文出處)






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章