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链表。
文章选自华清远见嵌入式培训
>>>更多优秀技术博文每日更新
看一个内核非常经典的实现--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链表。
文章选自华清远见嵌入式培训
>>>更多优秀技术博文每日更新
相关文章推荐
- container_of的实现分析
- container_of分析
- 内核:offsetof + container_of 分析
- Linux内核 container_of 宏和 offsetof 宏分析
- container_of使用分析
- container_of 宏的具体分析
- container_of分析
- 关于container_of和list_for_each_entry 及其相关函数的分析
- 转:container_of分析 研究内核的博客
- container_of分析
- Linux内核 container_of 宏和 offsetof 宏分析
- container_of 分析
- Linux内核 container_of 宏和 offsetof 宏分析
- 对container_of(ptr,type,member)分析
- container_of分析
- linux中container_of实现分析
- container_of分析
- 对container_of(ptr,type,member)分析
- 对container_of(ptr,type,member)分析
- container_of分析