細談VC程序調試的若干方法

記得我剛開始用VC編程時,多次遇到程序編譯鏈接都沒有任何錯 誤提示時運行卻發生意外錯誤或者陷入死機狀態,對此我真是茫然不知所措。後來經過項目工程的“洗禮”,我學到了一些解決這類錯誤的方法同時也將自己積累的 經驗運用到開發工作中,從而避免了重蹈覆轍,編程效率得以明顯提高。如果你用VC而不精通解決上述種種莫名其妙故障的調試技術,那麼軟件的進度和質量將無 法保障。VC調試之重要不言而喻。

  其實VC所依賴的開發平臺Microsoft Developer Studio本身提供的調試功能並不弱,每當我們創建一個新的VC工程項目時,默認狀態就是Debug(調試)版本,在"組建"(Build)菜單下的 Configurations中可以看到除了調試版本還可以設置成發行(Release)版本。調試版本由於包含了大量信息,所以它生成的可執行程序容量 會遠遠大於發行版。

  具體地,調試版本主要增加了兩個內容:其一,會執行編譯命令_D_DEBUG,使頭文件的調試語句ifdef及其代 碼附加到程序中;其二,在可執行文件中加入的調試信息使開發人員能夠觀察變量,進行單步執行等。在VC"組建"(Build)菜單下的"開始調試"中有4 條專用的調試命令:Go,Step into,Run to Cursor,Attach to process…。在運行程序源代碼時用Go命令(而不是Execute)才能處於調試狀態, Go命令會使程序運行變得緩慢下來,但可以更好地控制運行程序,我們可以在任何時刻中斷程序、單步執行、查看變量、檢查調用棧。

  有必要 詳細介紹一下VC的調試功能:首先,再次強調要用Go命令運行一個將要調試的程序;如果要中止調試狀態下的運行程序可以點擊Stop Debugging命令,還可以通過Break選項以可恢復方式中斷調試程序的運行流程(用Restart選項可以重新開始運行程序);Step Into選項表示每次只執行一行語句(單步執行),但如果當前代碼是調用一個函數,那麼Step Into表示進入該函數,全部函數語句執行完後返回,而Step Over則是跳出這個函數;Step To Cursor選項表示程序將執行到光標所在的可執行語句行上;在調試多線程程序時,可以在線程函數或主應用程序線程中設置斷點,還可以用Break選項結 束線程後用Threads選項查看運行線程列表,也可以選擇懸掛和恢復每個線程;在設置斷點後,在VC "查看"菜單的"調試窗口"中可以查看變量、內存、調用堆棧、寄存器以及反彙編語句。在程序中設置斷點的方法是,點擊要設置的代碼行並點擊設置代碼的工具 欄按鈕,會出現在代碼行最左邊的一個小黑點即是斷點標誌,這時再選Go程序會在執行到端點處停下來,如果要繼續執行可以再選Go。

  通過 選擇VC"工具"菜單下的"源瀏覽器"可以生成一個.BSC文件,使用瀏覽器可以從中發現多種信息:程序中任何一個變量、函數、類或宏在何處定義及引用; 可以列出所有聲明的函數類、變量、宏;可以發現調用一個指定函數的所有函數;可以找到一個指定類的派生來源或者它派生出哪些類。

在使用微軟 程序開發庫MSDN時,我們會發現其中的VC示例經常採用看似多餘的ASSERT語句,其作用就是使程序具有"維護"性。對於Debug版本的VC程序, 在遇到布爾值爲FALSE的ASSERT語句處停止,並顯示Assertion Failed對話;如果設置爲發佈版,所有ASSERT語句都會被預處理程序刪除。一個地道的VC編程員,應該有意在自己的代碼中通過"維護"特徵去檢測 任何設定,諸如輸入參數、循環範圍和變量值的設定。

  在安裝好VC系統之後,在VC之外的程序組中有一個程序Tracer是一個跟蹤工 具,在激活它後使用Go運行VC代碼,在輸出窗口就能夠看到程序運行過程中的內部過程,包括DLL調用等,你如果看不到任何輸出,可以轉到菜單"查看" (View)點擊"輸出"(Output)。

  其實,MFC自身就提供有錯誤查找和TRACE語句,而TRACE語句的語法與printf非常類似,所以我們可以在程序中直接加入這條跟蹤命令,如下所示:

// Example for TRACE
int i = 1;
char sz[] = "one";
TRACE( "Integer = %d, String = %s/n", i, sz );
// Output: 'Integer = 1, String = one'

  在Developer Studio中還提供了一個ERRLOOK工具,程序員只要輸入錯誤號就能得到系統出錯信息或模塊錯誤內容。

  MFC從Cobject派生的每個類都包含一個Dump函數,該函數可把當前狀態轉儲(Dumping)到輸出窗口,這在某些調試過程中會有用,以下代碼是Dump函數的用法:

