990 likes | 1.11k Views
第十三章. 程式除錯與測試. 本章學習目標:. 介紹 Visual C++ 2005 的偵錯環境 在偵錯之前,針對專案屬性進行偵錯資訊格式設定 如何進行程式碼的偵錯,如何設定中斷點 介紹些常見的錯誤,學習如何避免這些錯誤的發生 利用 _DEBUG 巨集,配合 assert() 函式來避免程式中錯誤的發生 利用 try/catch 敘述來攔截錯誤,如何在函數、類別進行例外處理. 程式為何要進行偵錯?當然是希望應用程式能輸出正確結果!即使是一個簡易的應用程式也必須經過測試和除錯。所以在本章節中,我們為各位詳細介紹 Visual C++ 在程式除錯與測試方面的相關技巧與資訊.
E N D
第十三章 程式除錯與測試
本章學習目標: • 介紹Visual C++ 2005的偵錯環境 • 在偵錯之前,針對專案屬性進行偵錯資訊格式設定 • 如何進行程式碼的偵錯,如何設定中斷點 • 介紹些常見的錯誤,學習如何避免這些錯誤的發生 • 利用_DEBUG巨集,配合assert()函式來避免程式中錯誤的發生 • 利用try/catch敘述來攔截錯誤,如何在函數、類別進行例外處理 程式為何要進行偵錯?當然是希望應用程式能輸出正確結果!即使是一個簡易的應用程式也必須經過測試和除錯。所以在本章節中,我們為各位詳細介紹Visual C++在程式除錯與測試方面的相關技巧與資訊
13-1-1 建立專案類型 13-1-2 設定偵錯環境 13-1-3 進入偵錯環境 13-1 Visual C++ 2005偵錯環境 13-1-4 啟動偵錯程序 13-1-5 中斷點的使用 13-2-1 前端處理指引_DEBUG 13-2 預防指標的錯誤 13-2-2 判斷提示assert 13-3-1 語法錯誤 13-3 其他常見的錯誤 13-3-2 物件初始化的錯誤 13-3-3 動態記憶體的錯誤 13-4-1 簡易的例外處理 13-4 例外狀況的處理 13-4-2 在函數中處理錯誤 13-4-3 類別的例外處理 章節目錄
13-1-1 建立專案類型 13-1-2 設定偵錯環境 13-1-3 進入偵錯環境 13-1-4 啟動偵錯程序 13-1-5 中斷點的使用 13-2-1 前端處理指引_DEBUG 13-2 預防指標的錯誤 13-2-2 判斷提示assert 13-3-1 語法錯誤 13-3 其他常見的錯誤 13-3-2 物件初始化的錯誤 13-3-3 動態記憶體的錯誤 13-4-1 簡易的例外處理 13-4 例外狀況的處理 13-4-2 在函數中處理錯誤 13-4-3 類別的例外處理 章節目錄 13-1 Visual C++ 2005偵錯環境
13-1 Visual C++ 2005 偵錯環境 • 撰寫程式有可能會產生錯誤,我們稱為「Bug」(臭蟲)。 • 對於一個程式設計的初學者而言,有時臭蟲會如影隨形隱藏在程式裡,與程式設計者糾纏不清。 • 程式中一個小小錯誤,可能只會讓程式中止執行,如果是嚴重錯誤,可能會造成資料流失,甚至讓電腦嚴重當機。 • 找出錯誤的過程稱為「除錯」(debugging)。 • 任何一個應用程式都必須重複進行除錯與測試動作,最好的法則就是在程式當中加入除錯程式碼進行自動偵測,否則隱藏的錯誤愈久,花費處理的時間就愈長! • 程式中會隱藏多少錯誤?它並沒有精確的統計數值,但是有一個平均值可做為參考:每千行的程式碼可能會產生10~15個錯誤。
Visual C++ 2005 的偵錯環境 • Visual C++ 2005提供了完整地偵錯環境。 • 透過建立專案的方式,編譯過的程式可選擇直接執行,或是按下「F5」按鍵進入偵錯環境。 • 大致上來說Visual C++ 2005的偵錯環境提供兩種偵錯模式 • 第一種是「逐步執行」,在偵錯模式下將程式碼一個個進行解析 • 另一種方式則是設定「中斷點」,透過條件的設定和呼叫次數來決定程式的中斷 • 當然我們也可以將上述二種方式合併使用。
13-1-1 建立專案類型 章節目錄 13-1 Visual C++ 2005偵錯環境
步驟2 步驟1 請加入下列範例【SetConfig.cpp】的程式碼,如下所示。 首先建立一個空白專案,選取「檔案」→「新增」→「專案」,將專案命名為【SetConfig.vcproj】 13-1-1 建立專案類型 • Visual C++ 2005的偵錯環境主要是針對專案類別配合專案中的檔案來提供偵錯程序,因此在進行偵錯之前,必須針對專案屬性進行設定。
執行程式:SetConfig.cpp • 我們已經知道key的答案為「3456」,可是執行時輸入3456卻不會顯示「數字正確」的訊息,因為編譯時並沒有產生錯誤,因此我們得找出程式碼到底那裡出錯了!
13-1-1 建立專案類型 13-1-2 設定偵錯環境 章節目錄 13-1 Visual C++ 2005偵錯環境
1 選取「ErrConfig」專案 執行「檢視/屬性 頁」指令 2 13-1-2 設定偵錯環境 步驟3:首先進入屬性頁設定畫面,執行動作如下
直接在「ErrConfig」專案名稱按下滑鼠右鍵,執行「屬性」指令 進入屬性頁的其他方式. • 除了透過指令來進入屬性頁之外,也可以直接在專案名稱處按下滑鼠右鍵,點選快捷功能表的指令來進入「屬性頁」畫面。
1 選取「ErrConfig」專案 2 執行「專案/屬性」指令 進入屬性頁的其他方式.. • 此外,執行「專案/ErrConfig屬性」指令也能進入屬性頁畫面。
1 展開「C/ C++」項目 選取「一般」項目 2 3 偵錯資訊格式顯示「C7」相同 4 按「確定」鈕 步驟4:設定偵錯資訊格式 進入屬性頁之後,第一個要設定的是偵錯資訊格式,展開「組態屬性」,進行下列設定:
Z7會產生目的檔(obj),提供偵錯工具使用的完整偵錯資訊符號。這些包含了變數名稱與型別,函式和行號。但是不會產生*.pdb 檔。 /Z7 除了提供完整的偵錯資訊符號之外,還包含偵錯工具使用的程式資料庫(PDB),產生偵錯資料時會將型別資訊存放於PDB中 /Zi ZI支援「編輯後繼續」功能,使用此項偵錯格式時,程式碼中不能含有「#pragma optimize」前置處理指引。一般來說,ZI僅支援x86編譯器,不支援64位元的使用。 /ZI Visual C++ 2005偵錯資訊格式 • Visual C++ 2005將偵錯資訊格式分為三種:/Z7、程式資料庫(/Zi)或編輯後繼續的程式資料庫(/ZI),詳細說明如下:
2 最佳化「停用(/Od)」 1 點選「最佳化」項目 按「確定」鈕 3 步驟5:關閉最佳化
偵錯與最佳化 • 編譯器為了讓程式碼執行效能更佳,它會重新組織指令並進行位置調整,如此才能產生較有效率的程式碼。 • 這種重新安排的動作,偵錯工具不一定能辨識並且把一組組的指令對應到原始程式碼。 • 一般來說,程式碼的最佳化會影響區域變數的位置和函式內部的位置。 • 因此在偵錯過程必須簡化程式碼處理程序,抑制程式碼的移動,所以從清單中選擇「停用(/Od」項目,讓程式碼在偵錯過程中不進行最佳化處理。
3 產生偵錯資訊「是」 1 展開「連結器」項目 2 點選「偵錯」項目 4 按「確定」鈕 連結器的設定 設定連結器的偵錯資訊,進行偵錯時,必須將「產生偵錯資訊」設為『是/DEBUG』,其設定步驟如下:
偵錯資訊的DEBUG選項 • DEBUG選項會讓程式碼進行偵錯時能夠建立「*.exe檔」或DLL的偵錯資訊。這樣的過程會將偵錯資訊置入程式資料庫(PDB)中。當偵錯資訊有更新時,PDB也會更新
13-1-1 建立專案類型 13-1-2 設定偵錯環境 13-1-3 進入偵錯環境 章節目錄 13-1 Visual C++ 2005偵錯環境
13-1-3 進入偵錯環境 • 完成屬性頁的設定之後,還得認識在偵錯環境中有那些好用的工具可以協助我們進行偵錯處理! • 首先要認識就是 • 偵錯工具列 • 「偵錯」功能表中涵蓋的指令 • 二者並無太大差異性,我們可以依據自己的操作需求來選擇
呼叫偵錯工具列 • 在進入偵錯之前,必須先找出「偵錯」工具列,透過此工具列來進行偵錯程序 執行「檢視/工具列」指令,勾選『偵錯』工具列
值 型別 名稱 用來存放變數名稱或運算式 用來顯示變數或運算式的值和資料型別 認識變數視窗 • 在偵錯模式下,我們能透過「變數視窗」來顯示、評估以及編輯變數和運算式 • 它包含 • 區域變數、自動變數和監看式等 • 每個變數視窗會提供三個欄位 • 「名稱」;「值」和「型別」
區域變數 顯示目前內容或範圍的區域變數和儲存資料的位址 自動變數 顯示目前和之前程式碼行中所使用的變數和函式傳回值 監看式 可以依據偵錯需求加入想要監看變數,在執行過程中觀察變數的變化 變數視窗提供的功能
偵錯中「逐步執行」和「不進入函式」 • 如果程式碼在編譯過程中並未產生錯誤,執行時卻發生嚴重錯誤,直接啟動偵錯模式,會比較容易找錯誤所在! • 如果程式碼編譯沒有問題,也能順利執行,卻發現結果並不符合原意,就必須花費功夫利用「逐步執行」一步步找出錯誤所在。 • 就像範例「SetConfig.cpp」已預設猜中的數字,卻發現執行時輸入正確數字也不會顯示「數字正確」的訊息。這種邏輯判斷的錯誤,有時無法立即找出錯誤所在,必須透過偵錯模式,逐步找出偵錯。
逐步執行 進入偵錯模式後,會由main()函式開始,一次只執行一個敘述。它會呼叫程式中所有函式(有些函式可能是組合語言產生的機械指令),因此除錯過程會變得繁鎖而緩慢 不進入函式 它只會簡易執行main()函式中毎個敘述,不會呼叫其他函數 「逐步執行」和「不進入函式」 • 進入偵錯模式的「逐步執行」(F11按鍵)和「不進入函式」(F10按鍵)有何不同?解說如下: • 「逐步執行」和「不進入函式」不同處在於函式的呼叫。如果下一行程式碼中有函式呼叫,「逐步執行」會去呼叫相關的函數
以範例說明偵錯程序. • 我們藉由範例「SetConfig.cpp」以【逐步執行】的偵錯程序來說明如下: • 下圖13-1是表示逐步執行到第8行程式碼,敘述中有cout物件,按下F11按鍵跳到第9行時,偵錯工具會去呼叫ostream函式
以範例說明偵錯程序.. • 圖13-2顯示了呼叫結果,程式碼編輯視窗那了原有的SetConfig.cpp程式碼,還多了ostream函式
以範例說明偵錯程序… • 「不進入函式」會以整個函式來執行,所以會以一行一行的程式敘述為主。例如下圖13-3,「不進入函式」進行到第8行程式碼後,按下F10按鍵會跳到第10行的敘述,不會進行函式呼叫。
逐步執行 想要查看函式呼叫的內部,可採用此方式去觀看函數內部 不進入函式 偵錯過程中 倘若不想進入函式內部,透過「不進入函式」方式也能進行偵錯程 跳離函式 要離開此函數內部就可利用此方式來回到程式碼的下一行敘述 偵錯過程中
13-1-1 建立專案類型 13-1-2 設定偵錯環境 13-1-3 進入偵錯環境 13-1-4 啟動偵錯程序 章節目錄 13-1 Visual C++ 2005偵錯環境
13-1-4 啟動偵錯程序 • 我們採用「不進入函式」方式,直接按下「F10」按鍵進入偵錯。 • 進入偵錯模式後,通常會在視窗底部開啟兩個視窗: • 左側是區域變數視窗 • 右側是中斷點視窗 • 程式碼編輯區左側會有一個黃色箭頭指向「main()」,表示偵錯程序由此開始,並且會去呼叫「命令字元提示視窗」!
1 按下「中斷點」旁三角鈕 2 點選「監看式」 3 點選「區域變數」 進入偵錯時會有一個黃色箭頭來逐行移動 找出區域變數和監看式視窗 • 為了要觀看變數的變化,先看看是否有區域變數和監看式兩個視窗!如果沒有,先透過下列步驟來找出它們:
觀看區域變數視窗. • 持續按下【F10】按鈕,讓黃色箭頭移向第8行,可以觀看左下角區域變數視窗的變化: • 由於已經讀入兩個變數inkey和key,因此在「名稱」欄位會顯示這兩個變數的名稱,而「值」欄位會顯示記憶體所配置的位址。 • 由於inkey尚未輸入任何字元,所以會看到一些奇怪文字,而key已經設定了起始值,所以除了顯示位址之外,表示它已經儲存了字元「3456」。「型別」欄位則顯示此變數的資料型別是一個字元型別的陣列。
觀看區域變數視窗.. • 此處區域變數是以堆疊方式讀入,利用先進後出方式來處理,因此第一個讀入的key變數會在最底層,後面陸續讀進來的變數就會往上堆放,好像堆盤子一樣。 • 繼續按下F10按鍵,會進入for迴圈,此時區域變數加入一個變數「i」用來計算for迴圈執行次數。 • 利用F10按鍵移到第12行時黃色箭頭會暫時不見,會切換為「命令字元提示視窗」:輸入數字「3456」,按下Enter鍵做確認動作後,畫面會自動切回程式碼編輯視窗繼續偵錯。
觀看區域變數視窗… • 黃色箭頭會指向第13行敘述,繼續按下F10,讓黃色箭頭移向第14行的敘述,此時觀看區域變數: • 在區域變數視窗中,發現變數key和inkey的內容相等,計數器i的值為「1」,ans值是來自於第13行key和inkey的比對結果(為0表示相符)。 • 如果繼續按下F10按鍵,應該會顯示第15行敘述來結束程式。實際上卻不是這樣,黃色箭頭會由第14行敘述跳到第19行,往下第20行,前往第21行,再跳到第24行,然後重新進入for迴圈,這說明第14行if的條件判斷產生問題!
滑鼠移向此處可明顯看到ans的條件值為false 使用監看式. • 我們可以把滑鼠指標移向第14行的if條件判斷來觀看ans的值:
1 選取「ans= =5」敘述 接下右鍵選取「加入監看式」 2 使用監看式.. • 另外,也可以透過監看式來觀看ans的條件值是否產生問題:
使用監看式… • 由監看式來看,表示程式碼中第19行的if條件判斷產生問題,所以「ans==5」的值是「false」(表示條件不成立)。 • 而區域變數視窗也已經告訴我們「ans == 0」是比對正確所得的答案,因此將程式碼部份修改如下:
13-1-1 建立專案類型 13-1-2 設定偵錯環境 13-1-3 進入偵錯環境 13-1-4 啟動偵錯程序 13-1-5 中斷點的使用 章節目錄 13-1 Visual C++ 2005偵錯環境
13-1-5 中斷點的使用 • 中斷點會讓應用程式在某一點中斷來暫停執行,檢視設定的條件找出程式的錯誤。 • 相對於利用除錯器進入偵錯模式來找出程式錯誤的耗時費工,設定中斷點能讓我們不用逐步地查看每一行的程式敘述! • 範例「SetBreak.vcrpoj」是for迴圈中,設定的條件值已經已經超出短整數的範圍,所以執行時會因為溢流現象而產生無窮盡迴圈,透過中斷點的設定來進行程式碼的修正!
範例:SetBreak.vcproj • 第5行的short資料型別的範圍為「-32,768 ~ + 32,767」,故意在第6行for迴圈讓count的值形成溢流情形,產生無窮盡迴圈。
游標插入點移向第7行,並按下F9按鍵來加入中斷點游標插入點移向第7行,並按下F9按鍵來加入中斷點 設定中斷點. • 設定中斷點非常簡單,只要在程式碼編輯視窗中,將插入點移向欲中斷的某一行程式碼,按下F9按鍵就能形成中斷點。因為程式是在第7行輸出count值產生問題,所以在程式碼第7行加入中斷點:
設定中斷點.. • 加入的中斷點會在程式碼的行號左側產生一個紅色圓點。在偵錯過程中,也可以依據需求來設定多個中斷點。 • 如果要取消中斷點,只要把游標插入點移向設定中斷點的那一行程式碼,再按下【F9】按鍵就能取消中斷點。
按下工具列「中斷點」 顯示「中斷點」視窗 設定中斷條件 • 程式原本是一個無窮盡迴圈,加入中斷點只是中斷程式的執行!程式在偵錯過程中能依據條件值來產生中斷,當count值小於「0」時就會產生中斷效果,請繼續執行下列條件值的設定: 步驟1
在「中斷點」視窗按下滑鼠右鍵,執行「條件」指令在「中斷點」視窗按下滑鼠右鍵,執行「條件」指令 步驟2:設定「中斷」條件