@[toc]
前言
在Linux内核中经常会见到container_of这个宏,通过某一个成员变量的地址获取这个结构体的地址,比如在一个工作队列中获取一个结构体的地址,watchdog_work为plat_data的一个成员变量
static void work_func(struct work_struct *work)
{
struct plat_data *data = container_of(work, struct plat_data, watchdog_work);
}
结构体变量在结构体的地址偏移
从结构体首地址开始,每一个结构体变量对于结构体的首地址有一个固定的地址偏移,当结构体的首地址为0时,此时结构体中各个成员变量的地址就是相对于结构体首地址的偏移 代码示例:
#include <stdio.h>
#include <stdlib.h>
struct s{
int age;
int id;
char *name;
};
int main()
{
//地址
printf("&s =%p\n",(struct s*)0);
printf("&age =%p\n",&((struct s*)0)->age);
printf("&id =%p\n",&((struct s*)0)->id);
printf("&name=%p\n",&((struct s*)0)->name);
return 0;
}
结果:
&s =(nil)
&age =(nil)
&id =0x4
&name=0x80
container_of宏应用
在Linux内核中定义了offset用来实现这个功能,定义为
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
其中offsetof宏定义则是计算MEMBER成员变量相对于结构体变量的地址偏移值,container_of三个参数为:
- ptr:结构体内成员member的地址
- type:结构体类型
- member:结构体变量 程序示例:
#include <stdio.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct s
{
int id;
char sex;
int age;
};
int main(void)
{
struct s s1 = {4,'S',6};
printf("&s1 = %p\n", &s1);
printf("&s1.sex = %p\n", &s1.sex);
printf("offsetof(struct s,sex)= %ld\n",offsetof(struct s,sex));
printf("&s1 = %p\n", container_of(&s1.sex, struct s, sex));
printf("&s1 = %p\n", (struct s*)((char *)&s1.sex-offsetof(struct s,sex)));
printf("s1->id=%d\n",(struct s*)((char *)&s1.sex-offsetof(struct s,sex))->id);
return 0;
}
结果为:
&s1 = 0x7ffe5da751ec
&s1.sex = 0x7ffe5da751f0
offsetof(struct s,sex)= 4
&s1 = 0x7ffe5da751ec
&s1 = 0x7ffe5da751ec
s1->id=4
将上面示例展开为:
#define container_of(ptr, type, member)
({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );
})
container_of(&s1.sex, struct s, sex)
const typeof( ((struct s*)0)->sex) *__mptr = (&s1.sex); //
(struct s *)( (char *)&s1.sex - offsetof(type,member)
上面的mptr为s1.sex的地址,(struct s *)( (char *)&s1.sex - offsetof(type,member)的值为s1的地址值,(struct s*)((char *)&s1.sex-offsetof(struct s,sex))->id的值为4,我们在Linux内核编程中,传给某个函数的参数是某个结构体的成员变量,在这个函数中可能还会用到此结构体的其它成员变量,这个时候通过宏变量container_of ,找到结构体的首地址,然后就可以访问其它成员变量了。