看一个例子:

#include <iostream>
using namespace std;
class A
{
public:
    int m1;
    int m2;
    int m3;
    void fun(){cout<<&m1<<' '<<&m2<<' '<<&m3<<endl;}
};
struct C1
{
    int c1_m1;
    int c1_m2;
};
struct C2
{
    int c2_m1;
    int c2_m2;
};
int main()
{
    //1
    cout<<(int)(&(((A*)0)->m3))<<endl;
    //2
    C1 tc1;
    tc1.c1_m1=111;
    tc1.c1_m2=222;
    C2 tc2;
    tc2.c2_m1=333;
    tc2.c2_m2=444;
    cout<<((C1*)(&tc2))->c1_m1<<endl;
    cout<<((C1*)(&tc2))->c1_m2<<endl;
    //3
    A *pa = 0;
    pa->fun();
}

输出结果是:

8

333

444


代码段1:

(int)(&(((A*)0)->m3)) 这行代码的含义是将0转化为一个类A的地址,即把地址0作为一个类A对象的数据部分的起始地址,即this指针的值为0,然后取这个对象的m3成员的地址,再次将得到的结果地址转换为整型输出。

在这行代码中没有对以地址0为起始地址的类A对象的成员m3进行数据访问,而是输出了其地址.

从这段代码可以看出,编译器对->m3这个操作的处理是这样的:先查询在类A中,成员m3相对起始地址的偏移量,然后在调用->m3这个操作的地址上加上相应的偏移量,将得到的结果地址当做m3成员的地址,由于该代码没有访问m3成员的数据,因此没有出现运行时错误。


代码段2:

同理,我们可以分析得到后面的代码为什么能够在不同类型的类对象地址之间进行转换后还能进行数据访问。


代码段3:

对象的成员方法调用过程中,实际调用的函数的参数列表中加入了一个this指针,而this指针指向的一个包含有该对象数据部分的结构体,即对象的地址。在代码段3中,传入fun()函数的this指针为0,因此,得到的m1,m2,m3的地址为0,4,8,同时,函数能成功调用,具体原因请参考 C++的非静态成员函数的访问


有什么问题,欢迎大家一起讨论!