// Example for CObject::Dump
void CAge::Dump( CDumpContext &dc ) const
{
 CObject::Dump( dc );
 dc << "Age = " << m_years;
}

   在MFC中還有一個非常有用的類是CMemoryState,我們可以在程序的任何部分使用這個類檢測內存衝突,並得到內存衝突的確切位置。 CMemoryState類有3個成員函數:CheckPoint可將堆的當前狀態存入類的實體;Difference可以比較兩個實體包含的堆之間的差 異;DumpStatistics用於標準化轉儲所有被CheckPoint捕獲後分配到堆的對象,如CheckPoint未被調用實體未被初始化時,該 函數將轉儲當前堆的所有內容。以下代碼表示了CMemoryState類的使用方法:

// Example for CMemoryState::CMemoryState,
// Includes all CMemoryState functions
CMemoryState msOld, msNew, msDif;
msOld.Checkpoint();
CAge* page1 = new CAge( 21 );
CAge* page2 = new CAge( 22 );
msOld.DumpAllObjectsSince();
msNew.Checkpoint();
msDif.Difference( msOld, msNew );
msDif.DumpStatistics();

  代碼運行的結果爲:

Dumping objects ->
{2} a CObject at $190A
{1} a CObject at $18EA
Object dump complete.
0 bytes in 0 Free Blocks
8 bytes in 2 Object Blocks
0 bytes in 0 Non-Object Blocks
Largest number used: 8 bytes
Total allocations: 8 bytes

  在MFC類和VC中本身就有"異常情況"這個概念,並在此基礎上形成它們處理系統錯誤和意外的主要機制。比如當系統內存分配殆盡時,你的運行程序就會收到內存異常的消息。這樣就給了程序員消除異常的機會。

   MFC中的異常情況主要有:CArchiveException表示檔案文件載入或保存時出錯,CDBException屬於數據庫錯誤, CFileException爲文件錯誤,CMemoryException爲調用new時發生分配錯誤,CNotSupportedException 表示指定操作不被支持,COleException表示在調用OLE操作時出錯,COleDispatchException表示在OLE自動操作時出 錯,CResourceException表示資源找不到或無法創建,CUserException用於通知用戶錯誤。

  MFC還包含一 系列以Afx-爲詞頭的調試函數:AfxAbort可以在發生致命錯誤時異常終止程序,AfxCheckMemory可以檢查堆和剩餘緩衝池的受損部分; AfxDoForAllClasses重聲明所有CObject的派生類;AfxDoForAllObject重聲明堆上所有CObject派生的對象; AfxEnableMemoryTracking啓用或禁止內存追蹤;AfxIsMemoryBlock用於確認指針所指內存有效; AfxIsValidAddress用於確認地址是駐留在程序的內存區域內;AfxIsValidString用於確認地址所指字符串有效; AfxSetAllocHook用於內存分配前進行檢測;AfxTraceEnabled啓動或禁止輸出跟蹤,AfxTraceFlags則進一步定製跟 蹤特徵。

