http://blog.sina.com.cn/s/blog_532f6e8f01017ljb.html
class A
{
public:
int m_iA1;
void print()
{
cout << "A" << endl;
}
};
int main()
{
A *pObjectA=NULL;
pObjectA->print();
return 0;
}
*a没有初始化,结果成功输出“A”。
分析:成员函数在代码段,成员变量在数据段,地址为在类对象地址基础上累加。
此处的print成员函数没有用到成员变量,与类外独立函数无异。调用的时候不涉及数据段,不会崩溃。
如果成员函数print中使用到了成员变量,情况会是怎样呢?
class A
{
public:
int m_iA1;
char m_chA2;
void print()
{
m_iA1 = 1;
cout << "A" << endl;
}
};
int main()
{
A *pObjectA=NULL;
pObjectA->print();
return 0;
}
编译执行后,崩在了print函数中的红色标记语句。
分析:此处的print成员函数用到成员变量m_iA1,我们可以看到,&pObjectA=0x00000000,&m_iA1=0x00000000,&m_iA2=0x00000004
尝试给pObjectA分配空间后,&pObjectA=0x003b6060,&m_iA1=0x003b6060,&m_iA2=0x003b6064,
这是合法的数据段地址,访问成功。
我们再给A类加一点有趣的东西来验证一下
1、static静态变量
class A
{
public:
static int m_iSA4;
static void print()
{
m_iSA4 = 4;
cout << "A" << endl;
}
};
int A::m_iSA4 = 0;
int main()
{
A *pObjectA=NULL;
pObjectA->print();
return 0;
}
运行成功!静态变量独立于类外部分配好了合法地址,访问成功。
2、继承
class B
{
int m_iB1;
public:
virtual void print()
{
cout << "B" << endl;
}
};
class A:public B
{
int m_iA1;
char m_chA2;
public:
void print()
{
cout << "A" << endl;
}
};
int main()
{
A *pObjectA=NULL;
pObjectA->print();
return 0;
}
运行之后崩在在main函数的红色标记语句。
分析:虽然A类的print函数没有用到显式的成员变量,但是类A继承于类B,print()是继承于B的虚函数,调用A的print函数时,用到了虚表,虚表地址在类对象数据段最开头,此时访问了非法地址。
可以看到成员变量地址:
给pObjectA分配空间之后再看
可以看到,在继承类对象的地址空间中的顺序为:虚表指针、基类成员变量、继承类成员变量
通常崩溃报错的对话框中会给出一些关键信息:
如此图中的0x00411596表示出错的代码段地址,0xC0000000为出错的数据段地址,0xC0000005为错误码。“Access violation”就是此错误码的具体含义,查看所有错误码含义可查阅Visual Studio-Debug-Exceptions-Win32 Exceptions。详细错误信息都会保存在dmp文件中,用Windbg这类工具可以看到详细信息,进行具体分析。