当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > container_of分析

container_of分析 时间:2018-09-27      来源:未知

看一个内核非常经典的实现--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链表。

上一篇:C++中placement new和delete讨论

下一篇:变量的存储类型

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部