Linux container_of详解
container_of简介
container_of是很重要的一个功能宏,它可以基于一个成员来取得成员所在的结构体。
container_of定义和使用
1 | /** |
- 参数1:成员的指针
- 参数2:成员所在的结构体名字
- 参数3:该成员在结构体里面的名字
- 返回值:返回所在结构体的指针,类型是参数2指定的
使用示例:
1 | struct a_dev { |
container_of详解
container_of分为三个主要的组成部分,分别是小括号+花括号组成的赋值语句、offsetof语句、指针运算语句。
整体赋值
container_of本身是通过一对小括号和一对花括号组成的复合赋值语句。可以看到宏定义里面有三个语句(分号分割),然后用小括号({})包起来返回给调用者。伪代码表示的逻辑如下。
1 | auto result = container_of(成员指针,结构体名称,成员在结构体中的名称); |
从C语言的({})赋值方法知道,({})的返回值就是最后一个语句的值,因此container_of宏的整体意义就是把运算后的指针返回给调用者。
offsetof计算成员到结构体头的偏移
offsetof的定义如下。
1 |
其核心定义是((size_t)&((TYPE *)0)->MEMBER)
。该语句分解如下。
- ((TYPE *)0):把地址0当作存储了一个TYPE,取为指针
- ((TYPE *)0)->MEMBER:struct结构体访问成员的编译原理实际上是指针偏移,这里利用
->
来“访问”MEMBER - 加一个
&
:前面已经访问到了MEMBER,对它加上&,实际上等同于计算了地址0按struct TYPE头地址相对MEMBER的偏移,相当于假设地址0有一个TYPE数据,这一步就取到了MEMBER成员的地址,即MEMBER相对于TYPE头的偏移 - (size_t):进行强制类型转换,把偏移变成“大小”
选用0地址的原因是如果结构体头地址是0,那么其成员的地址就是偏移的大小
对0地址做了”->调用”,这里利用了编译器进行的地址加减偏移,实际上编译器编译后就是数值0的加减,并未发生调用,不会产生空指针问题。
指针运算
第二步我们已经获取到了MEMBER相对于TYPE的偏移大小,而我们本身就知道了MEMBER的地址,那么把MEMBER抵消掉这段偏移,得到的就是TYPE的地址。再做一个强制类型转换,就取得了MEMBER所在的TYPE了。
1 | ((type *)(__mptr - offsetof(type, member))); |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 PeaceMaker!
评论
DisqusWaline