这是Linux-source-4.13.0/include/Linux/kernel.h中的container_of )函数的定义。
#definecontainer_of(ptr、type、member ) (void*__mptr=) void* ) ptr ); BUILD_BUG_ON_MSG (! __same_type(*(ptr ),) type * )0)-member ) (! __same_type(*(ptr ),void ),(pointertypemismatchincontainer _ of () ) ); () (type* ) __mptr-offsetof ) type,member ) ); ptr:表示结构中member的地址
type:表示结构体类型
member:代表结构的成员
更多版本如下。
#definecontainer_of(ptr、type、member ) (consttypeof ) ) (type )0)-member ) _ _ mptr=(ptr ); ( (type * ) ) char*__mptr-offsetof ) type,member ); }他们的区别在于__mptr的类型不同,一个是void*,另一个是type *。 其中包括在linux/stddef.h中定义的宏函数,即offsetof (可见也包括函数)
#defineoffsetof(type,MEMBER ) ) size_t ) ) TYPE *)-MEMBER ) ) ) ) )。
首先理解offsetof这个宏函数。 读了很多博客终于理解了。
假设(TYPE * )0 :即虚拟地址0存在,并将地址强制转换为结构指针类型(TYPE * ) 0。 地址0到sizeof[type]-1长度的存储器区域可以视为一个结构的存储器。 0是此虚构结构的起始地址,后跟虚构结构的成员。 这些成员没有实际内容,但他们之间的地址结构与普通赋值结构相同。
使用此虚构结构地址,只需确定成员相对于结构起始地址的偏移量。 选择“0”作为虚拟地址也很方便,因为每个成员所在的虚拟内存地址的位置是偏移量。 当然也可以设定为其他值,但最后进行减法运算才是真正的偏移。
下面的步骤简单说明。
代码清单:
# include stdio.h # define offset of (type,MEMBER ) ) int ) ) TYPE * )0)-MEMBER ) struct unit{char a; 短乙; int c; 双精度d; (; intmain(intargc,char const *argv[] ) {结构单元单元; printf (单元结构所占字节数%d(n ),sizeof )结构单元); int offset1=offset of (结构单元,a ); int offset2=offset of (结构单元,b ); int offset3=offset of (结构单元,c ); int offset4=offset of (结构单元,d ); printf (单元结构成员a的偏移地址为%dn )、offset1); printf (单元结构成员b的偏移地址为%dn )、offset2); printf (单元结构成员c的偏移地址为%dn )、offset3); printf (单元结构成员d的偏移地址为%dn )、offset4); 返回0; }最后输出结果:
由此,也能够推出结构体unit存储器存储的概略结构
char为1字节,short为2字节,int为4字节,double为8字节,null部分不保存内容只是为了实现字节对齐
显示了整个空间占16字节,为什么sizeof (单元)为16。
container_of ) )让我们了解一下函数的实现。
此函数用于根据特定结构的特定成员获取该结构的起始地址。
在所有版本中,第一步是创建新的指针变量__mptr,并将它分配给特定结构成员的地址(ptr )
从该__mptr减去结构成员的偏移量(根据offsetof函数计算的结果)得到的是该具体结构的具体起始地址。 强烈转换为所需的结构类型。
举个例子吧。
代码清单:
# include stdio.h # define offset of (type,MEMBER ) (int ) ) TYPE * )0)-MEMBER ) definecontainer_of1 ) ptr,ttr }#definecontainer_of2(ptr、type、member ) (consttypeof ) ) (type )0)-member ) _ _ mptr=(ptr ); () (type* ) __mptr-offsetof ) type,member ) ); } (结构单元) chara; 短乙; int c; 双精度d; (; intmain(intargc,char const * argv [ ] (结构单元*单元_ by _ container1); 从container_of1函数获取,成员为astruct unit *unit_by_container2; 从container_of2函数获取,成员为astruct unit *unit_by_container3; 从container_of1函数获取,成员为bstruct unit unit_test={'x ',1,2,3.3 }; char *unit_a=unit_test.a; short *unit_b=unit_test.b; int *unit_c=unit_test.c; double *unit_d=unit_test.d; unit _ by _ container1=container _ of1 (unit _ a,struct unit,a ); unit _ by _ container2=container _ of2 (unit _ a,struct unit,a ); unit _ by _ container3=container _ of1 (unit _ b,struct unit,b ); printf(unit_by_container1-b为%un )、unit_by_container1-b ); printf(unit_by_container1-c为%dn ',unit_by_container1-c ); printf(unit_by_container1-d为%fn ',unit_by_container1-d ); printf(unit_by_container2-b为%un ',unit_by_container2-b ); printf(unit_by_container2-c为%dn ',unit_by_container2-c ); printf(unit_by_container2-d为%fn ',unit_by_container2-d ); printf(unit_by_container3-a为%cn ',unit_by_container3-a ); printf(unit_by_container3-c为%dn ',unit_by_container3-c ); printf(unit_by_container3-d为%fn ',unit_by_container3-d ); 返回0; }执行结果:
可以看到前面提到的两个container_of版本达到了相同的效果。
由此,我相信对这两个函数会有很深的理解。