久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

VC/MFC調(diào)試技術(shù)(轉(zhuǎn)) - carekee - 博客園

 jinye6 2011-03-28

在VC程序中使用調(diào)試語句

  為了更好地對程序調(diào)試,可以使用如下方法:使用斷言,、使用跟蹤語句,、使用異常和返回值。

一,、斷言

1,、基本概念

  斷言是一種讓錯(cuò)誤在運(yùn)行時(shí)候自我暴露的簡單有效實(shí)用的技術(shù)。它們幫助你較早較輕易地發(fā)現(xiàn)錯(cuò)誤,,使得整個(gè)調(diào)試過程效率更高,。

  斷言是布爾調(diào)試語句,用來檢測在程序正常運(yùn)行的時(shí)候某一個(gè)條件的值是否總為真,,它能讓錯(cuò)誤在運(yùn)行時(shí)刻暴露在程序員面前,。使用斷言的最大好處在于,能在更解決錯(cuò)誤的發(fā)源地的地方發(fā)現(xiàn)錯(cuò)誤,。斷言具有以下特征:

.斷言是用來發(fā)現(xiàn)運(yùn)行時(shí)刻錯(cuò)誤的,,發(fā)現(xiàn)的錯(cuò)誤是關(guān)于程序?qū)崿F(xiàn)方面的。

.斷言中的布爾表達(dá)式顯示的是某個(gè)對象或者狀態(tài)的有效性而不是正確性,。

.斷言在條件編譯后只存在于調(diào)試版本中,,而不是發(fā)布版本里。

.斷言不能包含程序代碼,。

.斷言是為了給程序員而不是用戶提供信息,。

  使用斷言最根本的好處是自動(dòng)發(fā)現(xiàn)許多運(yùn)行時(shí)產(chǎn)生的錯(cuò)誤,但斷言不能發(fā)現(xiàn)所有錯(cuò)誤,。斷言檢查的是程序的有效性而不是正確性,,可通過斷言把錯(cuò)誤限制在一個(gè)有限的范圍內(nèi)。當(dāng)斷言為假,激活調(diào)試器顯示出錯(cuò)代碼時(shí),,可用Call Stack命令,,通過檢查棧里的調(diào)用上下文、少量相關(guān)參數(shù)的值以及輸出窗口中Debug表的內(nèi)容,,通常能檢查出導(dǎo)致斷言失敗的原因。_ASSERTE宏(屬于C運(yùn)行時(shí)間庫)還能在斷言失敗時(shí)顯示出失效斷言,。下面我們討論一下MFC庫中的斷言,。

2、MFC庫中的斷言

(1) ASSERT(布爾表達(dá)式)

用MFC時(shí)最好選擇ASSERT宏,,它的優(yōu)點(diǎn)是即使出現(xiàn)了WM_QUIT消息也能顯示斷言失效消息框,。

(2) VERIFY(布爾表達(dá)式)

VERIFY宏中的布爾表達(dá)式在發(fā)布版本中被保留下來。VERIFY宏簡化了對函數(shù)返回值的檢查,,一般用來檢查Windows API的返回值,。由于VERIFY宏里的布爾表達(dá)式在發(fā)布版本里保留了下來,因此最好盡量不要使用這個(gè)宏以實(shí)現(xiàn)程序代碼和調(diào)試代碼的完全分離,。

(3 )ASSERT_VALID(指向CObject派生類對象的指針)

ASSERT_VALID宏通過調(diào)用重載的AssertValid函數(shù)來確定指向CObject派生類對象的指針是否有效,。無論你什么時(shí)候從CObject派生類中得到一個(gè)對象,在對這個(gè)對象做任何操作之前都應(yīng)該調(diào)用ASSERT_VALID宏,。

(4) ASSERT_KINDOF(類名, 指向CObject派生類對象的指針)

這個(gè)宏用來驗(yàn)證指向CObject派生類對象的指針是否從某個(gè)特殊類中派生,,在調(diào)用它之前先調(diào)用ASSERT_VALID宏。只有在很特殊的場合下才用得到,,如檢測編譯器可能錯(cuò)過的對象類型問題,。

此外,還有兩個(gè)沒有正式文件的ASSERT宏的變種:ASSERT_POINTER(指針,指針類型),,ASSERT_NULL_OR_POINTER(指針,指針類型),。

3、什么時(shí)候使用斷言

  把斷言看作一種簡單的制造柵欄的方法,,這種柵欄能使錯(cuò)誤在穿過自己時(shí)暴露,。

.檢查函數(shù)的輸入

.檢查函數(shù)的輸出

.檢查對象的當(dāng)前狀態(tài)

.堅(jiān)持邏輯變量的合理性和一致性

.檢查類中的不變量

公有成員函數(shù)比私有和保護(hù)的成員函數(shù)需要更全面的斷言。

  不正確地使用斷言會導(dǎo)致錯(cuò)誤,。斷言應(yīng)該檢測那些在程序正常運(yùn)行的時(shí)候永遠(yuǎn)都不可能出現(xiàn)的狀態(tài),。斷言是用來揭示錯(cuò)誤的,而不是用來糾正運(yùn)行時(shí)刻錯(cuò)誤的,。

