|
1.不是錯(cuò)誤的錯(cuò)誤:斷言 . 將斷言視為錯(cuò)誤其實(shí)有點(diǎn)可笑,但是因?yàn)橛行┩瑢W(xué)甚至不知道這個(gè),所以我稍微提一下。斷言對(duì)話框大致上類似于: 斷言對(duì)話框是由assert引起的,在對(duì)話框上通常會(huì)給出表達(dá)式,例如assert( 0 ); 彈出對(duì)話框時(shí)就會(huì)將0這個(gè)表達(dá)式顯示出來(lái)(Expression:0)。關(guān)于assert的具體信息建議自己google。這里稍微提一下一個(gè)技巧:有時(shí)候?yàn)榱俗宎ssert提供更多的信息,我們可以這樣寫一個(gè)assert: assert( expression && "Function : invalid argument!" ); 因?yàn)樽址挥迷诓紶柋磉_(dá)式中時(shí),始終為true,不會(huì)妨礙對(duì)expression的判斷,當(dāng)斷言發(fā)生時(shí)(expression為false) 時(shí),斷言對(duì)話框上就會(huì)顯示這個(gè)字符串,從而方便我們調(diào)試。 要解決這個(gè)問(wèn)題,首先要確定斷言發(fā)生的位置,如果是你自己設(shè)置的斷言被引發(fā),就很好解決,如果是系統(tǒng)內(nèi)部的函數(shù)產(chǎn)生的,那么一般是因?yàn)槟銈魅氲暮瘮?shù)參數(shù)無(wú)效引起。
2.內(nèi)存相關(guān):最簡(jiǎn)單的非法訪問(wèn): C、C++程序中經(jīng)常誤用無(wú)效的指針,從而大致各種各樣的非法內(nèi)存訪問(wèn)(寫/讀)。最簡(jiǎn)單的情況類似于: 這樣的情況由類似以下代碼引起: char *p = 0; *p = 'a'; 當(dāng)你看到類似于“寫入位置XXXX時(shí)發(fā)生訪問(wèn)沖突“時(shí),那么你大致可以斷定,你的程序在某個(gè)地方訪問(wèn)到非法內(nèi)存。開(kāi)調(diào)試器對(duì)調(diào)用堆棧進(jìn)行跟蹤即可找出錯(cuò)誤。
3.內(nèi)存相關(guān):不小心的棧上數(shù)組越界: 當(dāng)你寫下類似以下的代碼時(shí): char str[3]; strcpy( str, "abc" ); 就將看到如下的對(duì)話框: 對(duì)話框大致的意思就是說(shuō)str周圍的棧被破壞了,因?yàn)閟tr本身就被放在棧上,所以strcpy(str,"abc")多寫入的'\0'就寫到非法的棧區(qū)域。看到這樣的對(duì)話框可以根據(jù)調(diào)用堆棧定位到錯(cuò)誤發(fā)生的函數(shù),然后檢查此函數(shù)內(nèi)部定義的數(shù)組訪問(wèn),即可解決問(wèn)題。
4.內(nèi)存相關(guān):不小心的堆上數(shù)組越界: char *str = new char [2]; strcpy( str, "ab" ); //執(zhí)行到這里時(shí)并不見(jiàn)得會(huì)崩潰 delete [] str;//但是到這里時(shí)就肯定會(huì)崩潰 以上代碼導(dǎo)致的錯(cuò)誤對(duì)話框還要詭異些: 似乎不同的DAMAGE對(duì)應(yīng)的錯(cuò)誤號(hào)(這里是47)都不一樣,因?yàn)檫@里的錯(cuò)誤發(fā)生在delete,而delete跟new很可能在不同的地方,所以這個(gè)錯(cuò)誤調(diào)試起來(lái)不是那么容易,很多時(shí)候只能靠經(jīng)驗(yàn)。 當(dāng)看到類似的對(duì)話框時(shí),根據(jù)調(diào)用堆棧跟到delete時(shí),你就可以大致懷疑堆上數(shù)組越界。
5.調(diào)用相關(guān):函數(shù)調(diào)用約定帶來(lái)的錯(cuò)誤: 這是所有我這里描述的錯(cuò)誤中最詭異的一種,先看下對(duì)話框大致的樣子: 對(duì)話框大致的意思就是說(shuō)(沒(méi)開(kāi)調(diào)試器時(shí)對(duì)話框樣式可能不一樣),通過(guò)函數(shù)指針調(diào)用某個(gè)函數(shù)時(shí),函數(shù)指針的類型(函數(shù)原型)可能與函數(shù)指針指向的函數(shù)的類型不一樣。這里的類型不一致主要是調(diào)用約定(call conversation)不一樣。如果函數(shù)類型(參數(shù)個(gè)數(shù),返回值)不一樣,一般不會(huì)出錯(cuò)。 調(diào)用約定是指調(diào)用一個(gè)函數(shù)時(shí),函數(shù)參數(shù)的壓入順序、誰(shuí)來(lái)清理?xiàng)5膬?nèi)容等。例如默認(rèn)的C、C++調(diào)用約定__cdecl,對(duì)于函數(shù)的參數(shù)是從右往左壓入。而__stdcall(WIN API的調(diào)用約定)則是從左向右壓。我這里所說(shuō)的函數(shù)類型不一樣,就是指一個(gè)函數(shù)是使用__cdecl,還是__stdcall。例如以下代碼:
#include <iostream> ![]() 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; ![]() } ![]() ![]()
因?yàn)镕unc默認(rèn)地被處理為_(kāi)_cdecl,而show是__stdcall的,所以當(dāng)通過(guò)函數(shù)指針my_func時(shí),就導(dǎo)致了以上對(duì)話框的出現(xiàn)。但是當(dāng)p指向show2時(shí),又不會(huì)出錯(cuò),這是因?yàn)閟how2沒(méi)有參數(shù),不同的調(diào)用約定不影響這個(gè)規(guī)則。 6.異常相關(guān):默認(rèn)終止程序 當(dāng)我們使用C++庫(kù)時(shí),因?yàn)閹?kù)本身可能會(huì)拋出C++異常,如果你不捕獲這個(gè)異常,那么C++默認(rèn)就會(huì)調(diào)用abort(或者exit)函數(shù)終止程序。例如: void test() { throw std::exception( "some exceptions" ); }![]()
當(dāng)你調(diào)用test函數(shù)時(shí),如果不catch這個(gè)異常,開(kāi)調(diào)試器就會(huì)得到類似的錯(cuò)誤對(duì)話框:
而如果不開(kāi)調(diào)試器,則會(huì)得到:
當(dāng)你看到類似于“This application has requested the Runtime to terminate it…”之類的字眼時(shí),那就表明程序調(diào)用了abort(或exit)函數(shù),導(dǎo)致程序異常終止。其實(shí)這個(gè)錯(cuò)誤只要開(kāi)調(diào)試器,一般可以準(zhǔn)確定位錯(cuò)誤的發(fā)生點(diǎn)。 7.VC運(yùn)行時(shí)檢查-未初始化變量 VC的調(diào)試器會(huì)對(duì)代碼進(jìn)行運(yùn)行時(shí)檢查,這可能會(huì)導(dǎo)致VC彈出對(duì)你看上去正確的代碼。這也許不是一個(gè)錯(cuò)誤。例如: int test_var; if( test_var == -1 ) test_var沒(méi)有初始化就進(jìn)行if判斷,當(dāng)運(yùn)行以上代碼開(kāi)調(diào)試器時(shí),就會(huì)得到如下對(duì)話框: 8.破壞的堆 VC對(duì)于在堆上分配的內(nèi)存都做了記錄,我想這主要用于free釋放內(nèi)存時(shí)做歸還處理。 char *p = (char*) malloc( 100 ); 當(dāng)執(zhí)行以上代碼時(shí),因?yàn)閜的值已經(jīng)改變,提交到free的指針值變化,VC就會(huì)給出以下錯(cuò)誤提示: |
|
|
來(lái)自: 3D建模仿真 > 《程序調(diào)試錯(cuò)誤》