在我們隨手編制的VC程序中,普遍存在着會發生內存泄漏的隱患,有些問題程序的痼疾症狀是在處理數據量激增時陷入癱瘓,更糟的要發 現內存泄漏並不容易。首先,我們要明確VC中內存泄漏的含義:簡單說就是一個程序申請得到了一段內存卻沒有及時釋放。比如用new在堆中分配了一個對象或 對象組卻並沒有調用delete操作。靈活的指針技術使內存泄漏的原因變得複雜化,比如改變了保存在一變量中的指針的值後未能刪除指針所指向的內存區;當 內存泄漏是來自一個帶有指針類型成員變量的類時會更加困難,因爲當調用分配指針時並沒有複製構造函數/析構函數或運算符。

  爲了防止發生 內存泄漏這樣棘手的故障,在VC編程時應當注意遵循幾個規範:其一,如果一個類包含有指針並且分配了指針值,那麼就需要構造相應的析構函數以刪除該指針; 其二,如果一個函數分配了一塊內存並把該內存塊返回給調用它的函數使用,那麼它返回的必須是一個指針而非一個引用,因爲引用不能被程序刪除;其三,即使一 個函數分配了一段內存並在同一函數的稍後部分刪除了該內存段,也要儘可能將內存塊分配到堆棧中;最後,就是決不要試圖改變一個指針值,除非已經刪除指針所 指的對象或通過數組指向了該指針所指向的內存,而且也不要對new返回的指針進行加1運算。

  每當編寫VC程序時,我們都會處於一個琳琅 滿目的集成開發環境(IDE)中,現實的真相是我們很多人在這裏編程多年,對開發環境瞭解並不全面和細微。記得王朔的小說中有句話說"穿了多年的外套在不 穿時才發現它原來還有一個兜!"。我們在安裝VC時,得到的IDE即Developer Studio, VC其實是Developer Studio下激活的一個組件而已,比如微軟的VJ++也是基於Developer Studio。很少有技術書籍會一一介紹Developer Studio界面元素,也許聰明的程序員輕易就能識別其含義,全部猜對界面圖符的含義並非易事。可是它們對我們瞭解開發信息很重要,也與調試程序有關聯。

   在Developer Studio下會生成多個文件去保存項目的所有信息:一個是以.DSW爲擴展名的項目工作區文件,它包含項目中所有文件的名稱、文件所在目錄、編譯器和連 接器的選項以及項目工作的其它信息;以.DSP爲擴展名的也是項目記錄文件,.OPT是工作區選項文件,它包含Developer Studio的所有個人設置 - 包括顏色、字體、工具欄、哪個文件被打開以及MDI窗口如何被定位和最新調試中的斷點等。在打開項目工作區文件時其它文件隨即會自動打開。在 Developer Studio下可以按類查看代碼,其中的ClassView顯示了應用程序中所有的類,每個類下顯示了成員函數和數據成員,在成員函數旁有粉紅圖標,數據 成員旁是藍綠色圖標,保護類成員的圖標旁有一枚鑰匙,私有類成員則有一個掛鎖圖標。

  當然,在開發環境下最主要的工作是輸入編輯程序源代 碼,源代碼會顯示"語法着色"。在缺省情況下,代碼爲黑色,夾以綠色的註釋和藍色的關鍵字(指VC所保留的public、private、new和int 等等)。--這些地球人都知道,但是爲了調試需要,我們還可以指定顏色去顯示字符串、數字和運算符。定義方法是通過Tools菜單下的Options對話 框中的Format選項卡設置。

  在Developer Studio提供的諸多菜單項中,我們往往對少數菜單避而不用,因爲不瞭解它們的作用唯恐好奇心會造成亂子;還有多個菜單項都可以達到目的,至於它們之間 的微小差別則不甚了了。比如在編譯和調試時常用到Build菜單組,它具有和應用程序編譯、運行調試相關的多項操作:其中的Compile菜單會編譯當前 的聚焦文件;Build菜單會編譯和鏈接所有在項目中修改的文件;Build All會編譯鏈接項目中所有文件,包括最近編譯後沒有修改過的文件;Batch Build用於包含有Debug和Release配置的項目;Clean會刪除所有的中間和輸出文件,因而項目目錄下僅包含源文件;Debugger Remote Connection用於遠程調試,即在一臺機器運行程序而在另一臺調試;Set Active Configuration可設置某個配置爲激活狀態(Debug或Release);Profiler能夠識別應用程序的瓶頸,即找到降低程序執行速度 的代碼和有關模塊,爲此需要在"工程/設置/Link"下勾選Profiling。

  VC中的警告信息是有級別的。在"工程/設置"下的 "C/C++"選項卡中的警告級別Warning Level缺省值是3,如果改爲更爲嚴格的4級,往往會產生更多的警告類錯誤。在C/C++選項卡中還提供了代碼優化欄Optimizations,在你 完成漫漫調試之旅準備正式發佈前夕,你應當改動此項,它提供了一些適合建立發行版應用程序的優化設置。

  其實,在多年以前,在軟件業 中針對開發具有一定規模軟件項目的情況就出現了軟件工程理論,以此來指導軟件人員樹立團隊協作意識,進而保證軟件項目的協調性及進度質量等。筆者在從事 VC開發中對此很有感觸,而且覺得對於每個開發人員自己也應當具備一定的工程素養。比如,我們在開發初期時,應當對所編寫的有價值的源代碼及時備份,即使 有些代碼在後續階段似乎沒有用處了也不妨“敝帚自珍”。在VC編程中我經常遇到的問題是,一個不算小的編寫了很久的程序,在稍微擴展一點功能時出現了故 障,而最撓頭的時花了很多時間也無法排故,所幸的是對原來的程序我還有備份,可以重新再來--功能還要加,只是還一種編程思路去實現相同的功能。說實在 的,本人在VC開發中,如果要總結“解決了多少問題”,不如說很多時候是採用"游擊戰"巧妙地"繞過"了一些棘手的問題。--尤其在軟件交工處於倒計時的 開發後期出現的故障,往往你已經沒有時間去找到故障,而是用前一個完好的VC Project去嘗試另一條捷徑。

發佈了10 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章