掌握C++ builder的除錯(cuò)藝術(shù)(2).1
2019-09-06 23:33:48
供稿:網(wǎng)友
掌握C++Builder的除錯(cuò)藝術(shù)
第二篇-近距離觀察(1)
1./t調(diào)試可執(zhí)行程序前的準(zhǔn)備
2./t工程選項(xiàng)
3./t設(shè)置斷點(diǎn)并闖入可執(zhí)行程序
4./t察看儲(chǔ)存在變量中的值
5./t使用Watches(觀察)
6./t使用Inspectors(巡視器)
7./t使用Evaluate/Modify(求值/修改)
8./tStepping Through, Over and Around Blocks of Code
9./tStepping的類(lèi)型
10. Stepping的注解
11. 其他提示
Okay,(再小小準(zhǔn)備一下)現(xiàn)在開(kāi)始追蹤、搜索經(jīng)過(guò)前次的努力后仍然躲在代碼中的bug的時(shí)候了,也就是開(kāi)始跟蹤前一篇文章代碼里標(biāo)記過(guò)的bug/異常。首先是準(zhǔn)備階段。
調(diào)試可執(zhí)行程序前的準(zhǔn)備
在我們開(kāi)始調(diào)試可執(zhí)行程序前,我們需要確保一些設(shè)置在大多數(shù)情況下的正確性。我將會(huì)一條接一條的過(guò)一遍,并簡(jiǎn)單解釋一下為什么必須那樣做。(如果您對(duì)有些東西感興趣的話(huà),按下幫助按鈕,會(huì)有許多更詳盡的內(nèi)容)。現(xiàn)在就開(kāi)始吧,先打開(kāi)Project|Options選項(xiàng)。
工程選項(xiàng)
首先我們?cè)?Compiler"(編譯)標(biāo)簽處停下。您只需簡(jiǎn)單的單擊"Full debug"(完全調(diào)試模式)按鈕,我們所需的絕大多數(shù)的其余設(shè)置就已經(jīng)搞定了。將"Code optimization"(代碼優(yōu)化)設(shè)為"None"(無(wú))總是件好事,這樣做實(shí)際上告訴編譯器:所有的事情都已做好,只需產(chǎn)生機(jī)器碼就行了。而不要為了提高一點(diǎn)點(diǎn)運(yùn)行速度嘗試進(jìn)行其他的智能優(yōu)化。(當(dāng)然,一切都完成之后,您可以打開(kāi)此項(xiàng)。)這樣做的好處是大大降低了我們調(diào)試的難度。因?yàn)槌绦蛑械拇a與我們書(shū)寫(xiě)的一樣,沒(méi)有被編譯器優(yōu)化過(guò)。在"debugging"(調(diào)試)面板中,將"Debug information"(調(diào)試信息)選上(點(diǎn)一下),并且必須設(shè)置為"Line number information"(行數(shù)信息)。我還建議將"Disable inline expansions"(禁用內(nèi)聯(lián)擴(kuò)展)一項(xiàng)選上。內(nèi)聯(lián)擴(kuò)展對(duì)發(fā)布的代碼來(lái)說(shuō)很好,但調(diào)試時(shí)最好還是關(guān)掉此項(xiàng),他只會(huì)讓您更頭痛。
然后是"Pascal"標(biāo)簽,尤其在您的工程里連接了Pascal單元或使用了基于Pascal的VCL控件時(shí)(若您擁有其Pascal源碼時(shí),編譯器會(huì)自動(dòng)使用此節(jié)中的設(shè)置重新編譯)。這里您必須將"Optimization"優(yōu)化選項(xiàng)禁用,然后通常我會(huì)將"debugging"(調(diào)試)部分的所有選項(xiàng)選上(打鉤)。
接下來(lái)是"Linker"(鏈接)標(biāo)簽,我們需要選上"Create debug information"(生成調(diào)試信息)。"Use dynamic RTL"(使用動(dòng)態(tài)RTL)以及"Don’t generate state files"(不要生成狀態(tài)文件)是造成麻煩的選項(xiàng)。我通常都會(huì)使用狀態(tài)文件(這樣允許增量鏈接,但會(huì)在編譯目錄下產(chǎn)生一個(gè)4倍于可執(zhí)行程序或更大的文件),換個(gè)角度來(lái)說(shuō),這樣會(huì)增加鏈接大工程時(shí)的速度。而使用dynamic RTL本身就是個(gè)爭(zhēng)論,尚有很多贊同和反對(duì)的討論。
下一個(gè)是"Directories/Conditionals"(路徑/條件)標(biāo)簽。在這里我們想要設(shè)定"Directories/Conditionals"(調(diào)試源路徑)的值。我們永遠(yuǎn)都應(yīng)將此處設(shè)定為$(BCB)sourcevcl,但是如果您有任何其他的組件附加的話(huà),通常將它們的路徑也加上是個(gè)好主意(路徑與路徑之間用”;”分隔或者您可以用按下…按鈕彈出的對(duì)話(huà)框來(lái)設(shè)定它們)。
最后也是最重要的設(shè)置是在"Packages"(程序包)標(biāo)簽上。根據(jù)所有恰如其分的調(diào)試經(jīng)驗(yàn)?zāi)仨毥?Build with runtime packages"(帶運(yùn)行時(shí)程序包編譯)。這么做的原因是程序包本身不包含而且不能包含調(diào)試信息。這樣做,也許不利于您跟蹤標(biāo)準(zhǔn)的VCL代碼,例如想看清楚VCL函數(shù)y中參數(shù)x是如何起作用的時(shí)候。但是大多數(shù)時(shí)候,您這么做將會(huì)發(fā)現(xiàn)調(diào)試器將您的絕大多數(shù)“癥狀”歸結(jié)給VCL,盡管“病因”就在您的源代碼中(或在其他的組件中(這已經(jīng)在我們所有人身上發(fā)生了))。一旦您發(fā)布您的正式版本時(shí),您可以決定是否使用程序包。(譯者注:程序包的本質(zhì)是一個(gè)特殊的DLL,不帶運(yùn)行程序包(靜態(tài))編譯可以讓您的程序脫離Cbuilder獨(dú)立運(yùn)行。),但在調(diào)試時(shí),請(qǐng)禁用掉。按下OK按鈕,我們已經(jīng)準(zhǔn)備好啦。下一個(gè)對(duì)話(huà)框只需打開(kāi)一次,但最好還是來(lái)檢查以下我們?cè)谶@里的設(shè)定是否正確。好了,打開(kāi)”Tools|Debugger Options…”吧。
對(duì)話(huà)框最下方的"Integrated debugging"(集成調(diào)試器)選項(xiàng)是關(guān)鍵所在。確信已經(jīng)打上鉤。按下OK按鈕準(zhǔn)備編譯可執(zhí)行程序吧。我建議重新來(lái)一次徹底的編譯(選擇Project|Build All),如果您修改過(guò)您的設(shè)置的話(huà)(尤其是改變”building with packages”方式后)。這將保證我們的所有程序單元按照我們所希望的那樣被編譯。
設(shè)置斷點(diǎn)并闖入可執(zhí)行程序
象您所見(jiàn)過(guò)的其他任何一款調(diào)試器一樣,C++Builder提供強(qiáng)大的斷點(diǎn)設(shè)置功能?;旧希瑪帱c(diǎn)是指代碼中的一個(gè)點(diǎn),程序執(zhí)行至此停下(與退出不同,這只是執(zhí)行中的暫停)并將控制權(quán)交還給調(diào)試器。設(shè)置一個(gè)斷點(diǎn)相當(dāng)容易。只需在您想要設(shè)置的程序代碼行左側(cè)的灰色槽形區(qū)域點(diǎn)擊,您會(huì)看到一個(gè)紅點(diǎn)出現(xiàn),這一行也會(huì)變紅。程序運(yùn)行到這一點(diǎn)就會(huì)暫停,將控制權(quán)交還給調(diào)試器。
您也許會(huì)問(wèn)如果我不想每次都停下來(lái)呢?當(dāng)然可以,而且還很容易做到,這取決于您暫停程序的標(biāo)準(zhǔn)是什么?(譯者注:條件斷點(diǎn))。在剛才那個(gè)斷點(diǎn)(紅點(diǎn))上右擊鼠標(biāo)并從彈出菜單上選擇” Breakpoint Properties”(斷點(diǎn)屬性)。此處可以設(shè)定兩種屬性"Condition"(條件)和"Pass Count"(通過(guò)次數(shù))。Condition(條件)屬性太方便了。您可以利用if()語(yǔ)句輸入幾乎是任意的條件。但請(qǐng)牢記條件中的所有變量,對(duì)此斷點(diǎn)都應(yīng)是可見(jiàn)的。條件屬性并未被編譯器編譯到執(zhí)行程序中,而是在運(yùn)行時(shí),當(dāng)程序運(yùn)行至斷點(diǎn)暫停后,檢查斷點(diǎn)的條件是否滿(mǎn)足。條件為真,停下,否則讓程序繼續(xù)運(yùn)行。另一個(gè)屬性"Pass Count"(通過(guò)次數(shù))也很容易理解。斷點(diǎn)將被通過(guò)Pass Count次后停下。結(jié)合使用這兩個(gè)屬性,在調(diào)試您的代碼時(shí),您可以設(shè)定非常嚴(yán)格的斷點(diǎn)。
還有一件要牢記的是,當(dāng)您在調(diào)試器中發(fā)生異常時(shí),會(huì)以產(chǎn)生異常處的那一行代碼上的斷點(diǎn)的形式出現(xiàn)。這種情況很容易制造。一旦您得到一個(gè)異常后應(yīng)做的步驟我會(huì)在以后展示如何在堆棧中回溯并跟蹤找出異常發(fā)生的真正原因(如引起異常產(chǎn)生的那一小片代碼)。
另一個(gè)要牢記的提示是當(dāng)您運(yùn)行您的程序時(shí),代碼窗口左側(cè)有藍(lán)點(diǎn)的任意一行都可以設(shè)成斷點(diǎn)。所有非法的斷點(diǎn)將會(huì)變?yōu)榧t點(diǎn)中帶一個(gè)黃色的小叉,這一行代碼也會(huì)變成黃褐色。合法的斷點(diǎn)則變?yōu)榧t點(diǎn)中帶一個(gè)綠色的小鉤。運(yùn)行時(shí),您可以設(shè)置/修改任意一點(diǎn),斷點(diǎn)立即生效而無(wú)須重新編譯。
察看儲(chǔ)存在變量中的值
一旦您的程序在您的斷點(diǎn)處停下后,該做什么?有一件事您想做而且必須做的,那就是察看儲(chǔ)存在您程序中的各種變量真實(shí)的值。這部分內(nèi)容涉及的方面很多,您一定要堅(jiān)持,忍受這些枯燥的東西。幸運(yùn)的是當(dāng)您看完這些,您一定會(huì)對(duì)調(diào)試器這部分最強(qiáng)大的功能有些新的理解。有許多種方法可以察看變量的值,主要要根據(jù)您的目的來(lái)決定。我會(huì)從察看當(dāng)前函數(shù)的Local Variables(局部變量)開(kāi)始把他們都講完。
察看局部變量沒(méi)有太多可以講的。只需點(diǎn)擊”View|Debug Windows|Local Variables”,或按下ctrl-alt-L將會(huì)彈出一個(gè)窗口,顯示了當(dāng)前函數(shù)的局部變量。窗口中的變量將會(huì)隨您單步向下執(zhí)行或回溯的函數(shù)體的更新而更新。
使用Watches(觀察)
下一步您可以通過(guò)設(shè)定一個(gè)variable watch(變量觀察)來(lái)察看程序中的變量。就象它的名稱(chēng)所表達(dá)的,觀察一個(gè)變量并將其值顯示在變量觀察窗口中(點(diǎn)擊"View|Debug Windows|Watches"或按下 ctrl-alt-W)。您可以通過(guò)兩個(gè)途徑來(lái)添加一個(gè)觀察,第一種是在代碼窗口中高亮選擇您要觀察的變量或表達(dá)式(是的!它可以理解并對(duì)絕大多數(shù)簡(jiǎn)單表達(dá)式求值,比如(i*j)+05 或者 SomeVector.Name)并右擊鼠標(biāo),選擇"Debug|Add Watch at Cursor"或按下ctrl-f5,就會(huì)加入觀察窗口。如果必要,同時(shí)會(huì)打開(kāi)觀察窗口
您還可以通過(guò)在觀察窗口的空白處雙擊來(lái)添加。這時(shí)會(huì)彈出添加watch對(duì)話(huà)框,"Expression"(表達(dá)式)域的意思無(wú)須多說(shuō),但另幾個(gè)域我想解釋一下,它們也同樣方便。
"Repeat count"(重復(fù)值)用于您觀察一個(gè)已知長(zhǎng)度的數(shù)組變量(比如一個(gè)blah[50]數(shù)組)。您要將Expression(表達(dá)式)設(shè)為數(shù)組的名字(本例中是blah)。"Repeat count"設(shè)為數(shù)組的元素?cái)?shù)量(本例中是50)。然后就會(huì)顯示數(shù)組的每個(gè)元素(如:blah[0], blah[1], blah[2]…)。
"Digits"(小數(shù)位數(shù))用來(lái)設(shè)定顯示十進(jìn)制浮點(diǎn)數(shù)的小數(shù)位數(shù)的。下面的點(diǎn)選集合是用來(lái)強(qiáng)制設(shè)定變量的顯示類(lèi)型的(將無(wú)符號(hào)長(zhǎng)整數(shù)顯示為十六進(jìn)制格式)。還有一點(diǎn)要特別說(shuō)明的是,如果您在watch窗口中用鼠標(biāo)右擊一個(gè)watch后的彈出菜單上會(huì)出現(xiàn)"Break When Changed"的選項(xiàng),這將在變量上設(shè)定一個(gè)斷點(diǎn),在此變量發(fā)生變化時(shí)會(huì)暫停程序。
使用Inspectors(巡視器)
巡視變量是察看變量中的數(shù)據(jù)的第三種辦法。也幾乎是觀察完整的類(lèi)的數(shù)據(jù)的最佳方法??梢杂袃煞N方法來(lái)巡視一個(gè)變量。第一種是在local variable window(局部變量窗口)中,雙擊一個(gè)變量,將會(huì)彈出"Debug Inspector"(調(diào)試巡視器)窗口,里面顯示了這個(gè)變量所有的"Data" (variables) (數(shù)據(jù)(變量))、"Methods" (functions)(方法(函數(shù)))和"Properties"(屬性)。如果這是個(gè)簡(jiǎn)單數(shù)據(jù),將會(huì)顯示此變量的名稱(chēng)及其中的值。(譯者注:如果是數(shù)組呢?真不錯(cuò)?。?br/>
您會(huì)注意到,Debug Inspector(調(diào)試巡視器)很象property editor(屬性編輯器)。當(dāng)然如此,更加重要的是,事實(shí)上您可以在運(yùn)行時(shí)實(shí)時(shí)改變這些值?。?!小心使用啦!改入壞值的結(jié)果會(huì)讓您有說(shuō)不出來(lái)的悲痛。巡視器的這個(gè)能力可用來(lái)快速測(cè)試(假設(shè)的)游戲關(guān)卡(譯者注:好像FPE,GM),而不用有編譯-運(yùn)行-修改-編譯-運(yùn)行的循環(huán)。
(舉例巡視Form1)在properties(屬性)頁(yè)上,您將會(huì)看到某些屬性實(shí)際上并沒(méi)有顯示其的值,而是顯示了{(lán)read=,write=}。如果這些值可以被賦值的話(huà),當(dāng)您在此區(qū)域單擊后,您會(huì)注意到一個(gè)"?"按鈕出現(xiàn)在屬性的右側(cè)。單擊這個(gè)按鈕將會(huì)系統(tǒng)執(zhí)行適當(dāng)?shù)暮瘮?shù)來(lái)嘗試取回屬性的值。我們可以在這兒舉個(gè)例子-就舉Form1的MDIChildCount的屬性吧。在MDIChildCoun的屬性值區(qū)域上單擊,在按下"?"按鈕,哇,0(正是非-MDI的程序的指定值)。調(diào)試巡視器強(qiáng)大的能力并未到此為止。在巡視器的成員變量的適當(dāng)區(qū)域雙擊可以打開(kāi)成員變量的巡視窗口,提供與您開(kāi)始打開(kāi)窗口一樣的能力。
巡視器窗口的另一個(gè)有用的功能是從對(duì)象繼承的能力。這可以在通過(guò)在適當(dāng)區(qū)域上右擊選擇"Descend"(繼承)來(lái)做到。繼承的結(jié)果是產(chǎn)生了一個(gè)新的變量。您會(huì)注意到頂部的下拉List box中的變量名稱(chēng)已經(jīng)換成新的變量名了。您可以直接在ListBox中切換巡視的變量。這使得在對(duì)象的不同部分快速切換變得非常簡(jiǎn)單,而不會(huì)讓大大小小的巡視器窗口擾亂您的工作空間。
有一點(diǎn)要牢記的是,如果您離開(kāi)函數(shù),或者離開(kāi)變量的作用范圍,調(diào)試巡視器會(huì)失去對(duì)變量的跟蹤。若您需要再次察看的話(huà),請(qǐng)重新設(shè)置巡視器。但是您在當(dāng)前函數(shù)的代碼中單步運(yùn)行的話(huà),巡視器會(huì)自動(dòng)刷新。
版權(quán)說(shuō)明:
國(guó)內(nèi)的網(wǎng)站上,有許多關(guān)于C++Builder的內(nèi)容,但多以軟件、組件為主。論壇里也大都不能令人滿(mǎn)意,很空虛的感覺(jué)。書(shū)籍又都昂貴,內(nèi)容卻有搶錢(qián)之嫌。對(duì)銀子不足的初學(xué)者、自學(xué)者關(guān)愛(ài)不夠,因而想盡自己的綿薄之力。
文中的所有資料都是從國(guó)外網(wǎng)站上收集而來(lái)。因?yàn)镋文不方便,所以翻成中文。因?yàn)镋nglish和計(jì)算機(jī)都不是非常好,文中的錯(cuò)誤在所難免。若大家覺(jué)得有用的話(huà),我計(jì)劃不斷搜集翻譯一些有用的東西。
有任何意見(jiàn)和建議請(qǐng)mailto:cker@sina.com
您可以隨意復(fù)制、分發(fā)、下載此文檔。但未經(jīng)本人同意,您不可以截取、改動(dòng)本文片斷,或用本文謀取任何形式的利益。