offsetof宏与container_of宏

简介: offsetof宏与container_of宏

说明:Linux内核中这两个宏用的非常多,所以在这里总结下

 

1、offsetof宏

作用:计算一个结构体成员变量在这个结构体中的偏移量

         我之前写过一篇如何计算结构体偏移量的文章,虽然可以手工计算,但是结构体成员太多的话,手工肯定是不现实的,所以Linux定义了这样一个宏。

Linux kernel中的路径:include/linux/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
这里分析#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 

(1)offsetof宏的原理:我们虚拟一个type类型结构体变量,然后用type.member的方式来访问那个member元素,继而得到member相对于整个变量首地址的偏移量。

(2)学习思路:第一步先学会用offsetof宏,第二步再去理解这个宏的实现原理。

(TYPE *)0            这是一个强制类型转换,把0地址强制类型转换成一个指针,这个指针指向一个TYPE类型的结构体变量。       (实际上这个结构体变量可能不存在,但是只要我不去解引用这个指针就不会出错)。

((TYPE *)0)->MEMBER      (TYPE *)0是一个TYPE类型结构体变量的指针,通过指针来访问这个结构体变量的member元素

&((TYPE *)0)->MEMBER  等效于&(((TYPE *)0)->MEMBER),意义就是得到member元素的地址。但是因为整个结构体变量的首地址是0,

下面结合代码进行具体讲解:

#include <stdio.h>
 
struct mystruct
{
  char a;     // 0
  int b;      // 4
  short c;    // 8
};
 
// TYPE是结构体类型,MEMBER是结构体中一个元素的元素名
// 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
 
// ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名
// 这个宏返回的就是指向整个结构体变量的指针,类型是(type *)
#define container_of(ptr, type, member) ({      \
  const typeof(((type *)0)->member) * __mptr = (ptr); \
  (type *)((char *)__mptr - offsetof(type, member)); })
 
int main(void)
{
  //offsetof宏解析
  struct mystruct s1;
  s1.b = 12;
  
  int *p = (int *)((char *)&s1 + 4);
  printf("*p = %d.\n", *p);
  
  int offsetofa = offsetof(struct mystruct, a);
  printf("offsetofa = %d.\n", offsetofa);       //结构体中成员a相对于整个结构体的偏移量是0
  
  int offsetofb = offsetof(struct mystruct, b);
  printf("offsetofb = %d.\n", offsetofb);       //结构体中成员b相对于整个结构体的偏移量是4
  
  int offsetofc = offsetof(struct mystruct, c);
  printf("offsetofc = %d.\n", offsetofc);       //结构体中成员c相对于整个结构体的偏移量是8
  
  //offsetof宏计算偏移量的原理如下
  printf("整个结构体变量的首地址:%p.\n", &s1);   
  printf("s1.b的首地址:%p.\n", &(s1.b));
  printf("偏移量是:%d.\n", (char *)&(s1.b) - (char *)&s1);
      
  return 0;
}

执行结果如下:

 

2、container_of宏

作用:知道一个结构体中某个元素的指针,反推这个结构体变量的指针。有了container_of宏,我们可以从一个元素的指针得到整个结构体变量的指针,继而得到结构体中其他元素的指针。

路径:include/linux/kernel.h,宏的原型定义如下:

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

这个宏分析不清楚,只是知道用法。。。

下面结合代码进行解析:

#include <stdio.h>
 
struct mystruct
{
  char a;     // 0
  int b;      // 4
  short c;    // 8
};
 
// TYPE是结构体类型,MEMBER是结构体中一个元素的元素名
// 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
 
// ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名
// 这个宏返回的就是指向整个结构体变量的指针,类型是(type *)
#define container_of(ptr, type, member) ({      \
  const typeof(((type *)0)->member) * __mptr = (ptr); \
  (type *)((char *)__mptr - offsetof(type, member)); })
 
int main(void)
{
  struct mystruct s1;
  struct mystruct *pS = NULL;
  
  short *p = &(s1.c);   // p就是指向结构体中某个member的指针
  
  printf("s1的指针等于:%p.\n", &s1);
  
  //container_of宏解析
  // 问题是要通过p来计算得到s1的指针
  pS = container_of(p, struct mystruct, c);
  printf("pS等于:%p.\n", pS);
      
  return 0;
}

执行结果如下:


相关文章
|
2月前
宏#define命令练习2
宏#define命令练习2。
31 12
|
2月前
宏#define命令练习
宏#define命令练习。
32 9
|
2月前
宏#define命令练习3
宏#define命令练习3。
26 6
|
6月前
offsetof宏(想了解offsetof宏的使用,那么看这一篇就足够了!)
offsetof宏(想了解offsetof宏的使用,那么看这一篇就足够了!)
|
7月前
#define定义常量和宏
#define定义常量和宏
45 0
|
7月前
|
编译器 C语言 C++
define与const关键字的多种用法
define与const关键字的多种用法
77 0
|
编译器
#define 定义常量和宏
工作机理:直接替换,以下列代码为例编译器直接将用到max的地方替换为100
71 0
用#define宏实现Add函数
用#define宏实现Add函数
108 0
|
存储 安全 编译器
【为什么】C++中的宏
【为什么】C++中的宏
138 0
|
编译器
宏定义(#define)及相关知识
宏定义(#define)及相关知识
124 0