container of()
在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member)
已知 结构体 type 的成员 member 的地址 ptr,求解结构体type的起始地址。
type的起始地址 = ptr - size (这里需要都转换为char *,因为它为单位字节)。
container of函数原型:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );} )
其次为 offserof 函数原型:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
(一)0 指针的使用 :
#include<stdio.h>
struct test
{
char i ;
int j;
char k;
};
int main()
{
struct test temp;
printf("&temp = %p\n", &temp);
printf("&temp.k = %p\n", &temp.k);
printf("& ((struct test *)0)->k = %d\n", ( (int)& ((struct test *)0)->k ) );
}
编译运行,可以得到如下结果:
&temp = 0xbf9815b4
&temp.k = 0xbf9815bc
&((struct test *)0)->k = 8
什么意思看到了吧,自定义的结构体有三个变量:i,j,k。
因为有字节对齐要求,所以该结构体大小为4bytes * 3 =12 bytes.
而&((struct test *)0)->k 的作用就是求 k到结构体temp起始地址的字节数大小(就是我们的size)。
在这里0被强制转化为struct test *型, 它的作用就是作为指向该结构体起始地址的指针,就是作为指向该结构体起始地址的指针,就是作为指向该结构体起始地址的指针, 而&((struct test *)0)->k 的作用便是求k到该起始指针的字节数。。。
其实是求相对地址,起始地址为0,则&k的值便是size大小(注:打印时因为需要整型,所以有个int强转)所以我们便可以求我们需要的 size 了 。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
所以offsetof()的作用就是求我们梦寐以求的size, 并以size_t形式返回(size_t: 无符号整型)。
(二) 内核编程的严谨性
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这里我们只看第二行:
const typeof( ((type *)0)->member ) *__mptr = (ptr);
它的作用是什么呢? 其实没什么作用,但就形式而言 _mptr = ptr, 那为什么要要定义一个一样的变量呢???
如果开发者使用时输入的参数有问题:ptr与member类型不匹配,编译时便会有warnning, 但是如果去掉改行,那个就没有了,而这个警告恰恰是必须的(防止出错有不知道错误在哪里)。。。这严谨性可以吧
typeof( ((type *)0)->member )
它的作用是获取member的类型仅此而已。至此基本结束
(三) 总结
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/s2603898260/article/details/79371024