神奇的宏 container_of()

内容翻译自 http://radek.io/2012/11/10/magical-container_of-macro/

当你开始阅读linux内核的代码的时候,你最终会遇到这个神奇的宏。


这个宏是做什么的?

三个参数:ptr, type,member, 分别为:成员的指针,包含这个成员的容器的类型,这个成员的名字

返回值:一个指针,指向包含这个成员的容器,如下图

神奇的宏 container_of()_第1张图片

这个宏在已知成员名字及指针,想找到包含自己的结构体的指针的时候很有用(废话),什么时候需要呢?比如在使用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) );})

结合以上已经非常容易理解


你可能感兴趣的:(linux,kernel)