container_of()宏

前言

看内核源码时经常遇到这个宏,现在解释一下这个,记录备查。

第一张图说明了container_of(ptr, type, member)宏的原理
在这里插入图片描述

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

表达式中的语句

引起您注意的第一件事可能是整个表达式的结构。该语句应返回一个指针,对吗?但是,其中只有一种奇怪的({})块,其中有两个语句。实际上,这是对C语言的GNU扩展,称为表达式内的括号组。编译器将评估整个块并使用该块中包含的最后一条语句的值。以下面的代码为例。它将打印5。

int x = ({1; 2;}) + 3;
printf("%d\n", x);

typeof()

这是非标准的GNU C扩展。它接受一个参数并返回其类型。它的确切语义在gcc文档中进行了全面介绍。

int x = 5;
typeof(x) y = 6;
printf("%d %d\n", x, y);

零指针取消引用

但是零指针取消引用呢?好了,获取成员的类型有点魔力。它不会崩溃,因为表达式本身永远不会被求值。编译器只关心其类型。万一我们要求提供地址,也会发生同样的情况。编译器再次不在乎该值,它只是将成员的偏移量添加到结构的地址(在这种情况下为0),然后返回新地址。

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中使用)。但是不在内核空间中,因为那里没有标准的C库。就像我们之前看到的那样,它有点像0指针取消引用魔术,并且为了避免现代编译器通常提供实现该功能的内置函数。这是凌乱的版本(来自内核):

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

它返回一个类型为TYPE的结构的成员MEMBER的地址,该地址从地址0(恰好是我们要查找的偏移量)存储在内存中。

放在一起

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

当您从这篇文章的开头更加仔细地查看原始定义时,您会开始怀疑第一行是否真的适合任何事情。你会是对的。第一行对于宏的结果并不是本质上重要的,但是它用于类型检查。第二行真正的作用是什么?它从其地址减去结构成员的偏移量,从而得出容器结构的地址。而已!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山羊哥-老宋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值