神奇的container_of()宏

Linux内核的神奇的container_of()宏


 

1. 用途

container_of()宏可以跟据结构体成员的地址返回结构体的地址。

2. 定义

Linux内核中list即链表结构有个宏container_of(),其定义(linux-2.6.11/include/linux/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) );})

相关的offsetof()宏定义(linux-2.6.11/include/linux/list.h)为:

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

3. 如何理解

3.1 offsetof()宏

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

我们一层层解析这个宏:

        ((TYPE *)0)        :  将0强制转换为指向TYPE类型的结构体的指针

        ((TYPE *)0)->MEMBER  :  通过上述结构体指针,得到结构体成员MEMBER

      &((TYPE *)0)->MEMBER  :  对结构体成员取地址操作,得到成员地址

  ((size_t) &((TYPE *)0)->MEMBER)    :  强制转化为size_t类型

这里面比较难以理解的是对0的相关操作,实际上编译器在解析这个宏的时候,并不会真的对0做以上操作,只是获取到该结构体的类型,然后将MEMBER成员在结构体内的偏移地址加上0后返回,即编译器返回的是MEMBER成员的结构体内偏移地址。可以使用以下代码验证:

 1 #include <stdio.h>
 2 
 3 #define offsetof0(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 4 #define offsetof1(TYPE, MEMBER) ((size_t) &((TYPE *)1)->MEMBER)
 5 
 6 struct data_stu{
 7     int data;
 8     char name[10];
 9 };
10 
11 int main(void){
12     printf("data: %d\t", offsetof0(struct data_stu, data));
13     printf("name: %d.\n", offsetof0(struct data_stu, name));
14 
15     printf("data: %d\t", offsetof1(struct data_stu, data));
16     printf("name: %d.\n", offsetof1(struct data_stu, name));
17 
18     return 0;
19 }

运行结果:

    

3.2 container_of()宏

下面我们再来看看container_of()宏

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

宏的第一行定义了一个member类型的指针,并赋值为该结构体成员member的地址。听起来有点怪?

宏的第二行将mptr的地址减去其在结构体内的偏移,从而得到结构体的内存地址。

需要说明的几点:

  •  typeof(x)出自GCC, 返回x的类型。详细可参考GCC文档说明
    int a;
    typeof(a) b; //b is a int
  • 为何要定义mptr

   为了类型检查。

4. 参考文档

  参见大神文档 https://radek.io/2012/11/10/magical-container_of-macro/

猜你喜欢

转载自www.cnblogs.com/Freeee/p/12316242.html