a.data_ 的地址是分为两部分的,一部分是a的this指针,另一部分是data_成员在A结构里的偏移量。这个偏移量是在main()之前就初始化了存在静态数据ptr里了。
数据成员指针p指向的内存里面放的就是个偏移量,和this指针合起来能寻址到对应的数据成员。
编译器看到a.*p,知道这是在通过数据成员指针来引用a的int数据成员。
也就会用a的this指针,和数据成员指针p的值(其值是实现为类的内部偏移量了)相加,然后访问数据。
a.*p 就是访问的:*(对象起始地址 + 对象里的成员偏移地址) = *(int *)((char *)this + *(offset_t *)p)。
p的值是静态成员ptr的值,静态成员ptr是在main()之前初始化的。
MSVC debug版是把Access<int A::*>::ptr初始化为0了(也可能是因为debug版的整个数据段都是初始化为0导致的)
A a{10};
00007FF78E1FCE7D mov edx,0Ah
00007FF78E1FCE82 lea rcx,[a]
00007FF78E1FCE86 call A::A (07FF78E142A4Eh)
std::cout << a.*Access<int A::*>::ptr << std::endl;
00007FF78E1FCE8B movsxd rax,dword ptr [Access<int A::*>::ptr (07FF78E783BB0h)]
00007FF78E1FCE92 mov edx,dword ptr a[rax]
00007FF78E1FCE96 lea rcx,[std::cout (07FF78E783ED0h)]
00007FF78E1FCE9D call std::basic_ostream<char,std::char_traits<char> >::operator<< (07FF78E13CDA6h)
gcc则是初始化为-1,这应该是个错误的或者没初始化的偏移值(可以细究一下为啥),所以打印的是2560。
Dump of assembler code for function main():
0x0000000000400762 <+0>: push rbp
0x0000000000400763 <+1>: mov rbp,rsp
0x0000000000400766 <+4>: sub rsp,0x10
0x000000000040076a <+8>: lea rax,[rbp-0x4]
0x000000000040076e <+12>: mov esi,0xa
0x0000000000400773 <+17>: mov rdi,rax
0x0000000000400776 <+20>: call 0x400802 <A::A(int)>
0x000000000040077b <+25>: mov rax,QWORD PTR [rip+0x2008d6] # 0x601058 <Access<int A::*>::ptr>
0x0000000000400782 <+32>: mov rdx,rax
0x0000000000400785 <+35>: lea rax,[rbp-0x4]
0x0000000000400789 <+39>: add rax,rdx
0x000000000040078c <+42>: mov eax,DWORD PTR [rax]
=> 0x000000000040078e <+44>: mov esi,eax
0x0000000000400790 <+46>: mov edi,0x601060
0x0000000000400795 <+51>: call 0x400660 <std::ostream::operator<<(int)@plt>
0x000000000040079a <+56>: mov esi,0x400620
0x000000000040079f <+61>: mov rdi,rax
0x00000000004007a2 <+64>: call 0x400640 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))@plt>
0x00000000004007a7 <+69>: mov eax,0x0
0x00000000004007ac <+74>: leave
0x00000000004007ad <+75>: ret
End of assembler dump.
(gdb) p $rax
$3 = 2560
(gdb) p $rdx
$4 = -1
【 在 namelij 的大作中提到: 】
: 感谢回复
: p = Access<int A::*>::ptr; // Access<int A::*>::ptr 是在静态成员tr的构造里初始化好的
: 在这句里面,是不是可以理解为把a.data_ 的地址 赋值给Access<int A::*>::ptr
: ...................
--
修改:z16166 FROM 114.241.225.*
FROM 114.241.225.*