在驱动中经常会用到两个宏 他们分别是container_of 和offsetof
#define offsetof(type, member) (size_t)&(((type*)0)->member)
我们来分析下这个宏定义传进去一个结构体类型和成员变量,返回这个成员变量的偏移值
(type*)0 //将0地址强制类型转换成type *类型,我们知道强制类型转换改变的是解析这段内存的方式,也可以说是步长
((type*)0)->member // 然后取他的成员变量
(size_t)&(((type*)0)->member) //最后取这个成员的地址并且转成size_t类型
精妙之处在与取0地址处做强转
(size_t)&(((type*)xxx)->member),当然你可以随便取一个地址做强转,但是由于你不知道基地址,
所以你没有办法算出来这个成员变量的偏移量。
感受到其中的精妙之处了吗?
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这个宏定义是传进去一个成员变量的地址,结构体类型,还有这个结构体类型,最后得到这个结构体指针,也就是这个结构体的基地址,
其原理也很简单:
typeof( ((type *)0)->member ) *__mptr = (ptr) //这里讲传进来的成员地址复制给__mptr
这里顺便说一下typeof这个宏
typeof是获取参数类型,可以检查一个变量是否存在,是否有值,typeof在两种情况下会返回”undefined”—-有一个变量没有被声明的时候,和一个变量的值是undefined的时候:
typeof(int *) ip 就等价于 int *ip
typeof(ip) ip_c 就等价于 int *ip_c
好了继续分析container_of
(type *)( (char *)__mptr - offsetof(type,member) )
__mptr的地址减去这个成员变量的偏移量,自然就等于这个结构体的基地址了
可能看解释会感觉很糊涂,来点代码
#include <stdio.h>
#include <stdlib.h>
#define offsetof(type, member) (size_t)&(((type*)0)->member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typedef struct
{
int type;
char name[64];
int age;
}dog;
int main()
{
int off_set = 0;
off_set = offsetof(dog,age);
printf("age offset: %d\n",off_set);
dog *Ty = (dog *)malloc(sizeof(dog));
dog *cof_re = container_of(&(Ty->age) , dog, age);
printf("ty addr = %p cof_re =%p\n",Ty,cof_re );
return 0;
}