4,、斷言與防御性編程(Defensive Programming)

  斷言在調(diào)試的時(shí)候向程序員揭示運(yùn)行時(shí)刻錯(cuò)誤(調(diào)試版本里),而防御性編程使用戶在運(yùn)行程序(發(fā)布版本里)時(shí),,當(dāng)出現(xiàn)意外情況時(shí)程序仍能繼續(xù)工作,。實(shí)際上,防御性的編程要求程序在檢測到意外時(shí)返回一個(gè)“安全”的值(比如布爾函數(shù)返回false,指針和句柄返回空值),,一個(gè)錯(cuò)誤代碼或者拋出一個(gè)異常來解決問題,。特定的防御性編程技術(shù)包括:處理無效函數(shù)參數(shù)和數(shù)據(jù)、出現(xiàn)問題的時(shí)候程序失敗,、檢查臨界函數(shù)返回的錯(cuò)誤代碼以及處理異常,。需要防御性編程的標(biāo)準(zhǔn)問題包括:錯(cuò)誤的輸入數(shù)據(jù)、內(nèi)存或者硬盤空間不夠,、不能打開一個(gè)文件,、外部設(shè)備不能訪問、網(wǎng)絡(luò)連接不上或者甚至在程序中還有錯(cuò)誤,,目的是保持程序的運(yùn)行狀態(tài),。如果你的程序是防御性的,別忘了使用斷言,。如果你使用斷言,,也別忘了防御性編程。這兩種技術(shù)最好結(jié)合在一起使用,。

二,、跟蹤語句

1、基本概念

  跟蹤語句(trace statements)可使程序執(zhí)行,,并使程序員可對可變值進(jìn)行查看,。它們提供了一個(gè)用于觀察的程序,并且獨(dú)立于一個(gè)交互式的調(diào)試器,,但是最具有特色的是它們常用于對調(diào)試器提供的信息進(jìn)行補(bǔ)充,。在VC中,跟蹤消息通常輸出到輸出窗口中的Debug標(biāo)簽,,也可以重新輸出到一個(gè)文件中,。跟蹤語句的特性如下:

.跟蹤語句用于報(bào)告代碼中重要的運(yùn)行事件。

.跟蹤語句的編譯通常是有條件的,,并只存在于調(diào)試版本中,,而在發(fā)布版本中不被編譯。

.跟蹤語句不能包含程序代碼或?qū)Τ绦虼a有間接的影響作用,。

.跟蹤語句的目的是向程序員提供信息,,而不是向用戶。

跟蹤語句也是調(diào)試語句,,它可以執(zhí)行程序,,并且在運(yùn)行中程序員可以查看變量。跟蹤語句對于那些使用交互式調(diào)試器很難調(diào)試的程序是很有效的,。

跟蹤語句和斷言的區(qū)別如下:

.跟蹤語句是無條件的,,斷言是有條件的布爾語句,。

.跟蹤語句用于顯示程序執(zhí)行和變量值,不直接顯示bug,,斷言用于顯示出bug,。

.跟蹤語句將信息輸出到調(diào)試窗口或文件中,可被隨意地忽略,,斷言打斷程序的執(zhí)行,。

2、MFC中的跟蹤語句

  在MFC中,,你可以使用TRACE和AfxOutputDebugString宏,、CObject::Dump虛擬函數(shù)和AfxDumpStack函數(shù)。TRACE宏由AfxDump實(shí)現(xiàn),,AfxDump由AfxOutputDebugString實(shí)現(xiàn)。AfxOutputDebugString宏和AfxDumpStack函數(shù)可以在所有版本中編譯,,其他只能在調(diào)試版本中編譯,。

(1)TRACE宏有以下形式:

_TRACE(reportType,format);

_TRACE0(reportType,format,arg1);

_TRACE1(reportType,format,arg1,arg2);

_TRACE2(reportType,format,arg1,arg2,arg3);

_TRACE3(reportType,format,arg1,arg2,arg3,arg4);

在MFC中,推薦使用TRACEn宏,,當(dāng)使用TRACE宏時(shí)需要使用_T宏來格式化參數(shù)以正確解決Unicode的校正,,而TRACEn不需要。

MFC TRACE宏中的一個(gè)缺點(diǎn)是AfxTrace函數(shù)使用一個(gè)512字符固定大小的緩沖區(qū),,這使得它在跟蹤長字符串時(shí)是無用的,。

(2)CObject::Dump

CObject類有一個(gè)轉(zhuǎn)儲(dump)虛擬函數(shù),所有繼承CObject的類都可以通過重載這個(gè)函數(shù),,輸出它們的值,。

3、Visual C++消息Pragma

消息Pragma實(shí)際上是一個(gè)編譯時(shí)的跟蹤語句,,你可以使用它來警告在預(yù)處理過程中發(fā)現(xiàn)的潛在的編連(build)問題,。典型的例子:

#if (WINVER>=0x0500)

#pragma message (“NOTE:WINVER has been defined as 0x0500 or greater.”)

#endif

  消息Pragma是非常有用的,尤其是在復(fù)雜編連中,。然而,,如果你要檢測一種特定的問題,而不是潛在的問題,,使用#error預(yù)處理來代替打斷編譯會更直接一些,。

  每當(dāng)你的程序中有錯(cuò)誤而你想得到更多信息的時(shí)候,你應(yīng)該去查看一下跟蹤消息,。由于VC輸出窗口的緩沖區(qū)是有大小限制的,,因此如果跟蹤消息數(shù)據(jù)產(chǎn)生的速度超過輸出窗口處理的速度,那么消息會塞滿緩沖區(qū),,導(dǎo)致數(shù)據(jù)丟失,。避免這個(gè)問題的簡單方法是在輸出大量數(shù)據(jù)的代碼段如轉(zhuǎn)儲對象時(shí),調(diào)用Sleep API函數(shù)。

三,、異常

1,、基本概念

  錯(cuò)誤是一種條件,在這種條件下,,如果不執(zhí)行額外的處理,,線程就不能正常地執(zhí)行下去。異常是用于處理錯(cuò)誤的,。使用異常的一個(gè)很明顯的好處就是它們通過發(fā)出錯(cuò)誤信號,,可以讓程序代碼和錯(cuò)誤處理代碼分開,而且不會讓程序忽略錯(cuò)誤,,你不用不斷地檢查函數(shù)的返回值,,因此它們將程序代碼簡單化。另一個(gè)好處是它們不需要嚴(yán)格的編程作風(fēng),。

異常的基本特性:

