对linux内核的container_of和offsetof宏的理解

linux内核中存在container_of宏,其定义如下:

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:        指向结构体中member这个成员的指针.
 * @type:       表示此结构体的类型.
 * @member:     所求结构体中的某个成员,ptr参数为其地址.
 *
 */
#define container_of(ptr, type, member) ({                      \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

通过此宏可以通过某个结构体中某个已知成员(member)的首地址获得整个结构体的首地址。宏定义中巧妙的使用了0地址。
一、 const typeof( ((type *)0)->member ) *__mptr = (ptr);
1、typeof( ((type *)0)->member )中typeof是GNU C对标准C的扩展,在此处的作用是获取结构体中member 成员的类型。
2、再定义一个member参数类型相同的__mptr参数:const typeof( ((type *)0)->member ) *__mptr。
3、最后再将ptr的地址赋值给__mptr。至此使用了临时变量保存了ptr的地址。

二、 (type )( (char )__mptr - offsetof(type,member) );
此结构中有一个offsetof宏,定义如下:

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

offsetof宏定义中的&((TYPE )0)->MEMBER也使用了0地址,将0地址强制转换为TYPE 类型,再求得MEMBER成员相对于0地址的偏移,最后将此偏移地址强制转化为整形:((size_t) &((TYPE *)0)->MEMBER)。
所以第二步利用offsetof(type,member) )求得member成员相对type首地址偏移后,再使用ptr地址减去偏移就是结构体类型的首地址了。
对linux内核的container_of和offsetof宏的理解_第1张图片
综上所述,在container_of和offsetof都使用了0地址才实现了目的。其实不使用0地址也可以达到同样的目的,只不过container_of和offsetofde这个地址要保持一致。例如下面代码对这两个宏进行了略微的更改,也能达到一样的效果。

#include

/*略微的改动,增加了base参数,可以用来改变相对地址,当base=0时,和内核宏一样。*/
#define offsetof(BASE,TYPE, MEMBER) ((size_t) &((TYPE *)BASE)->MEMBER-BASE)
#define container_of(base,ptr, type, member) ({                      \
        const typeof( ((type *)base)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(base,type,member) );})

struct data_info
{
        int i;
        char ch;
        float f;
};

int main(void)
{
        struct data_info temp_st={3,'a',1.5};
        int base = 7;
        printf("&temp_st=0x%02x\n",&temp_st);

        printf("container_of=0x%02x\n",container_of(base,&temp_st.ch,struct data_info,ch));

        return 0;
}

上述代码执行结果如下截图,可以看到求出的值和结构体的起始值一致:
这里写图片描述

你可能感兴趣的:(对linux内核的container_of和offsetof宏的理解)