container_of()宏在操作内核标准链表时很有用,也是内核提供的标准方法之一,其他的大多数链表操作都依赖这个宏.
这个宏用于获得一个结构(成员)的父结构体的入口地址.
因为C语言在编译的时候,结构体的地址就由ABI确定下来了, 这才有了下面的实现:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/* type:父结构体的类型
* member:子结构体在父结构体中的域名
* ptr:指向member的指针
*/
#define container_of(ptr, type, member) ({\
const typeof((type *)0)->member) * __mptr = (ptr);\
(type *)((char *)__mptr - offsetof(type, member));})
分析如下:
1>检查结构+存放member指针:
const typeof((type *)0)->member) * __mptr = (ptr);
假设从0开始存放的是type结构体
获取member的类型
定义一个member类型的指针__mptr, 并把ptr赋值给他(提前知道的)
这一句同时也检查了,type结构体中是否真的包含member域
2>计算member在type结构体中的偏移:
((size_t) &((TYPE *)0)->MEMBER)
假设0地址存放了一个TYPE类型的结构体
那么MEMBER的地址就是MEMBER在TYPE结构体中的偏移
3>得到父结构的入口地址, 并以"(type *)"返回:
(type *)((char *)__mptr - offsetof(type, member));
__mptr的地址减去它在父结构体中的偏移,就是父结构的入口地址了.