.異常是基于每個(gè)進(jìn)程而提出并處理的,。

.異常不能被線程忽略,必須被處理,。

.未處理的異常會使進(jìn)程結(jié)束,,而不僅僅是結(jié)束線程。

.異常出來在釋放棧時(shí)會釋放所有的棧對象,,避免了資源的漏洞,。

.異常處理需要大量的額外操作,使得它不適于經(jīng)常運(yùn)行的代碼,。

.可以拋出任何類型的異常對象,,除了整數(shù)。

如果正確執(zhí)行,,異常處理有下面的特性:

.異常是不是正常的運(yùn)行結(jié)果,,是特殊情況。

.異常在返回值無效的情況下使用,。

.異常是可靠的,,不可能被忽略。

.異常簡化了錯(cuò)誤處理,,簡化了程序代碼,,使錯(cuò)誤處理更加方便。

  Visual C++的默認(rèn)情況下,,在調(diào)試版本中處理異常,,而在發(fā)布版本中并不進(jìn)行處理。由于異常也是錯(cuò)誤,,Windows異常碼采用了同Windows錯(cuò)誤碼一樣的位映射模式,,為一個(gè)32位的值,,這些碼由Microsoft定義,任何異常碼的最高四位總是1100(二進(jìn)制),,即十六進(jìn)制里的0xC,。

2、Windows結(jié)構(gòu)異常和C++異常

  Windows結(jié)構(gòu)異常作為硬件異常(如訪問非法或被零除)或操作系統(tǒng)異常的結(jié)果被拋出,,C++異常只能由throw語句拋出,。Windows結(jié)構(gòu)異常處理不能處理對象的解析,因此你應(yīng)該在C++程序中一直使用C++異常,。然而,,C++異常不能處理硬件和操作系統(tǒng)異常,你的程序需要將結(jié)構(gòu)異常轉(zhuǎn)化為C++異常,。C++異常并不直接從你的程序代碼中拋出而是從C++運(yùn)行庫中拋出,,因此你需要調(diào)用棧窗口來返回你的代碼。為了正確處理硬件和操作系統(tǒng)異常,,你可以創(chuàng)建自己的異常類并使用_set_se_translator函數(shù)安裝一個(gè)結(jié)構(gòu)異常向C++異常的轉(zhuǎn)化器,,但不要捕獲那些不能恢復(fù)所產(chǎn)生問題的轉(zhuǎn)化后的結(jié)構(gòu)異常。

3,、MFC中的異常

  在MFC中,,所有的異常對象都是從CException基類(它有使用起來非常方便的GetErrorMessage和ReportError成員函數(shù))中派生來的,。大多數(shù)的MFC異常對象都是動(dòng)態(tài)分配的,,而且當(dāng)它們被捕獲時(shí),必須被刪除,,而沒有被捕獲的MFC異常由MFC本身在AfxCallWndProc函數(shù)中捕獲并刪除,。

4、異常的開銷

  當(dāng)拋出C++異常時(shí),,函數(shù)調(diào)用鏈將從此回溯搜索,,尋找可以處理拋出這類異常的處理器。若沒找到,,進(jìn)程結(jié)束,。如果找到,調(diào)用棧將被釋放,,所有的自動(dòng)(局部)變量也將釋放,,然后棧將被整理為異常處理器的上下文相關(guān)設(shè)備。因此異常開銷由一個(gè)異常處理器目錄和一個(gè)活動(dòng)的自動(dòng)變量表(它需要額外的代碼,、內(nèi)存,,而且不論異常是否拋出,都會運(yùn)行),,還得加上函數(shù)調(diào)用鏈的搜索,、自動(dòng)變量的解析和棧的調(diào)整(它只在拋出異常的時(shí)候需要執(zhí)行)組成,。

5、異常策略

(1)拋出時(shí)機(jī)

  拋出異常的時(shí)機(jī)應(yīng)該是一個(gè)函數(shù)發(fā)現(xiàn)一個(gè)錯(cuò)誤,,如果沒有一些特殊的操作,,該錯(cuò)誤能阻止程序正常的運(yùn)行,而這種操作它自己不能完成,,或是在函數(shù)不可能有返回值的時(shí)候,。

  使用異常處理更簡單,更可靠,,更有效,,可以創(chuàng)建更健壯的代碼。然而,,應(yīng)該只在意外的情況下使用異常處理,。如果你認(rèn)為一個(gè)指針應(yīng)該是空值,這種條件下就直接在代碼中檢查這個(gè)值,,而不要使用異常,。

(2)何時(shí)捕獲

對于這個(gè)問題,有一些可能的標(biāo)準(zhǔn):

.當(dāng)函數(shù)知道如何處理這個(gè)異常時(shí),。

.當(dāng)這個(gè)函數(shù)可以合理地處理這個(gè)異常而高級的函數(shù)不知道如何處理時(shí),。

.當(dāng)拋出異常可能使進(jìn)程崩潰時(shí),。

.當(dāng)函數(shù)可以繼續(xù)執(zhí)行它的任務(wù)時(shí),。

.當(dāng)需要整理分配好的資源時(shí)。

  異常處理的一個(gè)缺點(diǎn)是它可能導(dǎo)致資源的泄露,。因此,,防止資源泄露更應(yīng)該是保持程序異常安全的一部分。棧釋放時(shí)會自動(dòng)整理局部變量,,但不包括動(dòng)態(tài)分配的變量,。可以使用智能(smart)指針來保護(hù)你的代碼在存在異常的情況下不會產(chǎn)生資源泄漏,。

(3)怎樣捕獲

.非MFC的C++異常應(yīng)該通過引用來捕獲,。使用引用捕獲異常不需要?jiǎng)h除異常對象(因?yàn)槭褂靡貌东@的異常會在棧中傳送),而且它保留了多態(tài)性(因此你捕獲的異常對象正是你拋出的異常對象),。

