container_of()宏,太妙了~

描述

 

container_of(ptr, type, member)宏的作用

该宏的作用是通过结构体成员的地址和结构体类型推导出结构体的地址,type是指结构体的类型,member是成员在结构体中的名字,ptr是该成员在type结构体中的地址。

container_of(ptr, type, member)宏解析

在 linux 源码的 toolsincludelinuxkernel.h文件下,container_of()的定义如下:

#ifndef container_of
/**
* 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)); })
#endif

container_of() 宏的定义中的 offsetof(TYPE, MEMBER)typeof() 初学者可能会对其很陌生,所以我们要先从理解 offsetof(TYPE, MEMBER)typeof() 的作用开始。

offsetof(TYPE, MEMBER)

本质也是个宏定义,在 linux 源码的 toolsincludelinuxkernel.h 文件下定义如下:

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

offsetof 宏中的 TYPE 是指结构体的类型,MEMBER 是指结构体中的某个成员,作用是求出该成员的在该结构体中的偏移量。该宏首先将 0 (地址0)转化为 TYPE * 的结构体指针,表示地址为 0 的结构体指针,然后通过取地址符 &((TYPE *)0)->MEMBER) 取出该结构体指针中 MEMBER 成员的地址,最后再将地址值强转为 size_t 类型(内核中为 unsigned long 类型)即表示 MEMBER 成员在结构体中的偏移量。要理解该过程需要了解对结构体的内存分布,如图,结构体的内存分配是连续的,当结构体的地址为0时,成员的地址即为该成员的偏移量。

源码

实例:

#include 

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

typedef struct _offset
{
 char member_0;
 int member_1;
 char member_2;
}offset;

int main()
{
 printf("%d
", offsetof(offset, member_0));
 printf("%d
", offsetof(offset, member_1));
 printf("%d
", offsetof(offset, member_2));
 

 return 0;

}

输出:

源码

offsetof实例结果输出

typeof()

typeof()GNU C 中的一个关键字,和 sizeof() 一样都是 C 语言中的关键字而不是函数。作用是返回传入数据的类型。实例:

#include 

int main()
{
 int a = 3;
 typeof(a) b = a; /* 求出a变量的类型,并创建一个b变量 */
 

  printf("a=%d b=%d", a, b);

  return 0;
}

输出:

源码

img

container_of(ptr, type, member)

了解了 offsetof() 宏和 typeof 关键字之后就比较好理解 container_of 宏的作用了。

const typeof(((type *)0)->member) * __mptr = (ptr)

该代码的作用实际上是将 0 转化为 type * 结构体类型,再取出结构体中的MEMBER成员 (type *)0)->member, 再通过 typeof 关键字获取 MEMBER 成员的类型,并定义一个 MEMBER 成员类型的指针 const typeof(((type *)0)->member) * __mptr,将传入的 ptr 指针赋值给 __mptr__mptr = (ptr)

(type *)((char *)__mptr - offsetof(type, member));

该代码是将获取的 MEMBER 成员地址强转为 char *(强转的目的是考虑指针的加减的实质是指针在内存的偏移,偏移量为指针类型所占字节的个数),减去 MEMBER 成员在 type 结构体中的偏移量,强转为 type * 后得到结构体的地址。实例:

#include 

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

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

typedef struct _container 
{
 char member_0;
 int member_1;
 char member_2;
}container;

int main(void)
{
 container *a = NULL;
 container b = {'a', 2, 'b'}; 

  /* member_1在实例结构体中的地址  结构体类型 成员名 */
  a = container_of(&b.member_1, container, member_1);
  printf("a->member_0 = %c
", a->member_0);
  printf("a->member_1 = %d
", a->member_1);
  printf("a->member_2 = %c
", a->member_2);

  return 0;
}

输出:

源码

 

 

 


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分