調試是糾正或修改代碼,使之能夠順利地編譯、運行的過程。爲此,VC IDE提供了功能強大的調試和跟蹤工具。php
開發環境老是爲你的工程建立調試版和發行版。在調試版裏,咱們排查各類可能的程序錯誤,而後製做成發行版以得到較好的信息。這就是調試版與發行版的區別:前者包含了較多調試信息,最終執行文件較大,性能較差;後者最終執行文件較小,性能更好。具體地講,發行版和調試版區別有:express
1. 調試版下,可使用診斷和跟蹤宏,如ASSERT和TRACE,MFC類的DUMP功能也定義在調試版下。數組
2. 額外的變量初始化。編譯器會自動將你未初始化的變量逐每字節賦值爲0xCC。sass
3. 內存分配監視。在調試版下,在堆上分配的內存都會記錄,作額外的初始化(0xCD),在釋放時,會將其內容逐字節置0xFD。併發
怎樣配置調試版[C1] 或發行版?ide
調試版和發行版均可以從 Build | Configurations中增長,或者當這兩個配置已存在的狀況下,從Project | Settings命令下進行配置。二者在配置上主要有如下不一樣:函數
1. 兩個版本應該配置成不一樣的輸出文件夾。這在Project | Settings | General中的兩個編輯框中設定。工具
2. 調試版下,程序應該連接Debug版的c運行時庫、禁止代碼優化,並在C++屬性頁中的Preprocessor類別中加上預約義標識符_DEBUG。在Link屬性頁中的Debug類別中,選中DebugInfo和Microsoft Format選框。post
4. 發行版下,程序應該連接Release版的c運行時庫、設置代碼優化,不定義_DEBUG標識符,去掉DebugInfo選框中的選擇。性能
VC6.0的編譯器能夠報告大約1100個左右的錯誤,這還不包含警告。而在編譯和調試過程當中咱們還不只僅遇到編譯錯誤,也可能遇到連接錯誤和其餘錯誤,所以咱們只能講一些很常見、很重要的編譯錯誤。對咱們沒有列出來的編譯錯誤,解決它的最好辦法是,在output窗口中將光標定位在輸出行,而後按F1鍵以得到MSDN的幫助。若是MSDN的版本與你的VC不兼容,那麼你能夠用得到到Error xxxx做爲關鍵字在MSDN中搜索。目前這些錯誤的說明幫助出如今Visual C++ Programmer's Guide中的Build Errors條目下。
並不是錯誤
形如「Loaded 'C:\WINNT\system32\gdi32.dll', no matching symbolic information found.」這樣的報告不是錯誤。通常狀況下,咱們只須要跟蹤本身的源代碼,檢查在那裏發生了什麼錯誤。但有時候咱們也想看看,若是一個錯誤報告在系統DLL裏時到底是怎麼回事。Microsoft不肯意在系統DLL中包含調試信息,所以它提供調試符號幫助你跟蹤。隨VC6.0發行的有一個調試符號包,但其內容可能不能跟得上你操做系統版本。若是你須要適合你操做系統版本的調試符號,請到微軟更新網站上下載。
l LNK2001錯誤
LNK2001錯誤報告一個不能識別的外部標識符。一般你使用一個函數或外部變量,都必須事先給出其聲明,不然就會產生這樣一個錯誤。能夠從如下幾個方面尋找緣由:
1. 使用內聯函數,但該內聯函數定義在.cpp文件中,而不是在.h文件中。
2. 聲明瞭函數或外部變量,但找不到它們的定義。
3. 程序入口設置錯誤,而且進一步提示_WinMain@16': Unresolved External Symbol或相似錯誤。在Windows程序中,程序並非最早從WinMain()或Main()這樣的函數處開始執行,而是從WinMainCRTStartup()或MainCRTStratup函數處開始執行。這四個函數(其實是8個,每一個函數都有UNICODE版和單字節版)必須配套。若是進入點是WinMainCRTStartup (),則它期待在你的程序中找到一個WinMain()函數,若是找不到,則發生上述錯誤。
4. 在程序中使用了線程啓動函數_beginthread,但選擇的連接庫爲LIBC.LIB。
其餘錯誤見MSDN。
編譯錯誤都是靜態的,更多的錯誤發生在運行時。爲了排除運行時的錯誤,首先須要的是可以使正在運行的代碼停下來。VC6.0提供了多種斷點設置。
l 位置斷點
l 條件斷點:能夠設置如下條件爲真時中斷程序
1. 單變量值改變時
2. 單變量值判斷條件中斷,如K>0,i == 5等
3. 數組單元素或多元素值改變或值判斷條件中斷。
4. 指針:指針變量改變或指針所指內容改變
5. 監視固定內存值變化
6. 監視寄存器中值的變化,如 cs == 0等。
也能夠直接輸入斷點設置表達式,其語法是:
①Advanced Syntax 詳解
{[function],[source],[exe] } location
{[function],[source],[exe] } variable_name
{[function],[source],[exe] } expression
括在{}對中的是上下文設置,這三項中任意一項均可省略,但省略前面的項時,應注意保留逗號。以下面的設置
是錯誤的:
{File.c, File.exe} .143 - Bad
正確的設置如
{Fun} .143 這樣在函數Fun的第143行設置了一個斷點。
更詳細的說明見下面的舉例:
{[function],[source],[exe] } .100 - A line number (this may not work with some languages)
{[function],[source],[exe] } @100 - A line number (this works for all languages)
{[function],[source],[exe] } Traverse - A function name
{[function],[source],[exe] } CMyWindow::OnCall - A function name
{[function],[source],[exe] } 00406030 - A memory address (decimal)
{[function],[source],[exe] } 0×1002A - A memory address (hexadecimal)
{function,[source],[exe] } Label - A statement label. The context must include function since
Label is visible in the function’s scope.
更詳細的幫助,查看Visual C++ Programer’s Guid中Setting Breakpoints When Values Change or Become True主題。
l 消息斷點
l 異常斷點
當程序發生異常時,咱們應該當即停下來,看看發生了什麼事情。默認地,當一個異常發生時,Debugger 將異常信息寫到Output 窗口中,並根據你的選擇決定是否當即中斷程序。這個設置在Debug菜單中的Exceptions選項中提供。這裏提供了兩個選項Stop Always和Stop If Not Handled。當Stop If Not Handled被選中時,debugger僅僅往Output窗口中輸出一條信息,而並不停止程序,除非沒有這個異常最後也得不處處理。在有些狀況下,這會是一個有些晚的時機。當Stop Always被選中時,debugger會在異常處理代碼接管之前就中斷程序。
開發環境提供了一系列的查看方式來臨視甚至修改程序的運行狀態。總共提供了六種查看窗口:
l Watch窗口
Watch窗口用來查看和解碼中間變量,並容許你修改變量的值。Watch窗口共有四個Tab。每一個Tab都是一個ListView,由兩欄組成。左邊一欄爲變量名,右邊一欄則爲變量取值。只要在變量名欄輸入變量名並回車,就能夠獲得當前變量的值。下面是一些特殊的用法:
1. 列出數組中前n個元素的值:arr,n
2. 查看UNICODE編碼的字符串值:str,su
3. 已知消息值,查對應的宏:messageValue,wm
4. 查看窗口類標誌(Window class flag):flagValue,wc
5. 查看當前線程最後錯誤報告:@err,hr
6. 查看COM庫最後錯誤報告:@hres,hr
7. 查看當前寄存器值,把寄存器名看成變量名輸入就能夠了。
直接在右邊欄輸入新的取值,就會修改該變量的取值併發生做用。
l Variables窗口
這個窗口始終跟蹤當前上下文中重要的變量取值。但不能增長對變量的跟蹤。它包含三個Tab:
Auto窗口跟蹤當前聲明或前一個聲明中使用的變量,當你從一個函數調用中跳出時,還能夠顯示函數的返回值。
Locals窗口顯示對當前函數來講是局部的變量的取值。
This窗口顯示this指針所指對象取值。
此外,在Variables窗口的上部,還有一個Context的組合框,能夠用來切換當前查看的上下文。
l CallStack窗口
這個窗口顯示了函數調用的狀況。當你的程序出現一個斷言時,你能夠用這個窗口把函數調用順序調出來,而且能夠知道調用時傳遞參數的狀況。雙擊窗口中列出的函數,能夠定位到源代碼中。
l Memory窗口
Memory窗口的上部是一個地址輸入框。你能夠在這裏輸入0×00400000,由於這時進程的起始點,因此絕不奇怪,你會看到MZ這兩個字。Memory窗口支持定製顯示格式,並支持表達式。
在Memory窗口的下面窗口處按右鍵,會出現一個菜單,容許你按單字節、雙字節十六進制或四字節十六進制來顯示內存的內容。
若是你願意麻煩一點,還有辦法獲得更準確的顯示。點Tools | Options,選擇Debug屬性頁,在這裏能夠決定以什麼形式顯示內存內容以及從什麼地址開始顯示。這裏提供了14種格式選項。此外,你還能夠輸入一個地址表達式。好比,若是要知道當前棧頂的狀況,在這裏輸入ESP。
l Disassembly窗口
這個窗口能夠用跟蹤優化代碼,或者是複雜表達式。
l Registers窗口
Registers窗口顯示全部寄存器值,並容許你修改。它與Watch窗口的區別是,你不須要記住寄存器名,它老是把全部寄存器值給你列出來。
VC提供了兩個斷言宏:ASSERT和VERIFY。二者的區別是,在發行版中,ASSERT行不會執行,由於在發行版中,ASSERT宏被定義成
#define ASSERT /##/
這樣這行代碼被註釋掉,從而不會被編譯。VERIFY宏在發行版中被定義成
#define VERIFY
這樣VERIFY後面的代碼仍然加入編譯,從而獲得執行。
在你掌握了這兩個斷言的區別後,咱們將只講述ASSERT宏。ASSERT是一個帶參宏,其參數容許是一個表達式。當表達式的值爲假時,程序發生中斷並提示你是否進行調試。
除了使用條件判斷表達式外,ASSERT(0)也頗有用。它始終引發一個斷言錯,並提示你是否調試。你能夠把它放在一些你不指望程序運行到達的路徑上,若是事情一旦發生,則能夠直接進行調試。
VC還提供TRACE宏,以及它的姊妹宏TRACE0,TRACE1,TRACE2,TRACE3。TRACE宏的語法相似於printf,包括格式字符串的規定也是一致的。後面三個宏限定你使用一個格式字符串和一至三個變量。TRACE輸出到Output窗口,若是工具MFC TRACER中設置爲容許的話。
MFC還提供了對象診斷工具,即名爲Dump的函數。MFC庫中每一個從CObject繼承的類都有Dump的能力。若是你從MFC類庫中派生了某個類,在有必要的狀況下,最好重載這個函數。要記住,你重載的函數應該調用基類的Dump函數。另外,Dump函數只在調試版下可用。所以重載的函數也應該括在#ifdef _DEBUG宏中。