.MFC異常應(yīng)該通過指針來捕獲,。使用指針捕獲異常需要你刪除對象。因?yàn)樗鼈兺ǔ亩阎蟹峙?,?dāng)你處理完異常之后,,需要調(diào)用Delete成員函數(shù)來刪除。你不可以使用省略捕獲處理器捕獲MFC異常,,這會導(dǎo)致一個(gè)內(nèi)存泄露,。必須使用Delete成員函數(shù)刪除MFC異常,,而不用delete,因?yàn)橐恍㎝FC異常為靜態(tài)對象創(chuàng)建,。

在釋放棧的過程中拋出異常會導(dǎo)致進(jìn)程的終止,。釋放棧涉及到調(diào)用析構(gòu)函數(shù),異??梢宰柚拐{(diào)用delete操作符,,這樣會有資源泄漏,因此異常最好不要從析構(gòu)函數(shù)中拋出,。如果非要在析構(gòu)函數(shù)里拋出異常,,必須妥善處理,避免資源泄漏,。

6,、異常與防御性編程

  在異常發(fā)生時(shí)繼續(xù)執(zhí)行程序,遠(yuǎn)比執(zhí)行一個(gè)正常的關(guān)閉動(dòng)作要重要,。如果可能,,應(yīng)該將精力集中在繼續(xù)執(zhí)行程序,并在必須的情況下才正常地關(guān)閉程序,??赡茏罡镜恼jP(guān)閉是一個(gè)在崩潰時(shí)可以重新啟動(dòng)自己的進(jìn)程,這是Windows資源管理器使用的一種技術(shù),。

  如果一個(gè)與錯(cuò)誤相關(guān)的C++異常是可預(yù)料的,,如果它發(fā)生在非關(guān)鍵性的代碼中,如果它不是發(fā)生在程序啟動(dòng)或結(jié)束過程中或一個(gè)不可恢復(fù)的結(jié)構(gòu)異常的結(jié)果中,,這個(gè)程序就可以從其中恢復(fù),。

  一旦你的程序可以從與錯(cuò)誤相關(guān)的異常中恢復(fù),,應(yīng)該先檢查程序的狀態(tài)和它的文檔,。如果程序和文檔已經(jīng)被破壞了,進(jìn)程也應(yīng)該終止運(yùn)行,。否則,,程序需要通知客戶機(jī)確定動(dòng)作的過程。如果客戶機(jī)同意執(zhí)行下去,,程序應(yīng)該恢復(fù)錯(cuò)誤并繼續(xù)執(zhí)行,。

四、返回值

  并不是在所以場合下都能使用異常,,如在使用Windows API編程或帶有COM編程時(shí)并不使用異常,。在異常不適合的時(shí)候,使用返回值是一個(gè)好的辦法,。

返回值的基本特性:

.返回值可以指示正常和不正常的函數(shù)運(yùn)行,,但不能阻止線程的繼續(xù)運(yùn)行,。

.返回值很容易被忽略。

.返回值在典型情況下是一個(gè)整數(shù),,通常映射符合于一個(gè)預(yù)定義的值,。

.返回值能高效地傳遞和接收。

因此,,返回值最適合用于以下的情形:

.用于非錯(cuò)誤的狀態(tài)信息

.用于大多數(shù)情況下可以隨意忽略而不會出問題的錯(cuò)誤,。

.用于更易于出現(xiàn)在循環(huán)中的錯(cuò)誤。

.用于中間語言模塊如COM組件中的錯(cuò)誤,。

 

使用Visual C++調(diào)試器調(diào)試

一,、調(diào)試版本與發(fā)布版本

  有時(shí)程序能在調(diào)試版本運(yùn)行但不能運(yùn)行于發(fā)布版本,反之也有可能,。一般說來,,一個(gè)發(fā)布版本意味著某些類型的優(yōu)化,而一個(gè)調(diào)試版本則沒有優(yōu)化,。下面我們來看看它們的區(qū)別:

1,、特別針對調(diào)試版本的編譯選項(xiàng)

(1)/MDd,/MLd或者/MTd

  調(diào)試版本的運(yùn)行時(shí)刻庫有調(diào)試符號,使用了調(diào)試堆,,調(diào)試堆的目的是發(fā)現(xiàn)內(nèi)存破壞和內(nèi)存泄漏,,并且向用戶報(bào)告源代碼的哪個(gè)地方出了問題。特性:

.調(diào)試版本的運(yùn)行時(shí)刻庫對內(nèi)存的分配作了跟蹤,,允許用戶檢查內(nèi)存泄漏,。

.在剛分配的內(nèi)存里寫上0xCD的字節(jié)模式,用0xCD來填充剛分配的內(nèi)存,,有助于發(fā)現(xiàn)數(shù)據(jù)未被初始化的錯(cuò)誤,。

.在被釋放的內(nèi)存寫上0xDD的字節(jié)模式,有助于發(fā)現(xiàn)已被釋放的內(nèi)存,。

.在緩沖區(qū)的兩邊分配了四字節(jié)的保護(hù)數(shù)據(jù),,并用0xFD的字節(jié)模式作初始化,來檢查寫內(nèi)存的上溢出和下溢出,。

.在每個(gè)內(nèi)存分配的地方對源代碼文件名和行號作了記錄,,有助于用戶在源代碼中對內(nèi)存分配進(jìn)行定位。

(2)/Od

  這個(gè)選項(xiàng)用來關(guān)閉優(yōu)化開關(guān),。因?yàn)槲幢粌?yōu)化的代碼直接對應(yīng)于源代碼,,所以比優(yōu)化后的代碼更容易讀懂。未被優(yōu)化的代碼編譯和鏈接會更快,,會有更短的調(diào)試周期,。而由于優(yōu)化,發(fā)布版本不見得會比調(diào)試版本運(yùn)行得好,,優(yōu)化代碼要求編譯器做一些假設(shè),,去除冗余,,但有時(shí)這個(gè)假設(shè)是錯(cuò)誤的,并且去掉的冗余也有可能隱藏錯(cuò)誤,。如發(fā)布版本的幀指針(EBP寄存器)省略(FPO)隱藏了函數(shù)原型不匹配的錯(cuò)誤,;在同步異常模式(只能由throw語句拋出,編譯器默認(rèn),,由/GX編譯選項(xiàng)設(shè)置)下,,異常處理程序可能被優(yōu)化掉,會阻止程序中的C++異常處理代碼安全地捕獲結(jié)構(gòu)異常,,在這種情況下,,你必須使用異步異常模式(采取任何指令都會產(chǎn)生異常的機(jī)制,由/Eha編譯選項(xiàng)設(shè)置),。

