LS等于没有回答。
先看看一个类的结构,假设类的定义是:
struct s
{
int i1,i2;
char c1,c2;
}*pt,s1;
那么它在内存中的放置位置如下:
V 这里是pt指向的地方。
------------------------>内存地址顺次增加
s:|xxxx|xxxx|x|x|....
^
^
^ ^
i1
i2
c1 c2
假设我们要访问i1,实际上的代码是:pt->i1,它在内部的表示是*(int*)((char*)pt+0),也就是根据pt得到一个偏移量,将那个地方的一个int取出来。
如果访问s1的i1,写法是:s1.i1,实际的行为是:*(int*)((char*)&s1+0)
以此类推,假设要访问的是c1,写法和相应真实的操作为:
pt->c1
*(char*)((char*)pt+8)
s1.c1
*(char*)((char*)&s1+8)
为什么要先转化为char*呢?因为char*指针是以一个字节为单位前进的,我们只是利用这个特点而已,和char没有什么特别的关系。
好,我们回来看那个宏:
#define offset(s,m) (size_t)&(((s*)0)->m)
假设写成offset(struct s,c1),那么展开了就是:
(size_t)&(((struct s*)0)->c1)
根据我们刚才的写法,这实际上是:
(size_t)&(*((char*)(0)+8))
最外层的&和*抵消掉了,我们加一点儿空格,可以看得更清晰些:
(size_t) ( ((char*)0 + 8) )
所有的类型转换都没有改变值,我们可以继续化简:
(size_t)(0+8) 最后等于8。
这个数字就是c1在结构体s中的相对位置。
明白了么?