container_of 宏
今天遇到了一段这样的代码,大致意思是 通过该struct结构体变量的成员的地址来反推该struct结构体变量的地址
并且用到了内核的宏,container_of()
static inline struct nova_inode_info *NOVA_I(struct inode *inode)
{return container_of(inode, struct nova_inode_info, vfs_inode);
}
查了查该宏位于include/linux/kernel.h
文件
功能:由结构体变量的某个成员变量的内存地址来得到该结构体变量的内存地址
参数:
-
该结构体成员变量的地址(指针)
2. 该结构体的定义类型(type) 3. 该结构体成员的变量名()
具体实现:
#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})
原理:
- 用到GCC的
typeo
f预处理语句,typeof(x),返回参数x的类型 - 用到了
offsetof(type,member)
,由一个结构成员member,得到相对于结构开头的字节偏移量 .在<stddef.h>
中
container_of()做了两步.
- 模拟该结构体变量指针指向成员然后通过typeof()反推成员的数据类型,
const typeof( ((type *)0)->member)
,然后定义一个该成员变量类型的指针(就是ptr的数据类型)const typeof( ((type *)0)->member) *__mptr = (ptr)
- 将
__mptr
转换为char *
,按照1个字节计算,__mptr
减去成员偏移量就是结构体变量的基地址了。再将该地址(指针)转换为type指针 - 最后表达式返回的是
(type *) (结构体变量基地址)
指针
1. const typeof( ((type *)0)->member) *__mptr = (ptr)
2. (char *)__mptr - offsetof(type,member)
测试:
#include <stdio.h>
#include <stddef.h>#define everything_to_string(x) #x
/*
#define container_of(ptr,type,member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})
*/
//const struct member *__mptr=ptr;
//(struct Student *)( (char *)__mptr - offsetof(type,member) )
#define container_of(ptr,type,member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)((char *)__mptr - offsetof(type,member)); })struct info {int a;int b;
};
struct Student {char name[4]; // 0int age; // 4int grade; // 8int sex; // 12struct info last;
};int main()
{struct Student student={"123",22,100,1,{10,20}};struct info *ptr=&student.last;struct Student *pstudent = container_of(ptr,struct Student,last);printf("[%p]\n",pstudent);printf("[%p]\n",ptr);return 0;
}
参考资料
- https://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-the-linux-kernel
- https://gaomf.cn/2017/10/07/C_typeof/
- https://www.runoob.com/cprogramming/c-macro-offsetof.html