(3)/D “_DEBUG”

  打開條件編譯調(diào)試代碼開關(guān),。只有這個(gè)符號被定義,調(diào)試代碼才會被編譯,,MFC使用_DEBUG符號來確定到底鏈接的是哪個(gè)版本的MFC類庫,。在調(diào)試版本中,內(nèi)聯(lián)默認(rèn)情況下是被關(guān)閉的,。

(4)/ZI

  創(chuàng)建編輯繼續(xù)(Edit and Continue)的程序數(shù)據(jù)庫,。這個(gè)選項(xiàng)會打開/GF編譯選項(xiàng),/GF編譯選項(xiàng)會消除重復(fù)字符串,,并將字符串放到只讀內(nèi)存,。編輯繼續(xù)功能需要獲取存儲在PDB文件里的特殊信息來使得代碼的修改對調(diào)試器有效。如果被修改文件對應(yīng)的信息不在PDB文件里,,編輯繼續(xù)功能就不能進(jìn)行,,而且在調(diào)試過程中對代碼的任何修改都會出現(xiàn)下面的提示信息“One or more files are out of date or do not exist.”。

(5)/GZ

在調(diào)試版本中用來發(fā)現(xiàn)那些在發(fā)布版本里才發(fā)現(xiàn)的錯(cuò)誤,。其作用如下:

.用0xCC模式初始化自動(dòng)(本地)變量,。

.在通過函數(shù)指針調(diào)用函數(shù)時(shí),檢查棧指針,,確認(rèn)是否有調(diào)用規(guī)則不匹配,。

.在函數(shù)最后檢查棧指針是否被改變,。

(6)/Gm

  打開最小化重新鏈接開關(guān),,減少鏈接時(shí)間。

2,、特別針對發(fā)布版本的編譯選項(xiàng)

(1)/MD,/ML或者/MT

  使用發(fā)布版本的運(yùn)行時(shí)刻庫,。

(2)/O1或者/O2

  打開優(yōu)化開關(guān),使得程序會最小或說速度會最快,,優(yōu)化器還可能發(fā)現(xiàn)代碼中潛在的錯(cuò)誤,,而這些錯(cuò)誤可能會被調(diào)試版本掩蓋,。

(3)/D “NDEBUG”

  關(guān)閉條件編譯調(diào)試代碼開關(guān)。

(4)/GF

  消除重復(fù)字符串并將它們放到只讀內(nèi)存中以避免被錯(cuò)誤地修改,。

(5)/Zi

創(chuàng)建包含調(diào)試符號的程序數(shù)據(jù)庫,。

  如果一個(gè)錯(cuò)誤只發(fā)生在發(fā)布版本里,除非你是個(gè)匯編高手,,否則你需要調(diào)試符號來提示你到底程序出現(xiàn)了什么問題,,調(diào)試符號保存在程序的數(shù)據(jù)庫文件(PDB)中。Visual C++的AppWizard默認(rèn)情況下沒有為發(fā)布版本創(chuàng)建調(diào)試符號,。為創(chuàng)建調(diào)試符號,,打開工程設(shè)置對話框,選擇Win32 Release,,在C/C++標(biāo)簽里選擇Common類,,在調(diào)試信息里,如果是發(fā)布版本選擇Program Database,,如果是調(diào)試版本選擇Program Database for Edit and Continue(編輯繼續(xù)選項(xiàng)與優(yōu)化鏈接不相容,,不適于發(fā)布版本)。在Link標(biāo)簽里選擇Debug類,,然后選擇Debug Info和Microsoft format選項(xiàng),,最好不要選擇Separate types選項(xiàng),這樣所有的調(diào)試信息才會被合并到單獨(dú)的一個(gè)PDB文件中,。對于發(fā)布版本,,選擇Link標(biāo)簽,在Project options對話框的最后加上“/OPT:REF”,,這個(gè)選項(xiàng)使得不被引用的函數(shù)和數(shù)據(jù)不會出現(xiàn)在可執(zhí)行文件中,,避免了文件的無謂增大。對于調(diào)試版本不要使用這個(gè)選項(xiàng),,它會關(guān)閉增量鏈接(incremental linking),。

二、Visual C++編輯器的“設(shè)置”菜單

  當(dāng)你打開或新建一個(gè)包含至少一個(gè)工程的Workspace后,,Visual C++的Project菜單中的“Settings…”命令就變?yōu)橛行?,選擇它或者按下熱鍵Alt+F7后,便可調(diào)出工程設(shè)置對話框,,這里面的選項(xiàng)將影響整個(gè)工程的建立和調(diào)試過程,,因此很重要。

  在這個(gè)對話框中,,左上方的下拉列表框用于選擇一種工程配置,,包括有Win32 Debug、Win32 Release和All Configurations(指前兩種配置一起),某些選項(xiàng)在不同的工程配置中有不同的缺省值,。左邊的樹形視圖給出了當(dāng)前工程所有的文件及分類情況,。下面我們就以Win32 Debug為例來看看與工程有關(guān)的的四個(gè)主要選項(xiàng)卡的各自功能與含義(一共有十個(gè)選項(xiàng)卡):

