掌握C++Builder的除錯藝術-第二篇(完整版)之一 (轉)

worldblog發表於2007-12-05
掌握C++Builder的除錯藝術-第二篇(完整版)之一 (轉)[@more@]

 

 

 

Mastering the Art of Deging in C++Builder
Article 2 - Watching it Closely:namespace prefix = o ns = "urn:schemas--com::office" />

掌握C++Builder的除錯藝術

第二篇-近距離觀察

(1) 

1.  可前的準備

2.  工程選項

3.  設定斷點並闖入可執行程式

4.  察看儲存在變數中的值

5.  使用Watches(觀察)

6.  使用Inspectors(巡視器)

7.  使用Evaluate/Modify(求值/修改)bytamin-c_Articles%2522%2520l%2520">

8.  Through, Over and Around Blocks of Code

9.  的型別

10.  Stepping的註解

11.  其他提示

Okay,(再小小準備一下)現在開始追蹤、搜尋經過前次的努力後仍然躲在程式碼中的bug的時候了,也就是開始跟蹤前一篇文章程式碼裡標記過的bug/異常。首先是準備階段。

除錯可執行程式前的準備

在我們開始除錯可執行程式前,我們需要確保一些設定在大多數情況下的正確性。我將會一條接一條的過一遍,並簡單解釋一下為什麼必須那樣做。(如果您對有些東西感興趣的話,按下幫助按鈕,會有許多更詳盡的內容)。現在就開始吧,先開啟Project|Options選項。

工程選項

首先我們在"Compiler"(編譯)標籤處停下。您只需簡單的單擊"Full debug"(完全除錯)按鈕,我們所需的絕大多數的其餘設定就已經搞定了。將"Code optimization"(程式碼)設為"None"(無)總是件好事,這樣做實際上告訴:所有的事情都已做好,只需產生機器碼就行了。而不要為了提高一點點執行速度嘗試進行其他的智慧最佳化。(當然,一切都完成之後,您可以開啟此項。)這樣做的好處是大大降低了我們除錯的難度。因為程式中的程式碼與我們書寫的一樣,沒有被編譯器最佳化過。在"debugging"(除錯)皮膚中,將"Debug information"(除錯資訊)選上(點一下),並且必須設定為"Line number information"(行數資訊)。我還建議將"Disable inline expansions"(禁用內聯擴充套件)一項選上。內聯擴充套件對釋出的程式碼來說很好,但除錯時最好還是關掉此項,他只會讓您更頭痛。

然後是"Pascal"標籤,尤其在您的工程裡連線了Pascal單元或使用了基於Pascal的VCL時(若您擁有其Pascal原始碼時,編譯器會自動使用此節中的設定重新編譯)。這裡您必須將"Optimization"最佳化選項禁用,然後通常我會將"debugging"(除錯)部分的所有選項選上(打鉤)。

接下來是"Linker"(連結)標籤,我們需要選上"Create debug information"(生成除錯資訊)。"Use dynamic RTL"(使用動態RTL)以及"Don’t generate state files"(不要生成狀態)是造成麻煩的選項。我通常都會使用狀態檔案(這樣允許增量連結,但會在編譯目錄下產生一個4倍於可執行程式或更大的檔案),換個角度來說,這樣會增加連結大工程時的速度。而使用dynamic RTL本身就是個爭論,尚有很多贊同和反對的討論。

下一個是"Directories/Conditionals"(路徑/條件)標籤。在這裡我們想要設定"Directories/Conditionals"(除錯源路徑)的值。我們永遠都應將此處設定為$(BCB)vcl,但是如果您有任何其他的附加的話,通常將它們的路徑也加上是個好主意(路徑與路徑之間用”;”分隔或者您可以用按下…按鈕彈出的對話方塊來設定它們)。

最後也是最重要的設定是在"Packages"(程式包)標籤上。根據所有恰如其分的除錯您必須禁用"Build with runtime packages"(帶執行時程式包編譯)。這麼做的原因是程式包本身不包含而且不能包含除錯資訊。這樣做,也許不利於您跟蹤標準的VCL程式碼,例如想看清楚VCLy中引數x是如何起作用的時候。但是大多數時候,您這麼做將會發現偵錯程式將您的絕大多數“症狀”歸結給VCL,儘管“病因”就在您的中(或在其他的元件中(這已經在我們所有人身上發生了))。一旦您釋出您的正式版本時,您可以決定是否使用程式包。(譯者注:程式包的本質是一個特殊的DLL,不帶執行程式包(靜態)編譯可以讓您的程式脫離Cbuilder獨立執行。),但在除錯時,請禁用掉。按下OK按鈕,我們已經準備好啦。下一個對話方塊只需開啟一次,但最好還是來檢查以下我們在這裡的設定是否正確。好了,開啟”Tools|Debugger Options…”吧。

