offsetof宏:
通过前面的学习,我们知道通过结构体整体变量来访问其中的各个元素,在本质上是通过指针的方式来访问的,在形式上通过 . 的方式来访问的(实际上是编译器自动计算了偏移量)
offsetof宏的作用:通过这个宏来计算结构体中某个元素和结构体首地址的偏移量
offsetof宏的原理:我们虚拟一个type类型结构体的变量,然后用type.member的方式来访问member元素,继而得到了member相对于整个变量首地址的偏移量
offsetof宏:
//offsetof宏:这个宏的返回值是member元素相对于整个结构体变量的首地址的偏移量,类型是int
//type是结构体类型 , member是结构体中的一个元素名
#define offsetof(type,member) ((int) &((type *)0)->member)
offsetof宏的学习思路:对应指针和结构体不是很熟练的同学很难看到这个宏,第一我们先学会怎么使用offsetof宏,第二在去理解offsetof是怎么实现计算结构体的偏移量的。
offsetof宏解析:
offsetof实现方式类似于我下面的代码,就是通过减法计算出偏移量,要记住在offsetof宏中的被减数永远结构体的首地址。
printf("&s1的首地址为:%p\r\n", &s1);
printf("&s1.b地址为:%p\r\n", &s1.b);
printf("偏移量是:%d\r\n", (int)&(s1.b) - (int)(&s1));
下面我一步步解析offsetof这个宏
(type *)0:
这是一个强制类型转换,把0地址强制类型转换成一个指针,这个指针指向一个type类型的结构体变量(这个type的结构体可能存在也可能不存在,但是只要我们不去解引用这指针就不会出错)
((type *)0->member):
(type * )0是一个type类型的结构体变量的指针,通过指针的来访问这个结构体变量的member
&((type *)0->member):
&((type * )0->member)解剖的一个过程是:(type *)0 ->member >&((type *)0->member),目的就是得到member元素的地址,因为整个结构体变量的首地址是0
container_of宏:
container_of宏的作用:
知道结构体中的某个元素的指针,反推这个结构体变量的指针。通过container_of宏,我们可以从一个元素的指针得到整个结构体变量的指针(结构体的首地址),继而得到结构体其他元素的指针
container_of宏的原型:
#define container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
container_of宏的解析:
typeof关键字的作用:typeof(a)时通过变量a得到a的数据类型
container_of宏的原理:先通过typeof关键字得到member元素的类型定义成一个指针,然后用这个指针减去该元素相对与整个结构体变量的偏移量(通过offsetof宏得到的),减去之后得到的就是整个结构体的是首地址,再把这个地址强制转类型转换成type*即可。
typeof( ((type *)0)->member ) *__mptr = (ptr): 首先定义了一个临时的数据类型(通过 typedef(((type *)0)->member)获得)与ptr相同的指针变量_mptr,然后用它来保存ptr的值
用(char *)__mptr减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)
学习指南:
最基本的要求:必须会这两个宏的使用,能看到别人使用这个宏的时候是什么意思
进一步要求:能理解这两宏的工作原理,表述出来。(这个得慢慢来)
更高级要求:能写出这两个宏。