container_of()学习及往VC上的移植

在学习数据结构课时,咱们知道链表元素是个结构体,由数据项和指针项构成,正式里面的指针项是造成链表结构的核心,但数据项才是链表有意义的依托,若是一个链表元素只有指针项,没有数据项,这个链表是没有意义的。但这只是表面如此。在学习linux内核的双向循环链表中,咱们不得不叹服内核设计者的匠心独具。List_head 结构定义在include/linux/types.h中 linux


struct list_head { ios


      struct list_head *next, *prev; 数据结构


}; 学习


这个结构体没有数据项!也就是说,n多个这种元素能够首尾相连造成一个双向循环的环,可是在这个链表中没有信息的载体。那么一切链表的操做,好比遍历,在这里有什么意义呢?总不能就在一堆指针里闲逛吧。 测试


   咱们要让链表有意义,必须使链表有信息的载体,即数据项,这是毋庸置疑的。那就让咱们换个思路,既然一堆list_head已经构成一个链表,并且咱们不能往list_head对象里面加入数据项,那么让这些list_head对象位于另一个结构体的内部,做为指针域存在,这个外部结构体的其余成员做为数据项存在,是否就让链表有意义了呢。 this


   答案是确定的,可是问题又来了,N多外部结构体的对象经过包含list_head相互联系起来,但是在对链表进行操做时,咱们的目的是要操做或访问与之相关的数据。而如今数据是存在于List_head对象外的,咱们不能经过链表元素的指针来访问。事实上,咱们只能经过同时包含这些数据项和链表元素的外部结构体对象的指针来访问,为何呢?哈哈,绕晕了,由于这些数据原本就是这些外部结构体对象的内容之一啊。 spa


   如今问题归结为:已知一个结构体里某个成员的指针,怎么获得这个结构体的指针? .net


   试想,若是知道了这个已知成员在结构体里的地址偏移,咱们就能获得这个结构体的指针。如是List_head类型的链表的操做才显得有意义。 设计


   到如今全部的问题都指向了一个宏,container_of(),这个宏才是整个链表结构的最本质的地方。该宏定义在kernel.h中。 指针


 


/**


 * 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) );})


 


其中offsetof的定义为:


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


offsetof宏的做用是返回一个TYPE类型结构的MEMBER成员在该结构中的编译。把零地址强制转换一个指向该结构体对象的指针,那么该对象中MEMBER成员的地址就是该成员在这个结构体中的偏移。


那么container_of的工做原理就至关简单了,就是用MEMBER的地址减去上一步中获得的偏移,即为TYPE类型的结构体对象的地址。


为何__mptr要强制转换为char*呢?这是c语言的基础知识,指针的加减对应到地址是根据指针所指的数的类型来肯定的。若是MEMBER是int,而不把它的指针强制为char*,那么减去的将是4倍的地址偏移,固然是不对的。


既然内核中链表如此精妙,咱们是否能够将其纳为己用呢,固然能够,只要咱们实现了那个container_of宏就行。事实上,这是至关简单的,只需一个定义:


#define container_of(ptr, type, member)  (type *)( (char *)ptr - offsetof(type,member) )就能够了。Vc里预约义了offsetof宏,其语义跟linux内核相同。


 


在VC上的测试:


#define container_of(ptr, type, member)  (type *)( (char *)ptr - offsetof(type,member) )
#include<iostream>
using namespace std;
int main()
{


 struct StructA
 {
  int a1;
  int a2;
 };
 
 struct StructB
 {
 
  int b1;
  int b2;
  StructA a;
 };


 struct StructC
 {
  int c1;
  int c2;
  StructB b;
 };


 StructA aa;
 aa.a1=11;
 aa.a2=22;


 StructB bb;
 bb.b1=33;
 bb.b2=44;
 bb.a=aa;


 StructC cc;
 cc.c1=55;
 cc.c2=66;
 cc.b=bb;


StructA *pa=container_of(&aa.a1,StructA,a1);
cout<<pa->a1<<" "<<pa->a2<<endl;


StructB *pb=container_of(&bb.b2,StructB,b2);
cout<<pb->b1<<" "<<pb->b2<<" "<<(pb->a).a1<<" "<<(pb->a).a2<<endl;


StructB *pb2=container_of(pa,StructB,a);
cout<<pb->b1<<" "<<pb->b2<<" "<<(pb->a).a1<<" "<<(pb->a).a2<<endl;


StructC *pc=container_of(&cc.c1,StructC,c1);
cout<<pc->c1<<" "<<pc->c2<<" "<<(pc->b).b1<<" "<<(pc->b).b2<<" "<<(pc->b).a.a1<<" "<<(pc->b).a.a2<<endl;
StructC *pc2=container_of(pb,StructC,b);
cout<<pc->c1<<" "<<pc->c2<<" "<<(pc->b).b1<<" "<<(pc->b).b2<<" "<<(pc->b).a.a1<<" "<<(pc->b).a.a2<<endl;


}


运行结果为:


1bf9ac75-eb16-47d3-bf6d-aa7e45677218.JPG


至关温馨啊。今后,咱们能够把List_head和list.h中全部对链表的操做当作本身的东西了。

相关文章
相关标签/搜索