對話方塊最下方的"Integrated debugging"(整合偵錯程式)選項是關鍵所在。確信已經打上鉤。按下OK按鈕準備編譯可執行程式吧。我建議重新來一次徹底的編譯(選擇Project|Build All),如果您修改過您的設定的話(尤其是改變”building with packages”方式後)。這將保證我們的所有程式單元按照我們所希望的那樣被編譯。

設定斷點並闖入可執行程式

象您所見過的其他任何一款偵錯程式一樣,C++Builder提供強大的斷點設定功能。基本上,斷點是指程式碼中的一個點,程式執行至此停下(與退出不同,這只是執行中的暫停)並將控制權交還給偵錯程式。設定一個斷點相當容易。只需在您想要設定的程式程式碼行左側的灰色槽形區域點選,您會看到一個紅點出現,這一行也會變紅。程式執行到這一點就會暫停,將控制權交還給偵錯程式。

您也許會問如果我不想每次都停下來呢?當然可以,而且還很容易做到,這取決於您暫停程式的標準是什麼?(譯者注:條件斷點)。在剛才那個斷點(紅點)上右擊滑鼠並從彈出選單上選擇” Breakpoint Properties”(斷點屬性)。此處可以設定兩種屬性"Condition"(條件)和"Pass Count"(透過次數)。Condition(條件)屬性太方便了。您可以利用if()語句輸入幾乎是任意的條件。但請牢記條件中的所有變數,對此斷點都應是可見的。條件屬性並未被編譯器編譯到執行程式中,而是在執行時,當程式執行至斷點暫停後,檢查斷點的條件是否滿足。條件為真,停下,否則讓程式繼續執行。另一個屬性"Pass Count"(透過次數)也很容易理解。斷點將被透過Pass Count次後停下。結合使用這兩個屬性,在除錯您的程式碼時,您可以設定非常嚴格的斷點。

還有一件要牢記的是,當您在偵錯程式中發生異常時,會以產生異常處的那一行程式碼上的斷點的形式出現。這種情況很容易製造。一旦您得到一個異常後應做的步驟我會在以後展示如何在堆疊中回溯並跟蹤找出異常發生的真正原因(如引起異常產生的那一小片程式碼)。

另一個要牢記的提示是當您執行您的程式時,程式碼視窗左側有藍點的任意一行都可以設成斷點。所有的斷點將會變為紅點中帶一個黃色的小叉,這一行程式碼也會變成黃褐色。合法的斷點則變為紅點中帶一個綠色的小鉤。執行時,您可以設定/修改任意一點,斷點立即生效而無須重新編譯。

察看儲存在變數中的值

一旦您的程式在您的斷點處停下後,該做什麼?有一件事您想做而且必須做的,那就是察看儲存在您程式中的各種變數真實的值。這部分內容涉及的方面很多,您一定要堅持,忍受這些枯燥的東西。幸運的是當您看完這些,您一定會對偵錯程式這部分最強大的功能有些新的理解。有許多種方法可以察看變數的值,主要要根據您的目的來決定。我會從察看當前函式的Local Variables(區域性變數)開始把他們都講完。

察看區域性變數沒有太多可以講的。只需點選”View|Debug |Local Variables”,或按下ctrl-alt-L將會彈出一個視窗,顯示了當前函式的區域性變數。視窗中的變數將會隨您單步向下執行或回溯的函式體的而更新。

使用Watches(觀察)

下一步您可以透過設定一個variable watch(變數觀察)來察看程式中的變數。就象它的名稱所表達的,觀察一個變數並將其值顯示在變數觀察視窗中(點選"View|Debug Windows|Watches"或按下 ctrl-alt-W)。您可以透過兩個途徑來新增一個觀察,第一種是在程式碼視窗中高亮選擇您要觀察的變數或(是的!它可以理解並對絕大多數簡單表示式求值,比如(i*j)+05 或者 SomeVector[i].Name)並右擊滑鼠,選擇"Debug|Add Watch at Cursor"或按下ctrl-f5,就會加入觀察視窗。如果必要,同時會開啟觀察視窗

