指针的灵活应用--内核链表中的container_of

  指针在C语言是一种很强大的武器,运用的好的话可以为我们很好的服务,这里我们以内核链表中的一个宏container_of,来分析编写内核代码的大佬们是如何巧妙运用指针的。

  我们先直接给出container_of的定义

 1 /** 
 2 * container_of - cast a member of a structure out to the containing structure 
 3 * @ptr:    the pointer to the member. 
 4 * @type:   the type of the container struct this is embedded in. 
 5 * @member: the name of the member within the struct. 
 6 * */  
 7   
 8 #define container_of(ptr, type, member) ({                    /          
 9         const typeof( ((type *)0)->member ) *__mptr = (ptr); -/  
10         (type *)( (char *)__mptr - offsetof(type,member) );})

  在container_of中,我们又发现有一个typeof和offsetof,我们先来分别解释一下它们。

1.offsetof

  offsetof的定义如下:

1 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

  offsetof的意思是,在求结构体TYPE中,MEMBER相对于TYPE起始地址的偏移量。

  offsetof是0指针的灵活运用。我们可以直接用代码测试。

 1 /*************************************************************************
 2         > File Name: mypointer.c
 3         > Author: yudongqun
 4         > Mail: qq2841015@163.com
 5         > Created Time: Thu 05 Nov 2020 10:20:30 AM CST
 6  ************************************************************************/
 7 #include <stdio.h>
 8 #define NAME_LEN 20
 9 #define offset(type, member) ((size_t)&((type *)(NULL))->member)
10 typedef struct {
11     char name[NAME_LEN];
12     int school_number;
13 } Students;
14 
15 int main(void) {
16     Students stu;
17     printf("%lu\n", offset(Students, school_number));
18     printf("%lu\n", (size_t)((void *)&stu.school_number - (void *)&stu));
19     return 0;
20 }

编译并运行:

ydqun@VM-0-9-ubuntu trash % ./a.out [0]
20
20

可以看到,我们通过offsetof求出了school_number成员在Students结构体中的位置,即school_number相对于Students结构体中的偏移量。

2.typeof 

  typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛。仔细学习可以访问http://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof。我们这里只讲一下在contianer_of中typeof的用法。首先在#define container_of(ptr, type, member)中,type是结构体的名字,member指结构体type中的成员,ptr指某个type结构体变量中member的地址,我们单独把这语句代码const typeof( ((type *)0)->member ) *__mptr = (ptr),来写一个程序验证一下它的效果。

 1 /*************************************************************************
 2         > File Name: mypointer.c
 3         > Author: yudongqun
 4         > Mail: qq2841015@163.com
 5         > Created Time: Thu 05 Nov 2020 10:20:30 AM CST
 6  ************************************************************************/
 7 #include <stdio.h>
 8 #define NAME_LEN 20
 9 typedef struct {
10     char name[NAME_LEN];
11     int school_number;
12 } Students;
13 
14 int main(void) {
15     Students stu = {
16         .name = "Tom",
17         .school_number = 1234
18     };
19     typeof(((Students *)0)->school_number)* p = &stu.school_number;
20     printf("*p = %d, stu.school_number = %d\n", *p, stu.school_number);
21     printf("p = %p, &stu.school_number = %p\n", p, &stu.school_number);
22     return 0;
23 }

编译并运行。

ydqun@VM-0-9-ubuntu 20201031 % gcc my_typeof.c                                                                    [0]
ydqun@VM-0-9-ubuntu 20201031 % ./a.out                                                                            [0]
*p = 1234, stu.school_number = 1234
p = 0x7ffe0a2e99f4, &stu.school_number = 0x7ffe0a2e99f4

  由测试结果得知,typeof( ((type *)0)->member ) *__mptr = (ptr)的作用就是获取结构体type中member的类型去定义一个指向该member类型的指针mptr,并把指针ptr赋值给mptr,其中ptr是某个type结构体中的变量member的地址。

  至此,我们已经分析完offsetof和typeof,现在可以直接分析container_of了。

3.container_of

  其实container_of已经没啥好分析的了,const typeof( ((type *)0)->member ) *__mptr = (ptr),这一句代码是把指针ptr赋值给同类型的mptr。而宏定义#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)),是巧妙运用了0指针地址去求成员member在结构体type中的偏移量。那么我么知道成员member的地址ptr和偏移量,直接用member的地址去减去偏移量,就求出member成员所在的结构体变量的地址了,这就是container_of的作用。说到这里,也许你会觉得为啥要多执行const typeof( ((type *)0)->member ) *__mptr = (ptr);这一步操作,其实,这一步是防止开发者使用container_of时,输入参数有误,如果参数ptr输入错误,既ptr指向的类型和member的类型不匹配,编译器就会报错,所以container_of的严谨性真是让人叹服,不得不佩服内核代码编写者的代码功底。

  最后,我们直接给出container_of的测试代码。

 1 /*************************************************************************
 2         > File Name: container_of.c
 3         > Author: yudongqun
 4         > Mail: qq2841015@163.com
 5         > Created Time: Thu 05 Nov 2020 05:16:36 PM CST
 6  ************************************************************************/
 7 
 8 #include <stdio.h>
 9 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
10 #define container_of(ptr, type, member) ({\
11     const typeof( ((type *)0)->member ) *__mptr = (ptr);\
12     (type *)( (char *)__mptr - offsetof(type,member) );})
13 
14 #define NAME_LEN 20
15 typedef struct {
16     char name[NAME_LEN];
17     int school_number;
18 } Students;
19 
20 void func(int *p) {
21     Students *stu = container_of(p, Students, school_number);
22     printf("name: %s, school_number: %d\n", stu->name, stu->school_number);
23 }
24 
25 int main(void) {
26     Students stu = {
27         .name = "Tom",
28         .school_number = 1234
29     };
30     func(&stu.school_number);
31     return 0;
32 }
ydqun@VM-0-9-ubuntu 20201031 % gcc container_of.c                                                                 [0]
ydqun@VM-0-9-ubuntu 20201031 % ./a.out                                                                            [0]
name: Tom, school_number: 1234

  

posted @ 2020-11-05 17:41  ydqun  阅读(271)  评论(0编辑  收藏  举报