在Linux内核代码中多处使用了宏container_of,关于container_of 的宏的定义在include/linux/kernel.h
/**
* 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) );})
container_of宏中的参数ptr代表指针、type代表类型、member代表成员
简单的来说:container_of宏的作用是通过结构体中一个成员的地址找到这个结构体
struct test{
char data;
struct test * next;
int num;
};
struct test temp;
假如此时我们只有temp.num的地址,如何找到temp的首地址去访问它的其它成员呢?
使用宏container_of:container_of(&temp.num, struct test, num)
container_of宏展开:
第一行:
const typeof( ((type *)0)->num) *__mptr = (&temp.num)
typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型,这里是获取结构体中num的类型,所以这一行代码其实就是定义了__mptr是一个指向num类型变量的指针,并将temp.num的地址赋给它。
第二行:
(type *)( (char *)__mptr - offsetof(struct test,num) )
offsetof宏的定义在 include/linux/stddef.h #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE )0)->MEMBER)
把offsetof宏展开(size_t) &((type)0)->num,定义了0地址并强制转换成指向type类型的指针,在取出其中成员num的地址,这一步其实就能计算出num成员基于结构体首地址的偏移值。
总结:
(type *)( (char *)__mptr - offsetof(struct test,num) ) 就是成员num的地址 - num成员基于struct test首地址的偏移值,最终得到struct test的首地址。
代码demo:
struct test{
char data;
struct test *next;
int num;
};
struct test temp;
printf("结构体temp的首地址:%p \n",&temp);
printf("成员num地址:%p \n",&temp.num);
printf("-------------------------------\n");
printf("调用container_of(&temp.num,struct test,num):%p \n",
container_of(&temp.num, struct test, num));
printf("-------------------------------\n");
printf("num基于test基地址的偏移(size_t) &((struct test *)0->num: %ld \n)",(size_t)&((struct test *)0)->num);
运行后结果:
看运行结果验证container_of运行后的作用。