TYPE应该是一个结构体类型,MEMBER是结构体中的一个成员。
使用了这个宏之后可以得到MEMBER在TYPE中的偏移量。
0被强制类型转换成TYPE指针。
在计算机的0地址处没有TYPE结构体,0地址是留给操作系统使用的。
我们存在的疑问是将0地址转换为TYPE类型的指针,然后取MEMBER成员,这里会引起崩溃吗?
编译器到底做了什么?
&((TYPE*)0)->MEMBER 这个语句就是根据结构体的首地址和相应成员的偏移量获得成员的具体地址,这里我们将0转换为TYPE类型的指针,最终获得的成员的绝对地址和偏移地址是一样的。这句话并没有真正的去内存中取MEMBER这个成员。
示例程序:
1 #include <stdio.h> 2 3 4 struct ST 5 { 6 int i; // 0 7 int j; // 4 8 char c; // 8 9 }; 10 11 void func(struct ST* pst) 12 { 13 int* pi = &(pst->i); // 0 14 int* pj = &(pst->j); // 4 15 char* pc = &(pst->c); // 8 16 17 printf("pst = %p\n", pst); 18 printf("pi = %p\n", pi); 19 printf("pj = %p\n", pj); 20 printf("pc = %p\n", pc); 21 } 22 23 int main() 24 { 25 struct ST s = {0}; 26 27 func(&s); 28 29 return 0; 30 }
结果如下:
以空指针调用:
1 #include <stdio.h> 2 3 4 struct ST 5 { 6 int i; // 0 7 int j; // 4 8 char c; // 8 9 }; 10 11 void func(struct ST* pst) 12 { 13 int* pi = &(pst->i); // 0 14 int* pj = &(pst->j); // 4 15 char* pc = &(pst->c); // 8 16 17 printf("pst = %p\n", pst); 18 printf("pi = %p\n", pi); 19 printf("pj = %p\n", pj); 20 printf("pc = %p\n", pc); 21 } 22 23 int main() 24 { 25 struct ST s = {0}; 26 27 func(&s); 28 func(NULL); 29 30 return 0; 31 }
结果如下:
空指针调用func并没有崩溃,而且得到了想要的结果。
可见编译器只是计算成员的地址,并没有实际去取成员的值。也没有访问内存,只是做了一些计算。
offsetof的使用示例:
1 #include <stdio.h> 2 3 #ifndef offsetof 4 #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER) 5 #endif 6 7 8 struct ST 9 { 10 int i; // 0 11 int j; // 4 12 char c; // 8 13 }; 14 15 void func(struct ST* pst) 16 { 17 int* pi = &(pst->i); // 0 18 int* pj = &(pst->j); // 4 19 char* pc = &(pst->c); // 8 20 21 printf("pst = %p\n", pst); 22 printf("pi = %p\n", pi); 23 printf("pj = %p\n", pj); 24 printf("pc = %p\n", pc); 25 } 26 27 int main() 28 { 29 struct ST s = {0}; 30 31 func(&s); 32 func(NULL); 33 34 printf("offset i: %d\n", offsetof(struct ST, i)); 35 printf("offset j: %d\n", offsetof(struct ST, j)); 36 printf("offset c: %d\n", offsetof(struct ST, c)); 37 38 return 0; 39 }
结果如下: