container_of宏深究

在Linux內核編程中廣泛使用了container_of宏,有必要對該宏的使用及實現做一個詳細的分析。

1,作用

原型container_of(ptr,type,member)。ptr是指向類型爲type的結構體中member元素的指針,該宏最終返回類型爲type的結構體的指針。
舉個例子,

 struct my_struct{
     int i;
     int y;
 };
struct my_struct A;
//這裏假設知道A中y變量的地址,求A的地址
int *ptr = &(A.y);
container_of(ptr,struct my_struct,y) //返回A的地址。

2,實現原理

1  #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
2  #define container_of(ptr, type, member) ({          \
3      const typeof(((type *)0)->member) * __mptr = (ptr); \
4      (type *)((char *)__mptr - offsetof(type, member)); })
 網上對這部分的分析已經比較多了,這裏不太詳述。大體是offsetof計算出member變量在type中的偏移量,container_of在ptr的基礎上減去偏移量獲得整個結構的基地址。

3,若干問題
  1,上述3行似乎無用,定義如下似乎也可以

 #define container_of(ptr, type, member) ({          \
             (type *)((char *)ptr - offsetof(type, member)); })

    答曰:其實正常輸入來說,上述代碼同樣可以工作,第3行的主要作用在於類型檢測,阻止參數輸入錯誤的情況。這裏舉個例子來說明

#include <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({          \
     const typeof(((type *)0)->member) * __mptr = (ptr); \
     (type *)((char *)__mptr - offsetof(type, member)); })
#endif

#define container_of2(ptr, type, member) ({          \
     (type *)((char *)ptr - offsetof(type, member)); })
 
struct my{ 
    int i;
    int y;
};
char k=0;

int main(){
  struct my qq; 
  struct my *qqp;

  qq.y=8;
  printf("%p %p\n",&(qq.y),&qq);

  qqp=container_of(&(qq.y),struct my,y); //OK
  qqp=container_of2(&(qq.y),struct my,y);//OK

 qqp=container_of(&k,struct my,y);  //這裏會因爲參數不匹配而報錯
  qqp=container_of2(&k,struct my,y);  //沒有報錯,得到了一個錯誤的結果
  return 0;
}

 2 offsetof 中的地址0很容易理解,這樣可以很容易的計算出偏移量。但是第3行中的地址0怎麼理解呢?
      答:由於使用了typeof,這裏僅僅時取得數據類型。typeof是GCC的擴展。關於typeof的使用,舉個例子
        int *p;
         typeof(*p) =  int
         於是typeof(((type *)0)->member)就是得到了type中member元素的類型。於是我的理解就是這裏僅僅是爲了取得數據類型,於是0的取值不重要,我改成了100,1000完全沒有影響。使用0估計是因爲用習慣了,總得有個數,要是隨便寫的話,也許引起的誤解會更多。自己的想法,和實驗的結果,歡迎指正。


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