内容翻译自 http://radek.io/2012/11/10/magical-container_of-macro/
当你开始阅读linux内核的代码的时候,你最终会遇到这个神奇的宏。
这个宏是做什么的?
三个参数:ptr, type,member, 分别为:成员的指针,包含这个成员的容器的类型,这个成员的名字
返回值:一个指针,指向包含这个成员的容器,如下图
这个宏在已知成员名字及指针,想找到包含自己的结构体的指针的时候很有用(废话),什么时候需要呢?比如在使用c语言写一个适用性强的链表的时候
这个宏是如何实现的?
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
接下来逐步解释
表达式中的声明/语句
GNU 对C语言有一个扩展,名字为braced-group within expression,{}里面的整个表达式的值是最后一个声明的值。编译器计算evaluate整个{},以最后一个声明/语句作为整个表达式的值。
下面的例子结果是5
int x = ({1; 2;}) + 3;
printf("%d\n", x);
typeof()
这是一个非标准的 GNU C 扩展. 返回唯一参数的类型. 这个宏的语义在http://gcc.gnu.org/onlinedocs/gcc/Typeof.html中与具体描述
int x = 5;
typeof(x) y = 6;
printf("%d %d\n", x, y);
解引用 零指针
获取零指针的成员的类型或者取成员的地址时候并不会崩溃。因为表达式并不会被计算,编译器只关心类型。取成员地址的时候,取出的是在结构体中的偏移地址。
struct s {
char m1;
char m2;
};
/* This will print 1 */
printf("%d\n", &((struct s*)0)->m2);
下面两个定义是等价的
typeof(((struct s *)0)->m2) c;
char c;
offsetof(st, m)
其实现方法已经在上文中提到了。这个宏在stddef.h中,是标准库的一部分。但是内核没有标准库,下面是内核里的(messy)实现
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
返回名为MEMBER成员在TYPE类型结构体中的偏移位置.
放在一起
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
结合以上已经非常容易理解