该宏的作用是通过结构体成员的地址和结构体类型推导出结构体的地址,type是指结构体的类型,member是成员在结构体中的名字,ptr是该成员在type结构体中的地址。
在 linux 源码的 toolsincludelinuxkernel.h
文件下,container_of()
的定义如下:
#ifndef container_of
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({
const typeof(((type *)0)->member) * __mptr = (ptr);
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
在 container_of()
宏的定义中的 offsetof(TYPE, MEMBER)
和 typeof()
初学者可能会对其很陌生,所以我们要先从理解 offsetof(TYPE, MEMBER)
和 typeof()
的作用开始。
本质也是个宏定义,在 linux 源码的 toolsincludelinuxkernel.h
文件下定义如下:
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
offsetof
宏中的 TYPE
是指结构体的类型,MEMBER
是指结构体中的某个成员,作用是求出该成员的在该结构体中的偏移量。该宏首先将 0 (地址0)转化为 TYPE *
的结构体指针,表示地址为 0 的结构体指针,然后通过取地址符 &((TYPE *)0)->MEMBER)
取出该结构体指针中 MEMBER
成员的地址,最后再将地址值强转为 size_t
类型(内核中为 unsigned long
类型)即表示 MEMBER
成员在结构体中的偏移量。要理解该过程需要了解对结构体的内存分布,如图,结构体的内存分配是连续的,当结构体的地址为0时,成员的地址即为该成员的偏移量。
实例:
#include
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
typedef struct _offset
{
char member_0;
int member_1;
char member_2;
}offset;
int main()
{
printf("%d
", offsetof(offset, member_0));
printf("%d
", offsetof(offset, member_1));
printf("%d
", offsetof(offset, member_2));
return 0;
}
输出:
offsetof实例结果输出
typeof()
是 GNU C
中的一个关键字,和 sizeof()
一样都是 C 语言中的关键字而不是函数。作用是返回传入数据的类型。实例:
#include
int main()
{
int a = 3;
typeof(a) b = a; /* 求出a变量的类型,并创建一个b变量 */
printf("a=%d b=%d", a, b);
return 0;
}
输出:
img
了解了 offsetof()
宏和 typeof
关键字之后就比较好理解 container_of
宏的作用了。
const typeof(((type *)0)->member) * __mptr = (ptr)
该代码的作用实际上是将 0 转化为 type *
结构体类型,再取出结构体中的MEMBER成员 (type *)0)->member
, 再通过 typeof
关键字获取 MEMBER
成员的类型,并定义一个 MEMBER
成员类型的指针 const typeof(((type *)0)->member) * __mptr
,将传入的 ptr
指针赋值给 __mptr__mptr = (ptr)
。
(type *)((char *)__mptr - offsetof(type, member));
该代码是将获取的 MEMBER
成员地址强转为 char *
(强转的目的是考虑指针的加减的实质是指针在内存的偏移,偏移量为指针类型所占字节的个数),减去 MEMBER
成员在 type
结构体中的偏移量,强转为 type *
后得到结构体的地址。实例:
#include
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({
const typeof(((type *)0)->member) * __mptr = (ptr);
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
typedef struct _container
{
char member_0;
int member_1;
char member_2;
}container;
int main(void)
{
container *a = NULL;
container b = {'a', 2, 'b'};
/* member_1在实例结构体中的地址 结构体类型 成员名 */
a = container_of(&b.member_1, container, member_1);
printf("a->member_0 = %c
", a->member_0);
printf("a->member_1 = %d
", a->member_1);
printf("a->member_2 = %c
", a->member_2);
return 0;
}
输出:
全部0条评论
快来发表一下你的评论吧 !