container_of图文分析

container of()函数分析

简介

Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。

Container_of的定义如下:

1
2
3
#define container_of(ptr, type, member) ({	    \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

img

其实,原理很简单: 已知结构体type的成员member的地址ptr,求解结构体type的起始地址。

​ type的起始地址 = ptr - size (这里需要都转换为char *,因为它为单位字节)。

具体来说分为两步:

第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值。
第二步,用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。

==把mptr指针强转成(char *)是因为,char指针减法只移一个字节,如果这样才可能得出准确的地址,否则,改为int类型,在减1就移动4个就乱了。==

偏移

其中的语法难点就是如何得出成员相对结构体的偏移量?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* linux-2.6.38.8/include/linux/compiler-gcc4.h */
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

/* linux-2.6.38.8/include/linux/stddef.h */
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#include <stdio.h>

struct test_struct {
int num;
char ch;
float fl;
};

int main(void)
{
printf("offsetof(struct test_struct, num) = %d\n",
offsetof(struct test_struct, num));

printf("offsetof(struct test_struct, ch) = %d\n",
offsetof(struct test_struct, ch));

printf("offsetof(struct test_struct, fl) = %d\n",
offsetof(struct test_struct, fl));

return 0;
}

说明,__builtin_offsetof(a,b)是GCC的内置函数,可认为它的实现与((size_t) &((TYPE *)0)->MEMBER)这段代码是一致的。

​ 例子输出结果:

1
2
3
offsetof(struct test_struct, num) = 0
offsetof(struct test_struct, ch) = 4
offsetof(struct test_struct, fl) = 8

其中代码难以理解的地方就是它灵活地运用了0地址。如果觉得&( (struct test_struct )0 )->ch这样的代码不好理解,那么我们可以假设在0地址分配了一个结构体变量struct test_struct a,然后定义结构体指针变量p并指向a(struct test_struct p = &a),如此我们就可以通过&p->ch获得成员ch的地址。由于a的首地址为0x0,所以成员ch的首地址为0x4

img2

最后通过强制类型转换(size_t)把一个地址值转换为一个整数

总结

container_of(ptr, type,member)函数的实现包括两部分:

​ 1、判断ptr 与 member 是否为同意类型

​ 2、计算size大小,结构体的起始地址 = (type )((char )ptr - size) (注:强转为该结构体指针)

现在我们知道container_of()的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。

container_of(ptr,type,member),这里面有ptr,type,member分别代表指针、类型、成员。


https://blog.csdn.net/npy_lp/article/details/7010752

https://blog.csdn.net/s2603898260/article/details/79371024