提一个问题:已知一个结构体的名称和该结构体内的一个成员变量的名称,如何得到该成员变量在该结构体内的位置偏移?
举个C语言结构体的例子:
typedef struct _node {
int value32;
long value64;
struct _node * next;
} Node;
如上所示,如何得知 next在 Node中的位置偏移?
像这样一个简单的结构体自然可以人工计算一下即可。但如果是一个比较宏大的结构体,有数十上百个成员变量,要想知道中间某个变量的相对位置偏移,就比较麻烦了。
即使数得出来,将来也可能在其之前增加一些其他成员变量,到时候,这个数出来的值就失效了,又要重新数。
所以,如果用程序去计算指定成员变量的偏移,应该怎么做呢?
用下面的一行代码就可以做到。
#define GET_OFFSET(_type, _member) ((unsigned long)(&((_type *)0)->_member))
解释一下:
将地址0当作是一个Node实例的地址,从而自然可以使用->next获得next成员变量,再用取地址符就取得了next成员变量的地址。
因为该实例本身的地址从0开始,那么此处取到的next成员变量的地址自然就是位置偏移了。
那么为什么这个值不是负数呢?
笔者猜想,这是因为编译器假设这个实例在堆中,而在程序的进程空间中,堆地址是自低地址向高地址增长的,因此就是正数了。又或者根本也没那么复杂,编译器就只是简单认为成员变量的地址相对该实例的起始地址是逐渐增长的,而不是逐渐递减的。
完整的程序代码如下:
#include#define GET_OFFSET(_type, _member) ((unsigned long)(&((_type *)0)->_member)) typedef struct _node { int value32; long value64; struct _node * next; } Node; int main() { unsigned long offset = GET_OFFSET(Node, next); printf("offset of next = %ldn", offset); return 0; }
上述程序的执行结果为:
offset of next = 16
这个方法对C++的类也适用,但是只能对public类型的成员变量计算位置偏移,如下:
#includeusing namespace std; #ifdef __cplusplus #define ENV "CPP" #else #define ENV "C" #endif #define GET_OFFSET(_type, _member) ((unsigned long)(&((_type *)0)->_member)) class Node { public: virtual int getValue32() { return value32; } virtual int getValue64() { return value64; } Node(int v32=0, int v64=0) : value32(v32), value64(v64) {} private: int value32; long value64; public: Node * next; }; int main() { unsigned long offset = GET_OFFSET(Node, next); unsigned long offset_in_c_way = (unsigned long)(&((Node *)0)->next); cout << "ENV=" << ENV < ENV=CPP offset of next = 24
这里的offset之所以变成了24而不是16,是因为在Node实例的顶部有一个虚表指针,占用了8个字节,因此next成员变量的位置偏移从16字节变成了24字节。
另一个问题来了,我们计算成员变量在结构体内部的偏移有什么用呢?
这个请看下回分解。
(完)



