做一件事情首先应该知道它的目的是什么。 container_of的目的:如何通过结构中的某个变量获取结构本身的指针。 总体思路:假想一下,你的结构体中有好几个成员,你如何通过里面的“任一成员”获取整个结构体的首地址呢。container_of的做法就是通过typeof定义一个与“任一成员”同类型的指针变量pvar_a(假设变量名就是pvar_a),并让指针变量pvar_a指向这个“任一成员”,然后用 “pvar_a的地址” 减去 “pvar_a相对于你的结构体的偏移量”,这个结果对应的地址其实就是你的结构体的首地址,最后将这个得到的结果强制转换为你的结构体对应的类型就OK了。 关于container_of见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) );}) 注:补充一点C语言关于宏定义的知识,在一个宏中,如果有多条语句,则最终宏的返回结果是最后一条语句的值,这有点像逗号表达式。 关于offsetof见stddef.h中: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) TYPE是某struct的类型,(TYPE *)0是将0地址处强制转换为TYPE类型struct,MEMBER是该struct中的一个成员(你完全可以理解成就是我上面说的“任一成员”). 由于该struct的基地址为0,所以MEMBER的地址本身毫无疑问之意就是代表它自身的地址,另外它还巧妙的有另外一层含义就是MEMBER的地址数值上等于MEMBER相对与struct头地址的偏移量,现在你该明白之前为什么要选(TYPE *)0来强制转换,而不选(TYPE *)100,或者其他。 关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型. “const typeof( ((type *)0->member ) *__mptr = (ptr);” 的意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr. “(type *)( (char *)__mptr - offsetof(type,member) );” 的意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 其实该指针就是member的入口地址了.
下面是我结合以前在网上的一个例子修改后的一个新例子,希望对读者有帮助:
#include<stdio.h> /* *在应用层不能引用内核提供的container_of和offsetof宏,故需自己定义 */ #define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );}) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) /* *测试用的结构体 */ struct student{ char name[20]; char sex; }stu={"liuhb569620660",‘M‘}; int main(int argc, char *argv[]) { struct student *stu_ptr; //存储container_of宏的返回值 int offset; //存储offsetof宏的返回值 /* *下面三行代码等同于 container_of(&stu.sex, struct student, sex ) */ /* 首先定义一个 _mptr指针, 类型为struct student结构体中sex成员的类型,typeof用于获取(((struct student*)0)->sex)的类型,此处该成员类型为char,((struct student*)0)在offsetof处讲解 */ const typeof(((struct student*)0)->sex) *_mptr = &stu.sex; /* ((struct student*)0)是把0地址强制转化为指向student结构体类型的指针,该指针从地址 0 开始的 21个字节 用来存放name与sex,(注:char name[20]与char sex共21字节),sex存放在第20个字节出(从0字节开始),&((struct student *)0)->sex 取出sex地址(此处即为20),并强制转化为整形,所以offset为20,后面的printf结果将证明这一点 */ offset = (int)(&((struct student *)0)->sex); /* ((char*)_mptr - offset)此处先把_mptr指针转化为字符形指针,(为什么这么做呢? 如果_mptr为整形指针 _mptr - offset 相当于减去 sizeof(int)*offset个字节),减去 offset值 相当于 得到_mptr所在结构体的首地址(即stu的地址),然后我们把 该地址 强制转化为 struct student 类型即可正常使用了 */ stu_ptr = (struct student *)((char*)_mptr - offset); printf("\n### offsetof stu.sex = %d\n",offset); printf("### stu_ptr->name = %s \n" "### stu_ptr->sex = %c\n\n", stu_ptr->name, stu_ptr->sex); /* 下面增加验证直接调用container_of这个宏效果 */ /*通过sex成员找到结构体首地址*/ printf("\n### container_of(&stu.sex, struct student, sex)->name = %s\n", container_of(&stu.sex, struct student, sex)->name); printf("### container_of(&stu.sex, struct student, sex)->sex = %c\n", container_of(&stu.sex, struct student, sex)->sex); /*通过name成员找到结构体首地址*/ printf("\n### container_of(&stu.sex, struct student, sex)->name = %s\n", container_of(stu.name, struct student, name)->name); printf("### container_of(&stu.sex, struct student, sex)->sex = %c\n", container_of(stu.name, struct student, name)->sex); return 0; }
例子的演示效果如下:
深入浅出实例解析linux内核container_of宏,布布扣,bubuko.com
时间: 2024-10-11 05:16:07