分享

container_of 那点事

 studydoer 2019-12-16

container_of 和 sizeof一样,都是编程世界中的小贝壳,但用处颇大,且使用频率很高,特别是在内核中的应用更广泛,下面就来做下介绍。

一、定义:

//获取结构体成员相对于结构体的偏移 #define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER) //通过获取结构体中的某个成员,反推该结构体的指针 #define container_of(ptr, type , member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr) ; \  (type *)((char *)__mptr - offsetof(type,member)) ;})

二、作用

container_of的作用是根据结构体中某成员的地址、结构体的类型和成员字段名来求解该结构体的首地址,看下面的解析。

三、解析

const typeof(((type *)0)->member) *__mptr = (ptr) ;

第一阶段:

假定有一个该类型的结构体首地址是0,(type*)0;

然后用typeof求解出该结构体成员member的数据类型,

typeof(((type *)0)->member),接着用该类型定义一个变量,__mptr,直接给其赋值传进来的参数ptr;

第二阶段:

(char*)__mptr是一个内存中的地址,且是成员字段变量的首地址,那么用这个地址,减去成员字段和结构体地址的偏差就得到了结构体的首地址了,到这一步能理解码?其实,offsetof(type,member)就是求偏差的,来看看它。

第三阶段:

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

其实地址是四字节的数,而int类型也是四字节,offsetof直接用假定的0地址处的结构体来求的偏差,要知道偏差在同类型的结构体中都是一样的,把结构体地址放在0地址,那么偏差就是成员字段的地址了。

有没有一个疑问?

0地址能用来假定吗?0地址不是内核地址吗?

要知道container_of是一个宏定义,在编译阶段就展开了,0地址对于编译器来说是死的,不是cpu的运行地址,好理解了吧?看下面两段代码!

int main(int argc, char *argv[]){ struct test{ int num; char ch; }t1={100,'c'}; char *pch=&t1.ch; struct test *ptt=container_of(pch,struct test,ch); printf("num=%d\n",ptt->num);

return 0;}

int main(int argc, char *argv[]){ struct test{     int num;     char ch;   }t1={100,'c'}; char *pch=&t1.ch; struct test *ptt=({ const typeof(((struct test *)0)->ch) *__ptmp=(pch); (struct test *)((char *)__ptmp - ((size_t) &((struct test *)0)->ch)); }); printf("num=%d\n",ptt->num);

return 0;}

一个开卷有益的公众号:IT平头哥

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多