VC调试篇(zz)
設(shè)置
為了調(diào)試一個程序,首先必須使程序中包含調(diào)試信息。一般情況下,一個從AppWizard創(chuàng)建的工程中包含的Debug Configuration自動包含調(diào)試信息,但是是不是Debug版本并不是程序包含調(diào)試信息的決定因素,程序設(shè)計者可以在任意的Configuration中增加調(diào)試信息,包括Release版本。
為了增加調(diào)試信息,可以按照下述步驟進(jìn)行:
·???????? 打開Project settings對話框(可以通過快捷鍵ALT+F7打開,也可以通過IDE菜單Project/Settings打開)
·???????? 選擇C/C++頁,Category中選擇general ,則出現(xiàn)一個Debug Info下拉列表框,可供選擇的調(diào)試信息 方式包括:
命令行
?? ?
Project settings
?? ?
說明
無
?? ?
None
?? ?
沒有調(diào)試信息
/Zd
?? ?
Line Numbers Only
?? ?
目標(biāo)文件或者可執(zhí)行文件中只包含全局和導(dǎo)出符號以及代碼行信息,不包含符號調(diào)試信息
/Z7
?? ?
C 7.0- Compatible
?? ?
目標(biāo)文件或者可執(zhí)行文件中包含行號和所有符號調(diào)試信息,包括變量名及類型,函數(shù)及原型等
/Zi
?? ?
Program Database
?? ?
創(chuàng)建一個程序庫(PDB),包括類型信息和符號調(diào)試信息。
/ZI
?? ?
Program Database for Edit and Continue
?? ?
除了前面/Zi的功能外,這個選項允許對代碼進(jìn)行調(diào)試過程中的修改和繼續(xù)執(zhí)行。這個選項同時使#pragma設(shè)置的優(yōu)化功能無效
·???????? 選擇Link頁,選中復(fù)選框"Generate Debug Info",這個選項將使連接器把調(diào)試信息寫進(jìn)可執(zhí)行文件和DLL
·???????? 如果C/C++頁中設(shè)置了Program Database以上的選項,則Link incrementally可以選擇。選中這個選項,將使程序可以在上一次編譯的基礎(chǔ)上被編譯(即增量編譯),而不必每次都從頭開始編譯。
調(diào)試方法:
1、使用 Assert(原則:盡量簡單) assert只在debug下生效,release下不會被編譯。
2、防御性的編程
3、使用Trace
4、用GetLastError來檢測返回值,通過得到錯誤代碼來分析錯誤原因
5、把錯誤信息記錄到文件中
位置斷點(diǎn)(Location Breakpoint)
? 大家最常用的斷點(diǎn)是普通的位置斷點(diǎn),在源程序的某一行按F9就設(shè)置了一個位置斷點(diǎn)。但對于很多問題,這種樸素的斷點(diǎn)作用有限。譬如下面這段代碼:
void CForDebugDlg::OnOK()
{
?????? for (int i = 0; i < 1000; i++)??? //A
?????? {
????????????? int k = i * 10 - 2; //B
????????????? SendTo(k);????????? //C
????????????? int tmp = DoSome(i); //D
????????????? int j = i / tmp;??? //E
?????? }
}??? ?
??? ?
? 執(zhí)行此函數(shù),程序崩潰于E行,發(fā)現(xiàn)此時tmp為0,假設(shè)tmp本不應(yīng)該為0,怎么這個時候?yàn)?呢?所以最好能夠跟蹤此次循環(huán)時DoSome函數(shù)是如何運(yùn)行的,但由于是在循環(huán)體內(nèi),如果在E行設(shè)置斷點(diǎn),可能需要按F5(GO)許多次。這樣手要不停的按,很痛苦。使用VC6斷點(diǎn)修飾條件就可以輕易解決此問題。步驟如下。
? 1 Ctrl+B打開斷點(diǎn)設(shè)置框,如下圖:
Figure 1設(shè)置高級位置斷點(diǎn)
? 2 然后選擇D行所在的斷點(diǎn),然后點(diǎn)擊condition按鈕,在彈出對話框的最下面一個編輯框中輸入一個很大數(shù)目,具體視應(yīng)用而定,這里1000就夠了。
? 3 按F5重新運(yùn)行程序,程序中斷。Ctrl+B打開斷點(diǎn)框,發(fā)現(xiàn)此斷點(diǎn)后跟隨一串說明:...487 times remaining。意思是還剩下487次沒有執(zhí)行,那就是說執(zhí)行到513(1000-487)次時候出錯的。因此,我們按步驟2所講,更改此斷點(diǎn)的skip次數(shù),將1000改為513。
? 4 再次重新運(yùn)行程序,程序執(zhí)行了513次循環(huán),然后自動停在斷點(diǎn)處。這時,我們就可以仔細(xì)查看DoSome是如何返回0的。這樣,你就避免了手指的痛苦,節(jié)省了時間。
? 再看位置斷點(diǎn)其他修飾條件。如Figure 1所示,在“Enter the expression to be evaluated:”下面,可以輸入一些條件,當(dāng)這些條件滿足時,斷點(diǎn)才啟動。譬如,剛才的程序,我們需要i為100時程序停下來,我們就可以輸入在編輯框中輸入“i==100”。
? 另外,如果在此編輯框中如果只輸入變量名稱,則變量發(fā)生改變時,斷點(diǎn)才會啟動。這對檢測一個變量何時被修改很方便,特別對一些大程序。
? 用好位置斷點(diǎn)的修飾條件,可以大大方便解決某些問題。
數(shù)據(jù)斷點(diǎn)(Data Breakpoint)
? 軟件調(diào)試過程中,有時會發(fā)現(xiàn)一些數(shù)據(jù)會莫名其妙的被修改掉(如一些數(shù)組的越界寫導(dǎo)致覆蓋了另外的變量),找出何處代碼導(dǎo)致這塊內(nèi)存被更改是一件棘手的事情(如果沒有調(diào)試器的幫助)。恰當(dāng)運(yùn)用數(shù)據(jù)斷點(diǎn)可以快速幫你定位何時何處這個數(shù)據(jù)被修改。譬如下面一段程序:
#include "stdafx.h"
#include
?int main(int argc, char* argv[])
{
?????? char szName1[10];
?????? char szName2[4];
?????? strcpy(szName1,"shenzhen");??????????? ?
?????? printf("%s/n", szName1);????????? //A
??????? strcpy(szName2, "vckbase");????????????? //B
?????? printf("%s/n", szName1);
?????? printf("%s/n", szName2);
??????? return 0;
}
??????? 這段程序的輸出是
???????????? szName1: shenzhen
?????? szName1: ase
?????? szName2: vckbase
???? szName1何時被修改呢?因?yàn)闆]有明顯的修改szName1代碼。我們可以首先在A行設(shè)置普通斷點(diǎn),F5運(yùn)行程序,程序停在A行。然后我們再設(shè)置一個數(shù)據(jù)斷點(diǎn)。如下圖:
Figure 2 數(shù)據(jù)斷點(diǎn)
? F5繼續(xù)運(yùn)行,程序停在B行,說明B處代碼修改了szName1。B處明明沒有修改szName1呀?但調(diào)試器指明是這一行,一般不會錯,所以還是靜下心來看看程序,哦,你發(fā)現(xiàn)了:szName2只有4個字節(jié),而strcpy了7個字節(jié),所以覆寫了szName1。
? 數(shù)據(jù)斷點(diǎn)不只是對變量改變有效,還可以設(shè)置變量是否等于某個值。譬如,你可以將Figure 2中紅圈處改為條件”szName2[0]==''''y''''“,那么當(dāng)szName2第一個字符為y時斷點(diǎn)就會啟動。
? 可以看出,數(shù)據(jù)斷點(diǎn)相對位置斷點(diǎn)一個很大的區(qū)別是不用明確指明在哪一行代碼設(shè)置斷點(diǎn)。
其他調(diào)試手段:系統(tǒng)提供一系列特殊的函數(shù)或者宏來處理Debug版本相關(guān)的信息,如下:
宏名/函數(shù)名
?? ?
說明
TRACE
?? ?
使用方法和printf完全一致,他在output框中輸出調(diào)試信息
ASSERT
?? ?
它接收一個表達(dá)式,如果這個表達(dá)式為TRUE,則無動作,否則中斷當(dāng)前程序執(zhí)行。對于系統(tǒng)中出現(xiàn)這個宏 導(dǎo)致的中斷,應(yīng)該認(rèn)為你的函數(shù)調(diào)用未能滿足系統(tǒng)的調(diào)用此函數(shù)的前提條件。例如,對于一個還沒有創(chuàng)建的窗口調(diào)用SetWindowText等。
VERIFY
?? ?
和ASSERT功能類似,所不同的是,在Release版本中,ASSERT不計算輸入的表達(dá)式的值,而VERIFY計算表達(dá)式的值。
?
值
Watch
VC支持查看變量、表達(dá)式和內(nèi)存的值。所有這些觀察都必須是在斷點(diǎn)中斷的情況下進(jìn)行。
觀看變量的值最簡單,當(dāng)斷點(diǎn)到達(dá)時,把光標(biāo)移動到這個變量上,停留一會就可以看到變量的值。
VC提供一種被成為Watch的機(jī)制來觀看變量和表達(dá)式的值。在斷點(diǎn)狀態(tài)下,在變量上單擊右鍵,選擇Quick Watch, 就彈出一個對話框,顯示這個變量的值。
單擊Debug工具條上的Watch按鈕,就出現(xiàn)一個Watch視圖(Watch1,Watch2,Watch3,Watch4),在該視圖中輸入變量或者表達(dá)式,就可以觀察 變量或者表達(dá)式的值。注意:這個表達(dá)式不能有副作用,例如++運(yùn)算符絕對禁止用于這個表達(dá)式中,因?yàn)檫@個運(yùn)算符將修改變量的值,導(dǎo)致 軟件的邏輯被破壞。
Memory
由于指針指向的數(shù)組,Watch只能顯示第一個元素的值。為了顯示數(shù)組的后續(xù)內(nèi)容,或者要顯示一片內(nèi)存的內(nèi)容,可以使用memory功能。在 Debug工具條上點(diǎn)memory按鈕,就彈出一個對話框,在其中輸入地址,就可以顯示該地址指向的內(nèi)存的內(nèi)容。
Varibles
Debug工具條上的Varibles按鈕彈出一個框,顯示所有當(dāng)前執(zhí)行上下文中可見的變量的值。特別是當(dāng)前指令涉及的變量,以紅色顯示。
寄存器
Debug工具條上的Reigsters按鈕彈出一個框,顯示當(dāng)前的所有寄存器的值。
?
調(diào)試技巧:
1、VC++中F5進(jìn)行調(diào)試運(yùn)行
a)、在output Debug窗口中可以看到用TRACE打印的信息
b)、 Call Stack窗口中能看到程序的調(diào)用堆棧
2、當(dāng)Debug版本運(yùn)行時發(fā)生崩潰,選擇retry進(jìn)行調(diào)試,通過看Call Stack分析出錯的位置及原因
3、使用映射文件調(diào)試
a)、創(chuàng)建映射文件:Project settings中l(wèi)ink項,選中Generate mapfile,輸出程序代碼地址:/MAPINFO: LINES,得到引出序號:/MAPINFO: EXPORTS。
b)、程序發(fā)布時,應(yīng)該把所有模塊的映射文件都存檔。
c)、查看映射文件:見” 通過崩潰地址找出源代碼的出錯行”文件。
4、可以調(diào)試的Release版本
Project settings中C++項的Debug Info選擇為Program Database,Link項的Debug中選擇Debug Info和Microsoft format。
5、查看API的錯誤碼,在watch窗口輸入@err可以查看或者@err,hr,其中”,hr”表示錯誤碼的說明。
6、Set Next Statement:該功能可以直接跳轉(zhuǎn)到指定的代碼行執(zhí)行,一般用來測試異常處理的代碼。
7、調(diào)試內(nèi)存變量的變化:當(dāng)內(nèi)存發(fā)生變化時停下來。???
進(jìn)程控制
VC允許被中斷的程序繼續(xù)運(yùn)行、單步運(yùn)行和運(yùn)行到指定光標(biāo)處,分別對應(yīng)快捷鍵F5、F10/F11和CTRL+F10。各個快捷鍵功能如下:
快捷鍵
?? ?
說明
F5
?? ?
調(diào)試/繼續(xù)運(yùn)行
F10
?? ?
單步,如果涉及到子函數(shù),不進(jìn)入子函數(shù)內(nèi)部
F11
?? ?
單步,如果涉及到子函數(shù),進(jìn)入子函數(shù)內(nèi)部
CTRL+F10
?? ?
運(yùn)行到當(dāng)前光標(biāo)處。
F7
?? ?
重建
F9
?? ?
設(shè)置斷點(diǎn)/清除斷點(diǎn)
Ctrl+Shift+F9
?? ?
清除所有斷點(diǎn)
Shift+F5
?? ?
結(jié)束調(diào)試
Call Stack
調(diào)用堆棧反映了當(dāng)前斷點(diǎn)處函數(shù)是被那些函數(shù)按照什么順序調(diào)用的。單擊Debug工具條上的Call stack就顯示Call Stack對話框。在CallStack對話框中顯示了一個調(diào)用系列,最上面的是當(dāng)前函數(shù),往下依次是調(diào)用函數(shù)的上級函數(shù)。單擊這些函數(shù)名可以跳到對應(yīng)的函數(shù)中去。
關(guān)注
一個好的程序員不應(yīng)該把所有的判斷交給編譯器和調(diào)試器,應(yīng)該在程序中自己加以程序保護(hù)和錯誤定位,具體措施包括:
·???????? 對于所有有返回值的函數(shù),都應(yīng)該檢查返回值,除非你確信這個函數(shù)調(diào)用絕對不會出錯,或者不關(guān)心它是否出錯。
·???????? 一些函數(shù)返回錯誤,需要用其他函數(shù)獲得錯誤的具體信息。例如accept返回INVALID_SOCKET表示accept失敗,為了查明 具體的失敗原因,應(yīng)該立刻用WSAGetLastError獲得錯誤碼,并針對性的解決問題。
·???????? 有些函數(shù)通過異常機(jī)制拋出錯誤,應(yīng)該用TRY-CATCH語句來檢查錯誤
·???????? 程序員對于能處理的錯誤,應(yīng)該自己在底層處理,對于不能處理的,應(yīng)該報告給用戶讓他們決定怎么處理。如果程序出了異常, 卻不對返回值和其他機(jī)制返回的錯誤信息進(jìn)行判斷,只能是加大了找錯誤的難度。
另外:VC中要編制程序不應(yīng)該一開始就寫cpp/h文件,而應(yīng)該首先創(chuàng)建一個合適的工程。因?yàn)橹挥羞@樣,VC才能選擇合適的編譯、連接 選項。對于加入到工程中的cpp文件,應(yīng)該檢查是否在第一行顯式的包含stdafx.h頭文件,這是Microsoft Visual Studio為了加快編譯 速度而設(shè)置的預(yù)編譯頭文件。在這個#include "stdafx.h"行前面的所有代碼將被忽略,所以其他頭文件應(yīng)該在這一行后面被包含。
對于.c文件,由于不能包含stdafx.h,因此可以通過Project settings把它的預(yù)編譯頭設(shè)置為“不使用”,方法是:
·???????? 彈出Project settings對話框
·???????? 選擇C/C++
·???????? Category選擇Precompilation Header
·???????? 選擇不使用預(yù)編譯頭。
便于調(diào)試的代碼風(fēng)格:
不用全局變量
所有變量都要初始化,成員變量在構(gòu)造函數(shù)中初始化
盡量使用const
詳盡的注釋
總結(jié)
? 調(diào)試最重要的還是你要思考,要猜測你的程序可能出錯的地方,然后運(yùn)用你的調(diào)試器來證實(shí)你的猜測。
?
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生
為了調(diào)試一個程序,首先必須使程序中包含調(diào)試信息。一般情況下,一個從AppWizard創(chuàng)建的工程中包含的Debug Configuration自動包含調(diào)試信息,但是是不是Debug版本并不是程序包含調(diào)試信息的決定因素,程序設(shè)計者可以在任意的Configuration中增加調(diào)試信息,包括Release版本。
為了增加調(diào)試信息,可以按照下述步驟進(jìn)行:
·???????? 打開Project settings對話框(可以通過快捷鍵ALT+F7打開,也可以通過IDE菜單Project/Settings打開)
·???????? 選擇C/C++頁,Category中選擇general ,則出現(xiàn)一個Debug Info下拉列表框,可供選擇的調(diào)試信息 方式包括:
命令行
?? ?
Project settings
?? ?
說明
無
?? ?
None
?? ?
沒有調(diào)試信息
/Zd
?? ?
Line Numbers Only
?? ?
目標(biāo)文件或者可執(zhí)行文件中只包含全局和導(dǎo)出符號以及代碼行信息,不包含符號調(diào)試信息
/Z7
?? ?
C 7.0- Compatible
?? ?
目標(biāo)文件或者可執(zhí)行文件中包含行號和所有符號調(diào)試信息,包括變量名及類型,函數(shù)及原型等
/Zi
?? ?
Program Database
?? ?
創(chuàng)建一個程序庫(PDB),包括類型信息和符號調(diào)試信息。
/ZI
?? ?
Program Database for Edit and Continue
?? ?
除了前面/Zi的功能外,這個選項允許對代碼進(jìn)行調(diào)試過程中的修改和繼續(xù)執(zhí)行。這個選項同時使#pragma設(shè)置的優(yōu)化功能無效
·???????? 選擇Link頁,選中復(fù)選框"Generate Debug Info",這個選項將使連接器把調(diào)試信息寫進(jìn)可執(zhí)行文件和DLL
·???????? 如果C/C++頁中設(shè)置了Program Database以上的選項,則Link incrementally可以選擇。選中這個選項,將使程序可以在上一次編譯的基礎(chǔ)上被編譯(即增量編譯),而不必每次都從頭開始編譯。
調(diào)試方法:
1、使用 Assert(原則:盡量簡單) assert只在debug下生效,release下不會被編譯。
2、防御性的編程
3、使用Trace
4、用GetLastError來檢測返回值,通過得到錯誤代碼來分析錯誤原因
5、把錯誤信息記錄到文件中
位置斷點(diǎn)(Location Breakpoint)
? 大家最常用的斷點(diǎn)是普通的位置斷點(diǎn),在源程序的某一行按F9就設(shè)置了一個位置斷點(diǎn)。但對于很多問題,這種樸素的斷點(diǎn)作用有限。譬如下面這段代碼:
void CForDebugDlg::OnOK()
{
?????? for (int i = 0; i < 1000; i++)??? //A
?????? {
????????????? int k = i * 10 - 2; //B
????????????? SendTo(k);????????? //C
????????????? int tmp = DoSome(i); //D
????????????? int j = i / tmp;??? //E
?????? }
}??? ?
??? ?
? 執(zhí)行此函數(shù),程序崩潰于E行,發(fā)現(xiàn)此時tmp為0,假設(shè)tmp本不應(yīng)該為0,怎么這個時候?yàn)?呢?所以最好能夠跟蹤此次循環(huán)時DoSome函數(shù)是如何運(yùn)行的,但由于是在循環(huán)體內(nèi),如果在E行設(shè)置斷點(diǎn),可能需要按F5(GO)許多次。這樣手要不停的按,很痛苦。使用VC6斷點(diǎn)修飾條件就可以輕易解決此問題。步驟如下。
? 1 Ctrl+B打開斷點(diǎn)設(shè)置框,如下圖:
Figure 1設(shè)置高級位置斷點(diǎn)
? 2 然后選擇D行所在的斷點(diǎn),然后點(diǎn)擊condition按鈕,在彈出對話框的最下面一個編輯框中輸入一個很大數(shù)目,具體視應(yīng)用而定,這里1000就夠了。
? 3 按F5重新運(yùn)行程序,程序中斷。Ctrl+B打開斷點(diǎn)框,發(fā)現(xiàn)此斷點(diǎn)后跟隨一串說明:...487 times remaining。意思是還剩下487次沒有執(zhí)行,那就是說執(zhí)行到513(1000-487)次時候出錯的。因此,我們按步驟2所講,更改此斷點(diǎn)的skip次數(shù),將1000改為513。
? 4 再次重新運(yùn)行程序,程序執(zhí)行了513次循環(huán),然后自動停在斷點(diǎn)處。這時,我們就可以仔細(xì)查看DoSome是如何返回0的。這樣,你就避免了手指的痛苦,節(jié)省了時間。
? 再看位置斷點(diǎn)其他修飾條件。如Figure 1所示,在“Enter the expression to be evaluated:”下面,可以輸入一些條件,當(dāng)這些條件滿足時,斷點(diǎn)才啟動。譬如,剛才的程序,我們需要i為100時程序停下來,我們就可以輸入在編輯框中輸入“i==100”。
? 另外,如果在此編輯框中如果只輸入變量名稱,則變量發(fā)生改變時,斷點(diǎn)才會啟動。這對檢測一個變量何時被修改很方便,特別對一些大程序。
? 用好位置斷點(diǎn)的修飾條件,可以大大方便解決某些問題。
數(shù)據(jù)斷點(diǎn)(Data Breakpoint)
? 軟件調(diào)試過程中,有時會發(fā)現(xiàn)一些數(shù)據(jù)會莫名其妙的被修改掉(如一些數(shù)組的越界寫導(dǎo)致覆蓋了另外的變量),找出何處代碼導(dǎo)致這塊內(nèi)存被更改是一件棘手的事情(如果沒有調(diào)試器的幫助)。恰當(dāng)運(yùn)用數(shù)據(jù)斷點(diǎn)可以快速幫你定位何時何處這個數(shù)據(jù)被修改。譬如下面一段程序:
#include "stdafx.h"
#include
?int main(int argc, char* argv[])
{
?????? char szName1[10];
?????? char szName2[4];
?????? strcpy(szName1,"shenzhen");??????????? ?
?????? printf("%s/n", szName1);????????? //A
??????? strcpy(szName2, "vckbase");????????????? //B
?????? printf("%s/n", szName1);
?????? printf("%s/n", szName2);
??????? return 0;
}
??????? 這段程序的輸出是
???????????? szName1: shenzhen
?????? szName1: ase
?????? szName2: vckbase
???? szName1何時被修改呢?因?yàn)闆]有明顯的修改szName1代碼。我們可以首先在A行設(shè)置普通斷點(diǎn),F5運(yùn)行程序,程序停在A行。然后我們再設(shè)置一個數(shù)據(jù)斷點(diǎn)。如下圖:
Figure 2 數(shù)據(jù)斷點(diǎn)
? F5繼續(xù)運(yùn)行,程序停在B行,說明B處代碼修改了szName1。B處明明沒有修改szName1呀?但調(diào)試器指明是這一行,一般不會錯,所以還是靜下心來看看程序,哦,你發(fā)現(xiàn)了:szName2只有4個字節(jié),而strcpy了7個字節(jié),所以覆寫了szName1。
? 數(shù)據(jù)斷點(diǎn)不只是對變量改變有效,還可以設(shè)置變量是否等于某個值。譬如,你可以將Figure 2中紅圈處改為條件”szName2[0]==''''y''''“,那么當(dāng)szName2第一個字符為y時斷點(diǎn)就會啟動。
? 可以看出,數(shù)據(jù)斷點(diǎn)相對位置斷點(diǎn)一個很大的區(qū)別是不用明確指明在哪一行代碼設(shè)置斷點(diǎn)。
其他調(diào)試手段:系統(tǒng)提供一系列特殊的函數(shù)或者宏來處理Debug版本相關(guān)的信息,如下:
宏名/函數(shù)名
?? ?
說明
TRACE
?? ?
使用方法和printf完全一致,他在output框中輸出調(diào)試信息
ASSERT
?? ?
它接收一個表達(dá)式,如果這個表達(dá)式為TRUE,則無動作,否則中斷當(dāng)前程序執(zhí)行。對于系統(tǒng)中出現(xiàn)這個宏 導(dǎo)致的中斷,應(yīng)該認(rèn)為你的函數(shù)調(diào)用未能滿足系統(tǒng)的調(diào)用此函數(shù)的前提條件。例如,對于一個還沒有創(chuàng)建的窗口調(diào)用SetWindowText等。
VERIFY
?? ?
和ASSERT功能類似,所不同的是,在Release版本中,ASSERT不計算輸入的表達(dá)式的值,而VERIFY計算表達(dá)式的值。
?
值
Watch
VC支持查看變量、表達(dá)式和內(nèi)存的值。所有這些觀察都必須是在斷點(diǎn)中斷的情況下進(jìn)行。
觀看變量的值最簡單,當(dāng)斷點(diǎn)到達(dá)時,把光標(biāo)移動到這個變量上,停留一會就可以看到變量的值。
VC提供一種被成為Watch的機(jī)制來觀看變量和表達(dá)式的值。在斷點(diǎn)狀態(tài)下,在變量上單擊右鍵,選擇Quick Watch, 就彈出一個對話框,顯示這個變量的值。
單擊Debug工具條上的Watch按鈕,就出現(xiàn)一個Watch視圖(Watch1,Watch2,Watch3,Watch4),在該視圖中輸入變量或者表達(dá)式,就可以觀察 變量或者表達(dá)式的值。注意:這個表達(dá)式不能有副作用,例如++運(yùn)算符絕對禁止用于這個表達(dá)式中,因?yàn)檫@個運(yùn)算符將修改變量的值,導(dǎo)致 軟件的邏輯被破壞。
Memory
由于指針指向的數(shù)組,Watch只能顯示第一個元素的值。為了顯示數(shù)組的后續(xù)內(nèi)容,或者要顯示一片內(nèi)存的內(nèi)容,可以使用memory功能。在 Debug工具條上點(diǎn)memory按鈕,就彈出一個對話框,在其中輸入地址,就可以顯示該地址指向的內(nèi)存的內(nèi)容。
Varibles
Debug工具條上的Varibles按鈕彈出一個框,顯示所有當(dāng)前執(zhí)行上下文中可見的變量的值。特別是當(dāng)前指令涉及的變量,以紅色顯示。
寄存器
Debug工具條上的Reigsters按鈕彈出一個框,顯示當(dāng)前的所有寄存器的值。
?
調(diào)試技巧:
1、VC++中F5進(jìn)行調(diào)試運(yùn)行
a)、在output Debug窗口中可以看到用TRACE打印的信息
b)、 Call Stack窗口中能看到程序的調(diào)用堆棧
2、當(dāng)Debug版本運(yùn)行時發(fā)生崩潰,選擇retry進(jìn)行調(diào)試,通過看Call Stack分析出錯的位置及原因
3、使用映射文件調(diào)試
a)、創(chuàng)建映射文件:Project settings中l(wèi)ink項,選中Generate mapfile,輸出程序代碼地址:/MAPINFO: LINES,得到引出序號:/MAPINFO: EXPORTS。
b)、程序發(fā)布時,應(yīng)該把所有模塊的映射文件都存檔。
c)、查看映射文件:見” 通過崩潰地址找出源代碼的出錯行”文件。
4、可以調(diào)試的Release版本
Project settings中C++項的Debug Info選擇為Program Database,Link項的Debug中選擇Debug Info和Microsoft format。
5、查看API的錯誤碼,在watch窗口輸入@err可以查看或者@err,hr,其中”,hr”表示錯誤碼的說明。
6、Set Next Statement:該功能可以直接跳轉(zhuǎn)到指定的代碼行執(zhí)行,一般用來測試異常處理的代碼。
7、調(diào)試內(nèi)存變量的變化:當(dāng)內(nèi)存發(fā)生變化時停下來。???
進(jìn)程控制
VC允許被中斷的程序繼續(xù)運(yùn)行、單步運(yùn)行和運(yùn)行到指定光標(biāo)處,分別對應(yīng)快捷鍵F5、F10/F11和CTRL+F10。各個快捷鍵功能如下:
快捷鍵
?? ?
說明
F5
?? ?
調(diào)試/繼續(xù)運(yùn)行
F10
?? ?
單步,如果涉及到子函數(shù),不進(jìn)入子函數(shù)內(nèi)部
F11
?? ?
單步,如果涉及到子函數(shù),進(jìn)入子函數(shù)內(nèi)部
CTRL+F10
?? ?
運(yùn)行到當(dāng)前光標(biāo)處。
F7
?? ?
重建
F9
?? ?
設(shè)置斷點(diǎn)/清除斷點(diǎn)
Ctrl+Shift+F9
?? ?
清除所有斷點(diǎn)
Shift+F5
?? ?
結(jié)束調(diào)試
Call Stack
調(diào)用堆棧反映了當(dāng)前斷點(diǎn)處函數(shù)是被那些函數(shù)按照什么順序調(diào)用的。單擊Debug工具條上的Call stack就顯示Call Stack對話框。在CallStack對話框中顯示了一個調(diào)用系列,最上面的是當(dāng)前函數(shù),往下依次是調(diào)用函數(shù)的上級函數(shù)。單擊這些函數(shù)名可以跳到對應(yīng)的函數(shù)中去。
關(guān)注
一個好的程序員不應(yīng)該把所有的判斷交給編譯器和調(diào)試器,應(yīng)該在程序中自己加以程序保護(hù)和錯誤定位,具體措施包括:
·???????? 對于所有有返回值的函數(shù),都應(yīng)該檢查返回值,除非你確信這個函數(shù)調(diào)用絕對不會出錯,或者不關(guān)心它是否出錯。
·???????? 一些函數(shù)返回錯誤,需要用其他函數(shù)獲得錯誤的具體信息。例如accept返回INVALID_SOCKET表示accept失敗,為了查明 具體的失敗原因,應(yīng)該立刻用WSAGetLastError獲得錯誤碼,并針對性的解決問題。
·???????? 有些函數(shù)通過異常機(jī)制拋出錯誤,應(yīng)該用TRY-CATCH語句來檢查錯誤
·???????? 程序員對于能處理的錯誤,應(yīng)該自己在底層處理,對于不能處理的,應(yīng)該報告給用戶讓他們決定怎么處理。如果程序出了異常, 卻不對返回值和其他機(jī)制返回的錯誤信息進(jìn)行判斷,只能是加大了找錯誤的難度。
另外:VC中要編制程序不應(yīng)該一開始就寫cpp/h文件,而應(yīng)該首先創(chuàng)建一個合適的工程。因?yàn)橹挥羞@樣,VC才能選擇合適的編譯、連接 選項。對于加入到工程中的cpp文件,應(yīng)該檢查是否在第一行顯式的包含stdafx.h頭文件,這是Microsoft Visual Studio為了加快編譯 速度而設(shè)置的預(yù)編譯頭文件。在這個#include "stdafx.h"行前面的所有代碼將被忽略,所以其他頭文件應(yīng)該在這一行后面被包含。
對于.c文件,由于不能包含stdafx.h,因此可以通過Project settings把它的預(yù)編譯頭設(shè)置為“不使用”,方法是:
·???????? 彈出Project settings對話框
·???????? 選擇C/C++
·???????? Category選擇Precompilation Header
·???????? 選擇不使用預(yù)編譯頭。
便于調(diào)試的代碼風(fēng)格:
不用全局變量
所有變量都要初始化,成員變量在構(gòu)造函數(shù)中初始化
盡量使用const
詳盡的注釋
總結(jié)
? 調(diào)試最重要的還是你要思考,要猜測你的程序可能出錯的地方,然后運(yùn)用你的調(diào)試器來證實(shí)你的猜測。
?
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生
總結(jié)
- 上一篇: 使用MAP文件快速定位程序崩溃代码行
- 下一篇: 程序崩溃的定位