很早前就想寫點總結將編程中遇到的各種錯誤刨根挖底地羅列出來。但是因爲這些錯誤(VC中開調試器遇到的各種錯誤對話框)都是隨機性的,真正想總結的時候又不想不起來有哪些錯誤。恰好最近運氣比較背,各種錯誤都被我遇遍了,於是恰好有機會做個總結。
這裏所說的VC下的錯誤對話框時指在VC中開調試器運行程序時,IDE彈出的對話框。
1.不是錯誤的錯誤:斷言 .
將斷言視爲錯誤其實有點可笑,但是因爲有些同學甚至不知道這個,所以我稍微提一下。斷言對話框大致上類似於:
斷言對話框是由assert引起的,在對話框上通常會給出表達式,例如assert( 0 ); 彈出對話框時就會將0這個表達式顯示出來(Expression:0)。關於assert的具體信息建議自己google。這裏稍微提一下一個技巧:有時候爲了讓assert提供更多的信息,我們可以這樣寫一個assert:
assert( expression && "Function : invalid argument!" );
因爲字符串被用在布爾表達式中時,始終爲true,不會妨礙對expression的判斷,當斷言發生時(expression爲false) 時,斷言對話框上就會顯示這個字符串,從而方便我們調試。
要解決這個問題,首先要確定斷言發生的位置,如果是你自己設置的斷言被引發,就很好解決,如果是系統內部的函數產生的,那麼一般是因爲你傳入的函數參數無效引起。
2.內存相關:最簡單的非法訪問:
C、C++程序中經常誤用無效的指針,從而大致各種各樣的非法內存訪問(寫/讀)。最簡單的情況類似於:
這樣的情況由類似以下代碼引起:
char *p = 0;
*p = 'a';
當你看到類似於“寫入位置XXXX時發生訪問衝突“時,那麼你大致可以斷定,你的程序在某個地方訪問到非法內存。開調試器對調用堆棧進行跟蹤即可找出錯誤。
3.內存相關:不小心的棧上數組越界:
當你寫下類似以下的代碼時:
char str[3];
strcpy( str, "abc" );
就將看到如下的對話框:
對話框大致的意思就是說str周圍的棧被破壞了,因爲str本身就被放在棧上,所以strcpy(str,"abc")多寫入的'/0'就寫到非法的棧區域。看到這樣的對話框可以根據調用堆棧定位到錯誤發生的函數,然後檢查此函數內部定義的數組訪問,即可解決問題。
4.內存相關:不小心的堆上數組越界:
並不是每次數組越界都會得到上面所描述的錯誤,當數組是在堆上分配時,情況就變得隱祕得多:
char *str = new char [2];
strcpy( str, "ab" ); //執行到這裏時並不見得會崩潰
delete [] str;//但是到這裏時就肯定會崩潰
以上代碼導致的錯誤對話框還要詭異些:
似乎不同的DAMAGE對應的錯誤號(這裏是47)都不一樣,因爲這裏的錯誤發生在delete,而delete跟new很可能在不同的地方,所以這個錯誤調試起來不是那麼容易,很多時候只能靠經驗。
當看到類似的對話框時,根據調用堆棧跟到delete時,你就可以大致懷疑堆上數組越界。
5.調用相關:函數調用約定帶來的錯誤:
這是所有我這裏描述的錯誤中最詭異的一種,先看下對話框大致的樣子:
對話框大致的意思就是說(沒開調試器時對話框樣式可能不一樣),通過函數指針調用某個函數時,函數指針的類型(函數原型)可能與函數指針指向的函數的類型不一樣。這裏的類型不一致主要是調用約定(call conversation)不一樣。如果函數類型(參數個數,返回值)不一樣,一般不會出錯。
調用約定是指調用一個函數時,函數參數的壓入順序、誰來清理棧的內容等。例如默認的C、C++調用約定__cdecl,對於函數的參數是從右往左壓入。而__stdcall(WIN API的調用約定)則是從左向右壓。我這裏所說的函數類型不一樣,就是指一個函數是使用__cdecl,還是__stdcall。例如以下代碼:
void __stdcall show( const char *str )
{
}
void __stdcall show2()
{
}
int main()
{
typedef void (*Func)( const char *);
void *p = show;
Func my_func = (Func) p;
my_func( "kevin" );
return 0;
}
因爲Func默認地被處理爲__cdecl,而show是__stdcall的,所以當通過函數指針my_func時,就導致了以上對話框的出現。但是當p指向show2時,又不會出錯,這是因爲show2沒有參數,不同的調用約定不影響這個規則。
6.異常相關:默認終止程序
當我們使用C++庫時,因爲庫本身可能會拋出C++異常,如果你不捕獲這個異常,那麼C++默認就會調用abort(或者exit)函數終止程序。例如:
{
throw std::exception( "some exceptions" );
}
當你調用test函數時,如果不catch這個異常,開調試器就會得到類似的錯誤對話框:
而如果不開調試器,則會得到:
當你看到類似於“This application has requested the Runtime to terminate it…”之類的字眼時,那就表明程序調用了abort(或exit)函數,導致程序異常終止。其實這個錯誤只要開調試器,一般可以準確定位錯誤的發生點。
7.VC運行時檢查-未初始化變量
VC的調試器會對代碼進行運行時檢查,這可能會導致VC彈出對你看上去正確的代碼。這也許不是一個錯誤。例如:
int test_var;
if( test_var == -1 )
{
test_var = 0;
}
test_var沒有初始化就進行if判斷,當運行以上代碼開調試器時,就會得到如下對話框:
8.破壞的堆
VC對於在堆上分配的內存都做了記錄,我想這主要用於free釋放內存時做歸還處理。
char *p = (char*) malloc( 100 );
p += 10;
free( p );
當執行以上代碼時,因爲p的值已經改變,提交到free的指針值變化,VC就會給出以下錯誤提示:
posted on 2008-04-24 13:43 Kevin Lynx 閱讀(3590) 評論(9) 編輯 收藏 引用 所屬分類: C/C++ 、通用編程
評論
很好,不過好多都沒有遇到過
全遭遇過了
不錯的總結
re: 調試經驗總結-VC下的錯誤對話框 2008-04-25 11:38
chao hao !
高手一個啊!
re: 調試經驗總結-VC下的錯誤對話框 2008-04-26 22:28
調用相關:函數調用約定帶來的錯誤
不一定是調用約定錯誤,數組訪問越界覆蓋掉vc設置的cookie,也可能是這樣的提示
re: 調試經驗總結-VC下的錯誤對話框 2008-04-28 09:15
@yafare
例如?舉個例子代碼看看?(從對話框給的內容來看,似乎就是因爲函數調用錯誤)
re: 調試經驗總結-VC下的錯誤對話框 2008-04-28 22:00
呵呵我剛纔在Matlab中也出現了這樣的問題,想一想一小時前我還使用的好好的啊!仔細想想和操作系統相關的就是我把桌面主題給換了,隱約感覺到是這裏的問題,於是我就把主題改爲window XP,果然問題解決了,matlab可以正常啓動了!
re: 調試經驗總結-VC下的錯誤對話框 2008-04-29 22:22
這些都經常遇到,還有一些很難遇到,更隱祕的錯誤纔會導致的出錯對話框。
re: 調試經驗總結-VC下的錯誤對話框(陸續更新5.5.2008) 2008-05-09 08:59
頂
re: 調試經驗總結-VC下的錯誤對話框(陸續更新6.12.2008) 2008-07-04 07:30
謝謝樓主分享,樓主真是有心人。