在C语言专题八的第6篇文章嵌入式开发《C语言(八:(6)2分钟搞懂结构体之offsetof宏》中offsetof宏的使用以及如何理解这个宏做了详细介绍。在开头也提到了这个宏在Linux系统中以及链表操作中非常有用。同时提到了offsetof宏会被container_of宏使用。因此在学习container_of宏之前最好先看看offsetof宏的原理。我们可以通过测试、分析这个宏,一方面是学习使用这个宏,为以后使用链表或者看懂linux代码做准备,另一方面通过分析这个宏,把前面学习的知识点进行回顾。注意:这个宏理解起来有点难度,有很多初学者或者有一定经验的人不一定看不懂。涉及到的知识点比较多(包含结构体、强制类型转换、常量指针、指针相减操作)。如果不理解可以先把基础打扎实,然后可以多看几遍、多分析、多用代码测试。
对于这个宏会分析、会使用即可。曾在面试题中要求分析这个宏。当然了,能在理解的基础上写出这个宏的具体实现过程,更好。
container_of宏作用
1)原型
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
2)参数
container_of宏参数type为结构体类型、member为结构体类型中的某个成员、ptr为结构体类型type中的member成员地址。注意ptr是成员变量的地址。如下图为linux内核中一小部分代码,通过container_of宏获得结构体首地址(struct tulip_private *tp),进而访问其中成员(tp->dev; tp->base_addr)
3)作用
计算结构体的首地址也就是结构体中首元素的首地址。往往在链表中一个小的变量嵌套在一个大的结构体中可以通过变量的地址来获取到整个结构体的地址,进而访问这个大结构体中的其它成员变量。
如何使用container_of宏
使用方法只需要在 container_of(ptr, type, member) 第一个参数传入结构体中member成员的地址,第二个参数传入为结构体类型,第三个参数传入结构体member成员。如下代码计算结构体的首地址:
#include #define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );}) //定义结构体类型typedef struct student{char name[20];int age;float grade;}STUDENT;int main(int argc, char** argv){//定义并初始化STUDENT s2 = {"zhangsan