linux——内核中container_of宏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014470361/article/details/82561526

前言

  在查看linux内核中,经常会看到container_of这个宏定义,第一眼看到这个宏定义中括号一层又一层,晕了。下面本文就来好好分析这个宏的具体实现。
内核版本:linux-2.6.34

container_of宏详解

  container_of宏的源码定义在kernel.h文件中,具体如下:

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

  下面先来看宏定义中的三个参数,如下图:
这里写图片描述
  member是type结构体中的成员,而指针ptr指向member成员,那么container_of宏的作用是在type结构体类型已知和指向member成员的指针ptr已知的情况下得到type结构体的首地址。
  其中,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。
  下面先看第一句:const typeof( ((type *)0)->member ) *__mptr = (ptr);

  1. 首先把0强制转换为type*型的指针;
  2. 然后取指针指向的type类型结构体中的member;那么这里(type )0)岂不是(type )NULL,不可以对空指针引用啊。那么我的个人理解是NULL指针不能被解引用(NULL)而不是NULL指针不能使用,这里 ((type )NULL)->member只是用空指针和空指针指向的type结构体获得member成员,而并没有直接去读null地址的数据,换句话说这里只是使用 了NULL但并未进行 (*NULL)操作
  3. 接着用typeof得到上述member成员的类型;
  4. 最后把ptr赋值给__mptr,__mptr是指向member类型的一个指针。例如member是int类型,那么上述语句就等价于const int * __mptr = (ptr)语句,typeof( ((type *)0)->member )是获得member成员的类型。
      接着来看第二句,offsetof的源码定义在stddef.h文件中,如下:
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

  其中__compiler_offsetof的定义在compiler_gcc4.h文件中:

#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)

  __builtin_offsetof(a,b)是GCC的内置函数,可认为它的实现与((size_t) &((TYPE )0)->MEMBER)这段代码是一致的。那么((size_t) &((TYPE )0)->MEMBER)这句具体什么意思呢?
1. 首先把0强制转换为type*型的指针;
2. 然后取指针指向的type类型结构体中的member成员;
3. 接着用取地址符号&取member成员的地址,这个地址的值就是offset值;
4. 最后把menber地址强制转换成(size_t)类型。
offset值如下图:
这里写图片描述
  当type类型结构体的首地址为0,那么member成员的地址就是offset值。member的地址减去这个offset值就可得到type结构体首地址。
  所以(type )( (char )__mptr - offsetof(type,member) )的含义就是 (char )__mptr 减去offset值得到结构体首地址,并强制转化为(type )型。这里为什么要先把__mptr转化成(char *)是因为如果__mptr是其他类型,比如是int型,用__mptr 直接减 offsetof(type,member)实际上是减了4 X offsetof(type,member)个字节了,而不是减去 offsetof(type,member)个字节,就会引起错误。

实例测试

#include <stdio.h>

struct test_struct {
    int num;
    char ch;
    float f;
};

int main(void)
{
    printf("offsetof(struct test_struct, num) = %d\n", 
            offsetof(struct test_struct, num));

    printf("offsetof(struct test_struct,  ch) = %d\n", 
            offsetof(struct test_struct, ch));

    printf("offsetof(struct test_struct,   f) = %d\n", 
            offsetof(struct test_struct, f));

    return 0;
 }

输出;

offsetof(struct test_struct, num) = 0
offsetof(struct test_struct,  ch) = 4
offsetof(struct test_struct,   f) = 8

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/82561526