您的位置:首页 > 大数据 > 人工智能

container_of分析

2017-02-23 16:50 609 查看
作者:华清远见

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

文章选自华清远见嵌入式培训

>>>更多优秀技术博文每日更新
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息