#define offsetof(s, m) (size_t)&( ((s*)0) -> m )
这个宏的定义在stddef.h中定义。
分析:
首先将0强制类型转化为结构体指针类型s*,此时的零的类型就是 s*,那么其当然可以访问其成员m(通过(s*)0 -> m 的方式访问),那么此时再取这个变量的地址,即 & ((s*)0->m),这个地址呢减去结构体的基地址就是我们要求的偏移量。
结构体的基地址是什么呢,不就是(s*)0吗?那么(s*)0等于多少?不就是零嘛(你再怎么类型转化,0就是零,只不过这个指针在加减1时其加减的字节数目不一样了而已)。关于这一点的测试,见:
#include <iostream>
using namespace std;
int main()
{
int *p = (int*)0;
cout << p << endl; //输出为:0
return 0;
}
现在就很简单了,我们要求的偏移量等于& ((s*)0->m) - 0 = & ((s*)0->m),然后再将这个偏移量强制类型转化为size_t类型,size_t也是一个宏,就是基本数字类型,int或者long。
扩展
这个宏是很有用的,在linux内核中,有很多不同的结构通过一个list_head结构链接在一起,这些不同的结构都有一个list_head类型的成员变量(下文我们称这些不同的结构为容器),我们在访问 这些容器内的数据时,其实是通过list_head经过一定的操作来访问的,所谓的操作就是指通过list_head来找到以list_head为成员变量的 容器 的基地址。
怎么找呢?
通过offsetof找啊,offsetof能够得到list_head结构相对于其容器的偏移量,然后将list_head的地址减去这个偏移量就得到了其容器的基地址,有了这个基地址,再去访问容器内的变量就很简单了,就通过“ -> “ 操作就行了。关于内核中的list_head链表这个结构的详细介绍见:linux内核链表以及list_entry–linux内核数据结构
参考:
嵌入式高级c语言 P29