container_of 和 offsetof 和 typeof

在驱动中经常会用到两个宏 他们分别是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;
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/bin_zhang1/article/details/80926119