这个函数的目的是, 通过结构体里面的内容 找到 大结构体的 基地址。
函数的原型是:
PTR是指针
type , member 都是具体的类型。
12 /**
11 ▎* container_of - cast a member of a structure out to the containing structure
10 ▎* @ptr: the pointer to the member.
9 ▎* @type: the type of the container struct this is embedded in.
8 ▎* @member: the name of the member within the struct.
7 ▎*
6 ▎*/
5 #define container_of(ptr, type, member) ({ \
4 ▎ void *__mptr = (void *)(ptr); \
3 ▎ BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
2 ▎ ▎ ▎ !__same_type(*(ptr), void), \
1 ▎ ▎ ▎ "pointer type mismatch in container_of()"); \
856 ▎ ((type *)(__mptr - offsetof(type, member))); })
1
2 /**
--------------------------------------------------------------------------------------------------------------------------
先来看一个 我自己的 追踪,
接下来看一下 offsetof() 函数
结果是个这个。
再来追踪 __builtin_offsetof() 函数就追踪不到了,这是一个 GCC的函数。
从网上找找这个函数的实现。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
接下来 解释 一下 这个函数。还是 又不少的东西的。
首先是 这个 : (TYPE *)0)->MEMBER
它的意思是 , 在 TYPE 这个结构体中, 找到 MEMBER成员, 但是 编译器 首先会找到 MEMBER的偏移地址, 并不会 对 0 地址的内存有什么操作 , 先。
然后是: &((TYPE *)0)->MEMBER) , 这意味着 我不是已经找到了MEMBER的的位置了吗, 现在 对这个位置 取地址,你知道,基地址是0 , 所以 MEMBER的地址,就是一个相对地址, 这样我实际上找到的是 TYPE 与MEMBER的差值。但是这个地址值,是有类型的,类型就是 MEMBER* 。
然后就是: ((size_t) &((TYPE *)0)->MEMBER) 我把它强制转换成了一个 int 类型, 这就是一个数字了。
然后就是: __mptr - offsetof(type, member))) 这实际上就是 __mptr 减去一个 int 型的数字,
void *__mptr = (void *)(ptr) 这句说明, __mptr 是一个 void* 的指针。 那么 这句 __mptr - offsetof(type, member))) 就变成了 指针 加减 一个 整数了。
如果是在堆中的话,我们知道,堆是从下往上增长的。
那么 这个 ((size_t) &((TYPE *)0)->MEMBER) 将是一个正数。
那么 __mptr - offsetof(type, member))) 这个 意味着指针的位置 , 在从上往下 减, 也就是从一个小结构体, 找到了一个大结构体的 基地址。
然后就是: ((type *)(__mptr - offsetof(type, member))); 这个函数的 type* 就是 在将 计算出的 大结构体的 指针 (这是一个数字), 转换成 大结构体指针类型,用于寻找 在这个大结构体 中的其他成员。
------------------------------------------------------------------------------------------------------------------------
来看看我自己的测试。
我是在 4412 arm 的裸机程序中做的测试。
71 #define size_t unsigned int
72 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
73
74 struct human_mod{
75
76 int head;
77 char eye;
78 float foot;
79 };
80
81
82 int main(void)
83 {
84 size_t ret;
85 ret = offsetof(struct human_mod , foot);
86 int i = 0;
87 led_init ();
88 while(1)
89 {
90
91 led_on(i%2);
92 led_off(((i-1)+2)%2);
93 i++;
94 delay_ms(500);
95
96 }
97 return 0;
98 }
这是 汇编的结果:
int main(void)
{
40008198: e92d4800 push {fp, lr}
4000819c: e28db004 add fp, sp, #4
400081a0: e24dd008 sub sp, sp, #8
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:85
size_t ret;
ret = offsetof(struct human_mod , foot);
400081a4: e3a03008 mov r3, #8
400081a8: e50b300c str r3, [fp, #-12]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:86
int i = 0;
400081ac: e3a03000 mov r3, #0
400081b0: e50b3008 str r3, [fp, #-8]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:87
led_init ();
400081b4: ebffff95 bl 40008010 <led_init>
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:91
while(1)
{
也就是说 ,汇编的是 已经是结果了,而不是 过程。
这里 直接 把 8 这个数字算出来了。 看来汇编代码 还不底层,更底层的应该是 编译器源码了。
400081a4: e3a03008 mov r3, #8