定义
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#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) );})
container_of(ptr, type, member)
-
作用:通过小结构体地址得到其所在的大结构体地址
-
参数:
- ptr:从外面输入进来的小结构体指针(
struct list_head*
)(即小结构体的首地址) - type:大结构体类型(
struct kernel
) - member:大结构体中的小结构体变量的名字(
list
)
- ptr:从外面输入进来的小结构体指针(
-
使用:
struct kernel { datatype data; struct list_head list; }; struct list_head *p = (struct list_head *)0x22; // 假设小结构体地址为0x22 struct kernel *xp = list_entry(p, struct kernel, list); // 得到大结构体地址
-
在linux中,依次执行每个语句,然后将最后一条语句的值作为整个的值
#define xx ({;;;})
-
详细分析:
-
const typeof( ((type *)0)->member ) *__mptr = (ptr);
-
type
为struct kernel
,member
为小结构体变量的名字list
-
typeof( ((type *)0)->member )
得到小结构体的类型((TYPE *)0)
将0转换为struct kernel
类型的结构体指针,换句话说就是让编译器认为这个结构体是开始于程序段起始位置0(((type *)0)->member)
引用结构体中list
成员
-
整条语句等价于
const struct list_head *__mptr = (ptr);
-
总结:用
typeof()
获取结构体里list
成员属性的类型,然后定义一个该类型的临时指针变量__mptr,并将ptr
所指向的list
的地址赋给__mptr;为什么不直接使用 ptr 而要多此一举呢?我想可能是为了避免对 ptr 及prt 指向的内容造成破坏。
-
-
(type *)( (char *)__mptr - offsetof(type,member) );
-
offsetof(type,member)
,即((size_t) &((TYPE *)0)->MEMBER)
-
(TYPE *)0
首先将地址0
强制转换为struct kernel
类型的指针。这是一个虚拟的地址操作,目的是在逻辑上构建一个指向struct kernel
类型的指针,但实际上并不真的访问地址0
。 -
&((TYPE *)0)->MEMBER
接着取这个虚拟指针所指向的结构体中list
成员的地址。由于是从地址0
开始计算,所以得到的地址值实际上就是list
成员在struct kernel
结构体中的偏移量。 -
即得到小结构体起始位置 到大结构体的起始位置之间的字节数
-
-
通过小结构体指针 __mptr 减去(小结构体起始位置到大结构体的起始位置之间的字节数),得到了大结构体的地址
-
-