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這類工具可以看到詳細信息,進行具體分析。