您還可以透過在觀察視窗的空白處雙擊來新增。這時會彈出新增watch對話方塊,"Expression"(表示式)域的意思無須多說,但另幾個域我想解釋一下,它們也同樣方便。

"Repeat count"(重複值)用於您觀察一個已知長度的陣列變數(比如一個blah[50]陣列)。您要將Expression(表示式)設為陣列的名字(本例中是blah)。"Repeat count"設為陣列的元素數量(本例中是50)。然後就會顯示陣列的每個元素(如:blah[0], blah[1], blah[2]…)。

"Digits"(小數位數)用來設定顯示十進位制浮點數的小數位數的。下面的點選集合是用來強制設定變數的顯示型別的(將無符號長整數顯示為十六進位制格式)。還有一點要特別說明的是,如果您在watch視窗中用滑鼠右擊一個watch後的彈出選單上會出現"Break When Changed"的選項,這將在變數上設定一個斷點,在此變數發生變化時會暫停程式。

使用Inspectors(巡視器)

巡視變數是察看變數中的資料的第三種辦法。也幾乎是觀察完整的類的資料的最佳方法。可以有兩種方法來巡視一個變數。第一種是在local variable window(區域性變數視窗)中,雙擊一個變數,將會彈出"Debug Inspector"(除錯巡視器)視窗,裡面顯示了這個變數所有的"Data" (variables) (資料(變數))、"Methods" (functions)(方法(函式))和"Properties"(屬性)。如果這是個簡單資料,將會顯示此變數的名稱及其中的值。(譯者注:如果是陣列呢?真不錯!)

您會注意到,Debug Inspector(除錯巡視器)很象property editor(屬性編輯器)。當然如此,更加重要的是,事實上您可以在執行時實時改變這些值!!!小心使用啦!改入壞值的結果會讓您有說不出來的悲痛。巡視器的這個能力可用來測試(假設的)遊戲關卡(譯者注:好像FPE,GM),而不用有編譯-執行-修改-編譯-執行的迴圈。

(舉例巡視Form1)在properties(屬性)頁上,您將會看到某些屬性實際上並沒有顯示其的值,而是顯示了{read=,write=}。如果這些值可以被賦值的話,當您在此區域單擊後,您會注意到一個"?"按鈕出現在屬性的右側。單擊這個按鈕將會執行適當的函式來嘗試取回屬性的值。我們可以在這兒舉個例子-就舉Form1的MDIChildCount的屬性吧。在MDIChildCoun的屬性值區域上單擊,在按下"?"按鈕,哇,0(正是非-MDI的程式的指定值)。除錯巡視器強大的能力並未到此為止。在巡視器的成員變數的適當區域雙擊可以開啟成員變數的巡視視窗,提供與您開始開啟視窗一樣的能力。

巡視器視窗的另一個有用的功能是從繼承的能力。這可以在透過在適當區域上右擊選擇"Descend"(繼承)來做到。繼承的結果是產生了一個新的變數。您會注意到頂部的下拉List box中的變數名稱已經換成新的變數名了。您可以直接在ListBox中切換巡視的變數。這使得在物件的不同部分快速切換變得非常簡單,而不會讓大大小小的巡視器視窗擾亂您的工作空間。

有一點要牢記的是,如果您離開函式,或者離開變數的作用範圍,除錯巡視器會失去對變數的跟蹤。若您需要再次察看的話,請重新設定巡視器。但是您在當前函式的程式碼中單步執行的話,巡視器會自動重新整理。

 

版權說明

國內的網站上,有許多關於C++Builder的內容,但多以、元件為主。論壇裡也大都不能令人滿意,很空虛的感覺。書籍又都昂貴,內容卻有搶錢之嫌。對銀子不足的初學者、自學者關愛不夠,因而想盡自己的綿薄之力。

文中的所有資料都是從國外網站上收集而來。因為E文不方便,所以翻成中文。因為English和都不是非常好,文中的錯誤在所難免。若大家覺得有用的話,我計劃不斷蒐集翻譯一些有用的東西。

有任何意見和建議請to:cker@sina.com

您可以隨意複製、分發、此文件。但未經本人同意,您不可以擷取、改動本文片斷,或用本文謀取任何形式的利益。

 

史平洋

2001.2


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988571/,如需轉載,請註明出處,否則將追究法律責任。

相關文章