31.1.offsetof宏
(1)作用:用宏来计算结构体中某个元素和结构体首地址的偏移量(其本质是通过编译器来帮我们计算)。
(2)原理:我们虚拟1个type类型结构体变量,然后用type.member的方式来访问某个member元素,继而得到member相对于整个变量首地址的偏移量。
(3)学习思路:第1步先学会用offsetof宏;第2步再去理解这个宏的实现原理(见图1)。
31.2.container_of宏
(1)作用:知道某个结构体中某个元素的指针,反推整个结构体变量的指针,继而得到结构体中其它元素的指针。
(2)typeof关键字:typeof(a)即由变量a得到a的类型,typeof就是由变量名得到变量的数据类型。
(3)原理:先用typeof得到member元素的类型,然后通过该类型定义指向member元素的指针,然后用该指针减去该元素相对于整个结构体变量的偏移量(偏移量用offsetof宏得到)后得到整个结构体变量的首地址,再把该地址强制类型转换为type *即可。
(3)学习思路:第1步先学会用container_of宏;第2步再去理解这个宏的实现原理(见图2)。
31.3.学习指南和要求
(1)基本要求:必须要掌握这两个宏的使用,即知道这两个宏接收什么参数,返回什么值,会用这两个宏来写代码,看见代码中别人用这两个宏能理解什么意思。
(2)升级要求:能理解这两个宏的工作原理,能表述出来,有些面试笔试题会这么要求。
(3)高级要求:能自己写出这两个宏,不要着急,慢慢来。
31.struct_macro
/*
* 公司:XXXX
* 作者:Rston
* 博客:http://blog.csdn.net/rston
* GitHub:https://github.com/rston
* 项目:offsetof宏与container_of宏
* 功能:演示offsetof宏与container_of宏的使用。
*/
#include <stdio.h>
// offsetof宏:返回MEMBER元素相对于整个TYPE类型结构体首地址的偏移量
// 参数:TYPE为某个结构体类型,MEMBER为TYPE类型结构体中的某个元素
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
// container_of宏:返回整个结构体变量的指针
// 参数:ptr为指向结构体中member元素的指针,type为某个结构体类型,member为该结构体中的某个元素
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
struct mystruct
{
char a; // 偏移量0
int b; // 偏移量4
short c; // 偏移量8
};
int main(int argc, char **argv)
{
// 手动计算偏移量,然后通过指针方式访问结构体元素
struct mystruct s1 =
{
.a = 'c',
.b = 20,
.c = 8,
};
char *p1 = (char *)&s1;
int *p2 = (int *)((char *)&s1 + 4);
short int *p3 = (short int *)((char *)&s1 + 8);
// *p1 = c. *p2 = 20. *p3 = 8.
printf("*p1 = %c. *p2 = %d. *p3 = %d.\n", *p1, *p2, *p3);
// 使用offsetof宏
// offsetof(struct mystruct, a) = 0.
// (char *)&(s1.a) - (char *)&s1 = 0.
printf("offsetof(struct mystruct, a) = %d.\n", offsetof(struct mystruct, a));
printf("(char *)&(s1.a) - (char *)&s1 = %d.\n", (char *)&(s1.a) - (char *)&s1);
// offsetof(struct mystruct, b) = 4.
// (char *)&(s1.b) - (char *)&s1 = 4.
printf("offsetof(struct mystruct, b) = %d.\n", offsetof(struct mystruct, b));
printf("(char *)&(s1.b) - (char *)&s1 = %d.\n", (char *)&(s1.b) - (char *)&s1);
// offsetof(struct mystruct, c) = 8.
// (char *)&(s1.c) - (char *)&s1 = 8.
printf("offsetof(struct mystruct, c) = %d.\n", offsetof(struct mystruct, c));
printf("(char *)&(s1.c) - (char *)&s1 = %d.\n", (char *)&(s1.c) - (char *)&s1);
// 使用container_of宏
struct mystruct s2 =
{
.a = 'd',
.b = 30,
.c = 5,
};
// pS = 0xbfcc8350. &s2 = 0xbfcc8350.
struct mystruct *pS = container_of(&(s2.a), struct mystruct, a);
printf("pS = %p. &s2 = %p.\n", pS, &s2);
return 0;
}