【C】取出结构中成员变量偏移位置的宏定义
昨天看到的一句代码,是用来取出结构中成员变量相对偏移位置的宏,如下:
#define OFFSET_OF_STRUCT(type, var) ((size_t)(&((type *)NULL)->var))
举个例子
#include <stdio.h> #define OFFSET_OF_STRUCT(type, var) ((size_t)(&((type *)NULL)->var)) typedef struct person { int age; char name[10]; long num1; float num2; double num3; int num4; } PERSON; int main() { size_t offset0 = OFFSET_OF_STRUCT(PERSON, age); size_t offset1 = OFFSET_OF_STRUCT(PERSON, name); size_t offset2 = OFFSET_OF_STRUCT(PERSON, num1); size_t offset3 = OFFSET_OF_STRUCT(PERSON, num2); size_t offset4 = OFFSET_OF_STRUCT(PERSON, num3); size_t offset5 = OFFSET_OF_STRUCT(PERSON, num4); printf("%d %d %d %d %d %d\n", offset0, offset1, offset2, offset3, offset4, offset5); return 0; }
执行结果:0 4 16 24 32 40
代码分析
比较费解的一句宏定义语句,抽出一句语句来分析看看:
size_t offset2 = OFFSET_OF_STRUCT(PERSON, num1);
将宏定义手动代入展开如下:
size_t offset2 = ((size_t)(&((PERSON *)NULL)->num1)) ;
NULL即为0,(PERSON *)NULL即将0地址转为PERSON*的指针,&((...)->num1)取出指针所指向结构对应的num1成员的地址;num1成员的地址等于指针的地址加上num1在结构中的相对偏移,此处指针地址为0,于是num1成员的地址正好等于num1在结构中的相对偏移,于是将num1成员的地址强制转化为int型,结果正好是num1在结构中的相对偏移位置。解释完毕。
不得不说这是一个很巧妙的trick。
补遗
将上面宏定义中的size_t改为int,NULL改为值为NULL的指针,结果编译的时候出现了warning,代码如下:
#include <stdio.h> typedef struct person { int age; char name[10]; long num1; float num2; double num3; int num4; } PERSON; int main() { int off1 = 0; int off2 = 0; struct person *nul= NULL; printf("%d %d ", sizeof(&(((struct person *)nul)->name)), sizeof(&(((struct person *)0)->name))); off1 = (int)(&(((struct person *)nul)->name)); off2 = (int)(&(((struct person *)0)->name)); printf("%d %d\n", off1, off2); return 0; }
编译的提示信息如下:
t.c: In function ‘main’: t.c:23: warning: cast from pointer to integer of different size
将代码做了简化,如下:
#include <stdio.h> int main() { int off1 = 0; int off2 = 0; int *nul= 0; printf("%d %d ", sizeof((int *)0), sizeof((int *)nul)); off1 = (int)((int *)0); off2 = (int)((int *)nul); printf("%d %d\n", off1, off2); return 0; }
查了一下,出现此问题的原因是因为64位的系统中地址是8字节的(32位系统下是4字节的),而int长度是4字节的,于是系统编译时给出了一个警告信息。
但观察使用0地址的情况,同样是8字节的地址却没有给出任何警告,确实奇怪。这里只能猜测是常量存放的区域和处理方式的不同导致的,一下子也不知道该从哪个角度去分析这个问题,先记录一笔。有知道的同学欢迎留言解答,拜谢!
2011-3-30 更新
将宏定义的int改为size_t,在32位和64位架构下可以兼容;参见【C】int与size_t的区别。
标签: C
还没有人抢沙发呢~