说明: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; }
执行结果如下: