双链表基本结构定义
struct list_head{
struct list_head * next;
struct list_head * prev;
};
offsetof 宏
#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE*)0->MEMBER))
-
(TYPE*)0 将整数0强制转换成一个指向TYPE类型的指针,关键在于,这里并没有分配任何内存,只是告诉编译器有一个指向TYPE类型的指针,它的地址是0
-
((TYPE)0)->MEMBER是通过指针访问结构体的成员MEMBER,由于结构体的起始地址被认为是0,因此((TYPE)0)->MEMBER实际获取的是成员MEMBER相对于结构体起始地址的地址。
3.&((TYPE*)0)->MEMBER,这一步使用取地址运算符 &,获取上一步中得到的成员 MEMBER 的地址
4.(size_t) &((TYPE *)0)->MEMBER。这一步将上一步得到的地址(偏移量)强制转换为 size_t 类型。size_t 是一个无符号整数类型,通常用于表示对象的大小或偏移量。将其转换为 size_t 确保了偏移量以正确的类型表示,并且可以进行安全的算术运算。
container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
-
typeof( ((type \*)0)->member )
:获取成员类型(type *)0
:将整数0
强制转换为指向type
类型结构体的指针。这样做并不会访问实际的内存地址,只是为了进行类型推导。((type *)0)->member
:通过空指针访问结构体成员member
。编译器会根据type
的定义,推断出member
的类型。typeof( ((type *)0)->member )
:使用typeof
关键字获取member
成员的类型。例如,如果member
是int
类型,则typeof( ((type *)0)->member )
的结果就是int
。
-
const typeof( ((type \*)0)->member ) \*__mptr = (ptr);
:创建类型匹配的临时指针const typeof( ((type *)0)->member ) *__mptr
:声明一个类型为const typeof( ((type *)0)->member ) *
的指针__mptr
。const
关键字表示该指针指向的内容是不可修改的。这样做是为了进行类型检查,防止传入的ptr
指针类型与member
成员类型不匹配。= (ptr)
:将传入的ptr
指针赋值给__mptr
。
这一步的目的是创建一个与
ptr
类型相同的临时指针,并进行类型检查。在一些老的container_of
实现中,没有const
和typeof
,直接使用void * __mptr = (void *)(ptr);
,这样做虽然简单,但缺少类型检查,不够安全。新的实现方式更严谨。 -
(char \*)__mptr
:将指针转换为字符指针(char *)__mptr
:将__mptr
指针强制转换为char *
类型。这是关键的一步,因为char
类型的大小为 1 字节,将指针转换为char *
类型后,进行指针的加减运算时,步长就是 1 字节,方便进行偏移量的计算。
-
offsetof(type, member)
:计算成员偏移量offsetof(type, member)
:这是一个宏,用于计算成员member
在结构体type
中的偏移量(以字节为单位)。
-
(char *)__mptr – offsetof(type,member)`:计算结构体起始地址
(char *)__mptr - offsetof(type,member)
:从__mptr
指针(指向成员的地址)中减去member
成员的偏移量。由于__mptr
已经被转换为char *
类型,因此减法运算的单位是字节。计算结果就是结构体的起始地址。
-
(type *)( (char *)__mptr – offsetof(type,member) )`:将结果转换为结构体指针
(type *)( ... )
:将上一步计算得到的结构体起始地址强制转换为type *
类型,得到指向结构体的指针。