天天看點

container_of分析

作者:華清遠見

看一個核心非常經典的實作--container_of

這個宏在驅動和核心代碼中用的非常廣泛,下面我們來具體分析下。

container_of作用是通過結構體某個成員位址進而拿到整個結構體位址。

原型:container_of(ptr, type, member)

示例:現有一個student結構體變量并初始化。

struct student stu;

stu.num = 1;

stu.score = 100;

strcpy(stu.name, "zhangsan");

現在我們假設有結構體某個成員的位址,比如score成員的位址也就是&stu.score,那麼怎麼拿到整個結構體的位址呢?

這就是上面宏的作用,看下每個成員分别代表什麼:

ptr:代表結構體成員的真實位址

type:結構體的類型

member:結構體成員的名字

對于上面的例子,假如我們有個函數參數需要傳遞score成員的位址,而函數内部需要列印出原結構體變量的num的值,

隻需要做如下操作:

//ptr代表score成員的位址

void func(float *ptr)

{

struct student *tmp; //定義一個結構體指針

tmp = container_of(ptr, struct student, score); //先擷取到結構體變量的位址

printf("num = %d\n", tmp->num); //列印下

}

用起來還是非常簡單的,下面看下核心經典的實作:

在中定義

#define container_of(ptr, type, member) ({ \

const typeof(((type *)0)->member) * __mptr = (ptr); \

(type *)((char *)__mptr - offsetof(type, member)); })

看起來比較複雜,我們以上面的例子對宏進行替換得到如下兩句:

const typeof(((struct student *)0)->score) * __mptr = (ptr);

(struct student *)((char *)__mptr - offsetof(struct student, score));

第一句定義了一個__mptr指針指向了ptr,也就是指向了score成員的位址,

前面的typeof(((struct student *)0)->score)作用是取得結構體中score成員的類型,屬于gcc的一個關鍵字用法。

第二句話用__mptr(score成員的位址),減去score成員在原結構體中的偏移值,就得到了原結構體變量的位址。

第二句中又牽扯到一個新的宏:offsetof,它的作用就是求某個結構體成員在結構體的偏移值。看下原型:

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

替換上面的offsetof(struct student, score)得到結果就是:

((size_t) &((struct student *)0)->score)

非常巧妙,先把0位址轉換為一個指向student的結構體類型的指針,然後取出其score成員的位址,

因為這個位址是相對于0位址的,是以本身值就代表成員的偏移量,size_t是對位址進行的強轉。

我們再回到container_of的原型,其實它的第一句話定義新的指針完全沒有必要,那麼做隻是為了規範性,

完全可以改成如下的定義,效果一樣。

#define container_of(ptr, type, member) ({ \

(type *)( (char *)ptr - offsetof(type,member) );})

也就說我們直接用score成員的位址減去它的偏移量即可,是不是好了解多了。

最後,來個完整的測試用例:

#include

#include

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

#define container_of(ptr, type, member) ({ \

(type *)( (char *)ptr - offsetof(type,member) );})

struct student{

char name[10];

int num;

};

//num隻是num成員的位址

void func(int *num_addr)

{

struct student *test;

test = container_of(num_addr, struct student, num);

printf("%s\n", test->name);

}

int main()

{

struct student stu;

strcpy(stu.name, "zhangsan");

stu.num = 3;

func(&stu.num);

}

核心的list核心連結清單最關鍵的實作就是container_of,了解上面的内容更有助于我們學習list連結清單。

文章選自華清遠見嵌入式教育訓練

>>>更多優秀技術博文每日更新

繼續閱讀