1、 General選項(xiàng)卡

  這個(gè)選項(xiàng)卡比較簡單,,從上向下的第一個(gè)選項(xiàng)用于更改使用MFC類庫的方式: DLL的方式或是靜態(tài)連接,。我們可以在兩種方式之間進(jìn)行切換。第二個(gè)選項(xiàng)用于指定在編譯連接過程中生成的中間文件和輸出文件的存放目錄,,對于調(diào)試版本來說,,缺省的目錄是工程下面的“Debug”子目錄。第三個(gè)選項(xiàng)用于指定是否允許每種工程配置都有自己的文件依賴關(guān)系(主要指頭文件),,由于絕大多數(shù)工程的調(diào)試版本和發(fā)布版本都具有相同的文件依賴關(guān)系,,所以通常不需要更改該選項(xiàng)。

2,、 Debug選項(xiàng)卡

  Debug選項(xiàng)卡中是一些與調(diào)試有關(guān)的選項(xiàng),,由于選項(xiàng)比較多,它們被分成了幾個(gè)類,,我們可以從Category中選擇不同的類別,,選項(xiàng)卡就會切換顯示出相應(yīng)的選項(xiàng)。

  在General類別中,,可以指定要調(diào)試的可執(zhí)行文件名,。另外三個(gè)選項(xiàng)可以指定用于調(diào)試的工作目錄,開始調(diào)試時(shí)給程序傳送的命令行參數(shù),,以及進(jìn)行遠(yuǎn)程調(diào)試時(shí)可執(zhí)行文件的路徑,。

3、C/C++選項(xiàng)卡

  C/C++選項(xiàng)卡控制著Visual C++的編譯器,,其中的選項(xiàng)比較多,。下面有一個(gè)Project Options編輯框,里面列出的各種命令開關(guān)將會在開始編譯時(shí)作為命令行參數(shù)傳送給Visual C++的編譯器,。這些命令開關(guān)會跟隨其它選項(xiàng)改變而改變,。

  在General類別中,Warning level用于指定編譯器顯示警告的級別,,如果選中了Warnings as errors,,那么顯示的每一個(gè)警告都將會引起一個(gè)錯(cuò)誤,這樣在編譯完畢后就無法啟動(dòng)連接器來進(jìn)行連接,。Optimizations用于設(shè)置代碼優(yōu)化方式,,優(yōu)化的目的主要有提高運(yùn)行速度和減小程序體積兩種,但有時(shí)候這兩種目的是相互矛盾的,。另外,,在極少數(shù)情況下,,不進(jìn)行優(yōu)化,,程序能正常運(yùn)行,,打開了優(yōu)化措施之后,程序卻會出現(xiàn)一些莫名其妙的問題,。其實(shí)這多半是程序中有潛在的錯(cuò)誤,,關(guān)閉優(yōu)化措施往往只是暫時(shí)解決問題。Debug info用于指定編譯器產(chǎn)生的調(diào)試信息的類型,,為了使用Visual C++的即編即調(diào)功能,,必須在這里選擇生成“Program Database for Edit and Continue”類型的調(diào)試信息。Preprocessor definitions是一些預(yù)先定義的宏名,。

  C++ Language類別中的選項(xiàng)涉及到了C++語言的一些高級特性,,包括有成員指針的表示方式、異常處理,、運(yùn)行時(shí)類型信息,,一般情況下都不用改變它們。Code Generation類別中的選項(xiàng)涉及如何生成目標(biāo)代碼,,一般情況下保持缺省值即可,。在Customize類別中,從上到下六個(gè)選項(xiàng)的含義分別為:是否禁止使用Microsoft對C++的擴(kuò)展,;是否允許函數(shù)級別的連接,;是否消除重復(fù)的字符串;是否允許進(jìn)行最小化的重建,;是否允許遞增編譯方式,;是否允許編譯器在開始運(yùn)行時(shí)向Output窗口中輸出自己的版本信息。

  在Listing Files類別中,,我們可以指定編譯器生成瀏覽信息和列表文件(Listing file),,前者可由瀏覽信息維護(hù)工具BSCMAKE生成瀏覽信息文件,后者則包含了C/C++源文件經(jīng)過編譯后對應(yīng)的匯編指令,。Optimizations類別允許我們對優(yōu)化措施進(jìn)行更細(xì)微的控制,,選擇了Customize后,便可以選擇進(jìn)行哪幾項(xiàng)優(yōu)化,,在Inline function expansion中我們可以指定對內(nèi)聯(lián)函數(shù)的擴(kuò)展方式,。Precompiled Headers類別中是關(guān)于預(yù)編譯頭文件的一些選項(xiàng),一般情況下都不用更改,。Preprocessor類別中是關(guān)于預(yù)處理的一些選擇,。

4、Link 選項(xiàng)卡

  Link選項(xiàng)卡控制著Visual C++的連接器,。在General類別中,,可以指定輸出的文件名,,以及一些在連接過程中需要使用的額外的庫文件或目標(biāo)文件,下邊五個(gè)選項(xiàng)的含義分別為:生成調(diào)試信息,;忽略所有缺省的庫文件,;允許遞增連接方式(這種方式可以加快連接的速度);生成MAP文件,;允許進(jìn)行性能分析,。在Customize中選中Use program database允許使用程序數(shù)據(jù)庫。在Debug類別中,,我們可以指定調(diào)試信息的類別是Microsoft的格式,,還是COFF格式,或者兩種都有,,選中Separate types后連接器會把調(diào)試信息分開放在PDB文件中,,這樣連接起來會更快一些,但調(diào)試時(shí)速度卻會慢一些,。Input類別中是一些與輸入庫文件有關(guān)的選項(xiàng),,我們可以在這里指定使用或不使用某些庫文件或目標(biāo)文件。Output類別中則是一些與最終輸出的可執(zhí)行文件有關(guān)的選項(xiàng),,一般情況下都不用改變,。

