1 offsetof宏的原理以及作用
在使用container_of宏之前,我们先来了解下offsetof这个宏,它在Linux内核里的源码是这个样子:
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
1.1 offsetof宏的工作原理
虚拟一个TYPE
类型的结构体变量,通过TYPE.MEMBER
的方式来访问MEMBER
成员,进而得到MEMBER
成员相对于整个结构体首地址的偏移量。
这句话理解起来看似很抽象,&((TYPE *)0)->MEMBER
相当于得到了成员的偏移减去0地址偏移,也就是结构体的首地址,进而就得到了该成员相当于整个结构体的偏移量,接下来写一个例子就明白了:
1.2 offsetof宏编程案例
#include <stdio.h> #define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER) #pragma pack(4) struct ptr { char a ; short b ; int c ; double d ; }; #pragma pack() int main(void) { struct ptr Pt ; printf("ptr:%d\n",sizeof(struct ptr)); //相对地址偏移量 int offset = offsetof(struct ptr,a); printf("offset:%d\n",offset); int offset1 = offsetof(struct ptr,b); printf("offset1:%d\n",offset1); int offset2 = offsetof(struct ptr,c); printf("offset2:%d\n",offset2); int offset3 = offsetof(struct ptr,d); printf("offset3:%d\n",offset3); return 0 ; }
运行结果:
在案例中,我们以默认4字节对齐得到的4个结构体变量在结构体中的偏移,明白了offsetof
宏如何使用,就解决了我们的大疑问了,我们来看看container_of
宏怎么使用吧。
2 Linux container_of宏的原理以及作用
Linux中的container_of
宏长如下这个样子,那它有什么作用呢?我们来详细剖析一下。
#define container_of(ptr, type , member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr) ; \ (type *)((char *)__mptr - offsetof(type,member)) ;})
2.1 container_of宏的工作原理
先用typeof
获取变量的数据类型,也就是member
成员的类型,然后将member
这个成员 的指针转成自己类型的指针,再从offsetof相减,就得到整个结构体变量的首地址了,再将该地址强制转化为type *
。
接下来写一个关于container_of
宏的编程案例:
2.2 container_of宏编程案例
#include <stdio.h> #include <stdlib.h> //获取结构体成员相对于结构体的偏移 #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)) ;}) //让结构体默认以四字节对齐 #pragma pack(4) struct ptr { char a ; short b ; int c ; double d ; }; #pragma pack() int main(void) { struct ptr Pt ; struct ptr *pt ; printf("ptr:%d\n",sizeof(struct ptr));//16 //获取结构体的首地址 printf("ptr:%p\n",&Pt); //0028FEA8 Pt.a = 'a'; Pt.b = 2 ; Pt.c = 4 ; Pt.d = 12.04 ; //通过container_of获取结构体的首地址 pt = container_of(&Pt.c, struct ptr , c); printf("pt:%p\n",pt); printf("a:%c\n",pt->a) ; printf("b:%d\n",pt->b) ; printf("c:%d\n",pt->c) ; printf("d:%.2lf\n",pt->d); return 0 ; }
运行结果: