container_of宏实现原理

1、在内核中的原型:

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

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

2、参数:

ptr:指向实例化的结构体元素member的指针

type:是这个结构体类型

member:结构体中一个元素的元素名

3、源码分析:0地址是不能访问进行读写的,这里只是用0地址做一个类型转化,没有读写

(1)   ((type *)0)->member 将0地址定义成一个type类型指针,这个指针就可以访问这个类型里面的任意元素了

(2)   typeof(((type *)0)->member) 得到这个元素的数据类型

(3)   const typeof(((type *)0)->member) * __mptr = (ptr)  把内存中被实例化的结构体变量里面member元素的指针用新定义的__mptr存下来,__mptr就是member元素的指针类型

(4)   offsetof(type, member)  得到type结构体中某个元素和结构体首地址的偏移量(所以的地址含义都是以字节位单位的,因为内存就是这样定义的比如0000到0001就是一个字节)

(5)   (char *)__mptr - offsetof(type, member)   __mptr本身是一个指针,它里面的值就是指向的元素的地址比如 p = p1;p1 = &b;p存的就是b的地址,(char *)p后,相当于p是一个指向char的指针,p+1时,地址才会加1,要看p指向的类型,觉得p+1等于多少,这时与偏移量相减值才对

(6)(type *)((char *)__mptr - offsetof(type, member))  将指针变成指向结构体类型的指针

作用:从一个元素的指针得到整个结构体变量的指针,继而得到结构体中其他元素的指针。

4、运用:

(1)比如内核管理驱动,假设一个结构体A包含整个驱动所以信息(在内存中被实例化,由malloc申请得到的地址空间),里面有一个B结构体是这个驱动某一类的信息,B里面包含一个双向链表节点,内核每注册和卸载一个驱动,这个管理驱动的双向链表里面对应的就会添加和删除一个节点,我们可以很容易得到这个链表节点的指针,然后利用B里面这个成员指针,得到结构体变量B的指针,再得到变量A的指针(也就是得到一个指向A结构体的指针),通过这个指针去操作A里面的每个元素

(2)一般地,在应用编程中,已知A结构体变量里面的某个成员的指针,想去知道其他成员的指针或者变量。首先我们知道某个成员b的指针,container_of得到整个结构体变量的指针,然后定义一个该类型的指针p,去接受container_of返回的指针,去指向A里面任意一个元素,就得到了这个元素,然后对这个元素赋值

(3)本来可以通过A结构体变量取地址得到&A,就能得到A的指针,但是在内核里面我们有时候无法访问到结构体变量A,无法得到&A,内核在对结构体A进行填充时,是在多个函数里面分别对A的某一部分进行填充,这些函数接收的参数是A里面某个元素的指针,我估计,比如有时候我们通过驱动的ID来记录是哪个驱动,内核返回一个ID给我们,这个ID就是A结构体里面的一个元素,这个时候我们无法得知这个ID所在的结构体变量名,所以无法&A,然后在另一个函数C里面要操作这个结构体里面其他元素,这时只能传&ID给函数C,内部通过container_of得到整个结构体A的指针,再去操作其他元素

猜你喜欢

转载自blog.csdn.net/qq_40334837/article/details/81389197