三、Visual C++調(diào)試工具

1,、調(diào)試窗口

(1)觀察窗口(Watch)

  調(diào)試程序時(shí),,可使用觀察窗口監(jiān)視變量和表達(dá)式。

(2)快速查看窗口(Quick watch)

  功能和觀察窗口差不多,。

(3)變量窗口(Variables)

  變量窗口有三個(gè)標(biāo)簽:Auto標(biāo)簽顯示了當(dāng)前語句和前一條語句用到的變量,,Locals標(biāo)簽顯示當(dāng)前函數(shù)的局部變量,this標(biāo)簽顯示了this指針執(zhí)行的對象,。

(4)寄存器窗口(Register)

  可以監(jiān)視CPU的寄存器,、標(biāo)志值以及浮點(diǎn)堆棧

(5)內(nèi)存窗口(Memory)

  可顯示從一特定地址開始的虛擬內(nèi)存。Address框允許你指定從哪個(gè)虛擬內(nèi)存地址開始顯示,。

(6)調(diào)用棧窗口(Call stack)

  可顯示引起當(dāng)前源代碼語句執(zhí)行的一系列函數(shù)調(diào)用,,當(dāng)前函數(shù)在堆棧的頂端。

(7)反匯編窗口(Disassembly)

  可查看編譯器生成的對應(yīng)于源代碼的匯編指令,。

2,、調(diào)試符號

  程序數(shù)據(jù)庫文件(.pdb)包含了Visual C++調(diào)試器所需的調(diào)試信息和程序信息。調(diào)試信息包含了變量的名字和類型,、函數(shù)原型,、源代碼行號、類和結(jié)構(gòu)的布局,、FPO調(diào)試信息(重建堆棧幀)以及進(jìn)行增量鏈接所需的信息,。對于設(shè)置了Program Database for Edit and Continue選項(xiàng)的程序,,PDB還要包含執(zhí)行編輯繼續(xù)功能所需的信息。

3,、使用斷點(diǎn)

  斷點(diǎn)(BreakPoint)是運(yùn)行你向調(diào)試器描述環(huán)境,,并讓調(diào)試器設(shè)置好程序狀態(tài)的一種機(jī)制。如果沒有斷點(diǎn),,只有在程序里一步一步跟蹤使用調(diào)試器,。在Visual C++中,,你可以設(shè)置三種類型的斷點(diǎn):代碼定位斷點(diǎn),、數(shù)據(jù)斷點(diǎn)和消息斷點(diǎn)。

四,、提高調(diào)試器的查錯(cuò)能力

  盡量采用編譯時(shí)刻檢查而不是運(yùn)行時(shí)刻檢查,。

1、使用最高的編譯警告級別/W4

  象if(x=2)這樣的語句,,默認(rèn)的警告級別為/W3時(shí)不顯示任何信息,,但改成最高警告級別/W4時(shí)則會出現(xiàn)“waning C4706:assignment within conditional expression”的警告。/W4能給出一些/W3所不能給的警告,。

2,、在調(diào)試版本中使用/GZ編譯選項(xiàng)

  /GZ選項(xiàng)用來發(fā)現(xiàn)那些在發(fā)布版本里才發(fā)現(xiàn)的錯(cuò)誤,包括未被初始化的自動(dòng)(局部)變量,、堆棧錯(cuò)誤,、不正確的函數(shù)原型等。

3,、使用#pragma warning編譯器指示

  你可以使用#pragma warning編譯器指示來禁止整個(gè)程序,、特定的頭文件、特定的代碼文件或是特定的某一行代碼的特定警告,,這看你把#pragma放在哪里,。

4、使用沒有警告的編譯法則/WX

  這個(gè)編譯選項(xiàng)把所有的警告當(dāng)成錯(cuò)誤來對待,,只有在假警告被消除之后才能應(yīng)用,。有時(shí)編譯警告可能是合理的,處理編譯警告的核心是要發(fā)現(xiàn)錯(cuò)誤,,而不是抑制警告本身,。這個(gè)法則對于大的程序開發(fā)小組來說很有幫助。最終目標(biāo)是消除錯(cuò)誤,,而不是消除警告,。

五、內(nèi)存空間與分配

1,、內(nèi)存分配錯(cuò)誤

  動(dòng)態(tài)內(nèi)存分配錯(cuò)誤有兩種基本類型:內(nèi)存錯(cuò)誤和內(nèi)存泄漏,。

(1)內(nèi)存錯(cuò)誤

  當(dāng)一個(gè)指針或者該指針?biāo)赶虻膬?nèi)存單元成為無效單元,,或者內(nèi)存中分配的數(shù)據(jù)結(jié)構(gòu)被破壞時(shí),就會造成內(nèi)存錯(cuò)誤,。指針未被初始化,,指針被初始化為一個(gè)無效地址,指針被不小心錯(cuò)誤地修改,,在與指針相關(guān)聯(lián)的內(nèi)存區(qū)域被釋放后使用該指針(這種指針被稱為虛懸(dangling)指針),,這些都會使指針變?yōu)闊o效指針。當(dāng)通過一個(gè)錯(cuò)誤指針或者虛懸指針對內(nèi)存進(jìn)行寫入,,或者將指針強(qiáng)制轉(zhuǎn)換為不匹配的數(shù)據(jù)結(jié)構(gòu),,又或者是寫數(shù)據(jù)越界,內(nèi)存自身也會遭到破壞,。刪除未被初始化的指針,、刪除非堆指針、多次刪除同一指針或者覆蓋一個(gè)指針的內(nèi)部數(shù)據(jù)結(jié)構(gòu),,都會造成內(nèi)存分配系統(tǒng)錯(cuò)誤,。

