我们经常遇到一种场景,比如把 链表结点或者队列结点 放入到 数据结构体 中,当我们知道结点地址,想要得到数据结构的地址,下面这个宏可以出场了。
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )
举例:
#include<stdio.h> typedef struct _test { char i; int j; char k; }Test; int main() { Test *p = 0; printf("%p ", &(p->k)); }
大概可以总结为以下4步:
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型.巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
结果很好,但是自己有两个疑问:
1.结构体中,成员变量表示的地址在内存中是怎样分布得,会以什么结果显示?
2.强转0指针,上面只做第二步操作会不会挂掉?
举例:
struct item_s{ void *data; struct item_s *next; }; int main(int argc,char **argv){ struct item_s item_ins={0}; struct item_s *itemp_ins=NULL; 1.printf(&item_ins); 2.printf(&(item_ins.is_tmp)); 3.printf(itemp_ins->is_tmp); 4.printf("%x\n",&(itemp_ins->is_tmp)) }
然后疑问解决,通过1 2结果对比,会发现,他们的差值即为 成员地址相对结构体地址 的偏移量。通过3 4结果对比,会发现,在一个地址为0x0上是不能直接做取操作得,但是通过取地址符操作可以取得偏移量。