(2)內(nèi)存泄漏

  內(nèi)存泄漏在被動(dòng)態(tài)分配的內(nèi)存沒有被釋放時(shí)產(chǎn)生。有許多情況會導(dǎo)致內(nèi)存泄漏,,如沒有在程序的全部執(zhí)行路徑中釋放內(nèi)存,,沒有在析構(gòu)函數(shù)中釋放所有的內(nèi)存等。一個(gè)程序在崩潰之前可運(yùn)行的時(shí)間越長,,則導(dǎo)致崩潰的原因與內(nèi)存泄漏的關(guān)系越大,。

  Windows會在程序結(jié)束的時(shí)候?qū)⑿孤┑膬?nèi)存收回,因此內(nèi)存泄漏是個(gè)暫時(shí)性的問題,。但為什么必須消除內(nèi)存泄露呢,?首先,內(nèi)存泄漏往往會導(dǎo)致系統(tǒng)資源的泄漏,。動(dòng)態(tài)分配內(nèi)存往往不僅僅代表一塊存儲區(qū)域,,還代表了某些類型的系統(tǒng)資源,如文件,、窗口,、設(shè)備上下文、GDI對象等,。其次,,高質(zhì)量的程序和特定的服務(wù)器程序必須能夠無限地運(yùn)行下去。最后,,內(nèi)存泄漏往往是其他程序錯(cuò)誤或不良編程習(xí)慣的征兆,。

  導(dǎo)致內(nèi)參泄漏的原因:忘記釋放內(nèi)存;構(gòu)造函數(shù)失??;存在內(nèi)存泄漏的析構(gòu)函數(shù),;存在內(nèi)存泄漏的異常處理程序;多個(gè)返回語句,;使用錯(cuò)誤形式的delete,。

2、關(guān)于內(nèi)存的初始化

  在調(diào)試版本里,,堆里未被初始化的內(nèi)存被0xCD字節(jié)模式填充,,堆里釋放的內(nèi)存被0xDD字節(jié)模式填充。堆棧里被初始化的內(nèi)存被0xCC字節(jié)模式填充,。調(diào)試版本和發(fā)布版本里,,未被初始化的全局內(nèi)存都被初始化為0。

3,、內(nèi)存虛擬地址空間

  Windows使用一組固定的范圍來分割進(jìn)程的4GB虛擬地址空間,,因此有時(shí)可通過查看指針的返回值來判斷指針是否有效,。

(1)Windows2000虛擬地址空間劃分

0~0XFFFF(64KB):不能用來檢測空指針賦值(訪問沖突)

0x10000(64KB)~0x7FFEFFFF(2GB-64KB):Win32進(jìn)程私有的(非保留的),,用于程序代碼和數(shù)據(jù)

0x7FFF0000(2GB-64KB)~0x7FFFFFFF(2GB):不能用來防止覆蓋OS分區(qū)(訪問沖突)

0x800000000(2GB)~0xFFFFFFFF(4GB):為操作系統(tǒng)保留,不可訪問(訪問沖突)

(2)Windows2000虛擬地址空間使用

0x00030000~0x0012FFFF:線程棧

0x00130000~0x003FFFFF:堆(有時(shí)堆位于此處)

0x00400000~0x005FFFFF:可執(zhí)行代碼

0x00600000~0x0FFFFFFF:堆(有時(shí)堆位于此處)

0x10000000~0x5FFFFFFF:App DLLs,、Msvcrt.dll,、Mfc42.dll

0x77000000~0xFFFFFFFF:Advapi32.dll、Comctl32.dll,、Gdi32.dll,、Kernel32.dll、Ntdll.dll,、Rpcrt4.dll,、Shell32.dll、User32.dll

其中,,0x00400000是所有版本的Windows能使用的最低基地址,。

六、一些調(diào)試技術(shù)

1,、調(diào)試死循環(huán)

  使用Debug菜單下的Break命令,。在Windows2000中,如果程序有輸入請求,,可以使用F12鍵中斷程序,,然后檢查窗口的調(diào)用棧,或單步跟蹤代碼找到死循環(huán)的發(fā)生原因,。

2,、用Spy++調(diào)試與消息有關(guān)的問題

  調(diào)試消息的最好方案是使用Visual C++提供的Spy++工具。Spy++允許程序員查看窗口,、消息,、進(jìn)程和線程,。Spy++默認(rèn)的消息輸出:第一欄顯示行號。第二欄顯示接受消息的句柄,。第三欄中的“S”表示消息是用SendMessage發(fā)出的,,“P”代表消息是由PostMessage發(fā)出的,“R”是消息句柄的返回值,。第四欄給出解碼后的消息名,,消息參數(shù)或返回值。

3,、非常規(guī)方法

(1)重新編連你的應(yīng)用程序

  當(dāng)你的程序表現(xiàn)出異常的或意外的行為,,或者Visual C++編譯器因?yàn)橐粋€(gè)內(nèi)部編譯器錯(cuò)誤而失敗時(shí),最好刪除工程中的Debug或Release文件夾,,從頭開始重新進(jìn)行編連,。

(2)重新啟動(dòng)Visual C++

  Visual C++有超強(qiáng)的能力,但編譯器的某些特性也會引起奇怪的錯(cuò)誤,。如果你的程序表現(xiàn)得很奇怪,,你可是試著清除所有的斷點(diǎn),關(guān)閉或隱藏觀察窗口,,檢查工程設(shè)置對話框看最近做了什么修改,,直至重新啟動(dòng)Visual C++以便消除由于Visual C++環(huán)境引起的異常行為。

(3)重新啟動(dòng)Windows

  當(dāng)你發(fā)現(xiàn)Windows或者其他程序表現(xiàn)出異常的或出人意料的行為時(shí),,就應(yīng)該重新啟動(dòng)Windows,,以消除操作系統(tǒng)給調(diào)試帶來的干擾。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多