Visual?Studio實用調(diào)試技巧大全
一.什么是BUG??
Bug一詞的原意是蟲子,而在電腦系統(tǒng)或程序中隱藏著的一些未被發(fā)現(xiàn)的缺陷或問題,人們也叫它"bug"。這是為什么呢?這就要追溯到一個程序員與飛蛾的故事了。
Bug的創(chuàng)始人格蕾絲·赫柏(Grace Murray Hopper),是一位為美國海軍工作的電腦專家,也是最早將人類語言融入到電腦程序的人之一。而代表電腦程序出錯的“bug” 這名字,正是由赫柏所取的。1947年9月9日,赫柏對Harvard Mark II設(shè)置好17000個繼電器進行編程后,技術(shù)人員正在進行整機運行時,它突然停止了工作。于是他們爬上去找原因,發(fā)現(xiàn)這臺巨大的計算機內(nèi)部一組繼電器的觸點之間有一只飛蛾,這顯然是由于飛蛾受光和熱的吸引,飛到了觸點上,然后被高電壓擊死。所以在報告中,赫柏用膠條貼上飛蛾,并把“bug”來表示“一個在電腦程序里的錯誤”,“Bug”這個說法一直沿用到今天。
格蕾絲·赫柏的報告
二.調(diào)試及其重要性??
2.1 什么是調(diào)試
所有發(fā)生的事情都一定有跡可循,如果問心無愧,就不需要掩蓋也就沒有跡象了,如果問心有愧,就必然需要掩蓋,那就一定會有跡象,跡象越多就越容易順藤而上,這就是 推理的途徑。順著這條途徑順流而下就是犯罪,逆流而上,就是真相。
而我們程序員就好比一個偵探,一個用來尋找bug,修改bug的偵探。人們將這個過程叫做"Debug"(中文稱作"調(diào)試"),意即"捉蟲子"或"殺蟲子"。每一次調(diào)試都是嘗試破案的過程。
調(diào)試(英語:Debugging / Debug),又稱除錯,是發(fā)現(xiàn)和減少計算機程序或電子儀器設(shè)備中程序錯誤的一個過程。
2.2 調(diào)試的基本步驟
- 發(fā)現(xiàn)程序錯誤的存在
- 以隔離、消除等方式對錯誤進行定位
- 確定錯誤產(chǎn)生的原因
- 提出糾正錯誤的解決辦法
- 對程序錯誤予以改正,重新測試
本文將詳細介紹在windows系統(tǒng)的 VS環(huán)境下的調(diào)試過程。
2.3 Debug與Release的介紹
在VS中,我們會發(fā)現(xiàn)我們的程序可以在兩個環(huán)境下運行,這兩個環(huán)境就是Debug版本和Release版本,它們二者有何區(qū)別呢?
VS中的Debug與Release
Debug 通常稱為 調(diào)試版本,它包含調(diào)試信息,并且不作任何優(yōu)化, 便于程序員調(diào)試程序。
Release 稱為 發(fā)布版本,它往往是 進行了各種優(yōu)化,使得程序在代碼大小和運行速度上都是最優(yōu)的, 以便用戶很好地使用。
我們可以分別在兩種環(huán)境下編譯代碼生成對應(yīng)的可執(zhí)行程序如下:
顯然,Release版本的可執(zhí)行文件比Debug版本的小很多,說明編譯器對其作了優(yōu)化。
因此,我們?nèi)粘Kf的調(diào)試是在Debug版本的環(huán)境下進行的,這是因為Release版本在不加以配置的情況下其調(diào)試信息會被編譯器優(yōu)化,對于程序員來說,調(diào)試基本都是在Debug環(huán)境下運行(本文下面的調(diào)試也都是在Debug環(huán)境下進行調(diào)試)。而對于測試人員來說,由于要站在用戶的角度上來測試程序是否能正常使用,因此測試是在Release版本的環(huán)境下進行的。
??但不排除編寫的程序在Debug環(huán)境下沒有問題,在Release版本出現(xiàn)問題的情況。有些問題在Release環(huán)境下才會浮現(xiàn),這時我們就需要對Release版本進行配置,進行"調(diào)試"排查錯誤。我們有以下幾種方法:
1.項目 -> 屬性 -> C/C++ -> 優(yōu)化 -> 禁用優(yōu)化,通過禁用優(yōu)化使其能生成調(diào)試信息進行排錯,如下:
2.在項目 -> 屬性 -> C/C++ -> 優(yōu)化界面處,將選項逐個改為對應(yīng)的Debug選項,如/O2改為/O1、/Oy改為/Oy-或者將運行時間優(yōu)化改為程序大小優(yōu)化。注意的是,一般一次只改一個選項,通過觀察改哪個選項時錯誤消失來鎖定該選項相關(guān)的錯誤,針對性地查找。個人較推薦這種方法。
話又說回來,Release版本下編譯器到底可能會做什么優(yōu)化呢?請看如下代碼:
#include <stdio.h> int main() { int i = 0; int arr[10] = {0}; for(i=0; i<=12; i++) { arr[i] = 0; printf("hehe\n"); } return 0; }
我們很容易就發(fā)現(xiàn)對數(shù)組進行了越界訪問,當程序運行起來時應(yīng)該會崩潰。但是在Debug環(huán)境下我們發(fā)現(xiàn)程序并沒有崩潰而是陷入了死循環(huán):
我們運行調(diào)試代碼轉(zhuǎn)到反匯編如下:
我們知道,數(shù)據(jù)在棧上的開辟是從高地址向低地址處開辟的,因此在Debug環(huán)境下變量i的地址比數(shù)組arr的地址高。而在數(shù)組內(nèi)部數(shù)據(jù)的存儲是從低地址向高地址的,因此首元素地址arr是在數(shù)組所在空間的低位,如下:
我們很驚訝的發(fā)現(xiàn)(arr+12)就是變量i的地址。當循環(huán)過程中i等于12時,此時將arr[i]改為0就等價于將i的值修改為0,然后i++后i等于1小于12,繼續(xù)進行循環(huán),依次反復(fù)形成了死循環(huán)。如下:
整個過程簡化圖如下:
而在Release版本的環(huán)境下程序并不會出現(xiàn)死循環(huán)的問題:
我們可以打印出此時變量i和數(shù)組在棧上的地址如下:
我們發(fā)現(xiàn)此時變量i被編譯器優(yōu)化到低地址處,arr[12]存儲的值就不是i了,便不會出現(xiàn)死循環(huán)的情況。
綜上,以上代碼在Release版本下,編譯器使 變量在內(nèi)存中開辟的順序發(fā)生了變化,影響到了程序執(zhí)行的結(jié)果,這便是優(yōu)化帶來的好處。
三.windows環(huán)境下調(diào)試介紹?
首先第一步,我們需要將環(huán)境切換為Debug版本,才能進行調(diào)試
3.1 常用快捷鍵介紹
以下是在調(diào)試過程中最為常用的幾個快捷鍵:
快捷鍵 | 功能 |
Ctrl+F5 | 開始執(zhí)行而不進行調(diào)試。用于想讓程序直接運行起來而不調(diào)試時。 |
F9 | 作用:創(chuàng)建斷點和取消斷點 斷點:可以使程序在任何你想停下的地方停止執(zhí)行,繼而一步步執(zhí)行下去。我們可以在任何地方設(shè)置斷點,一個程序也可以有多個斷點。 |
F10 | 逐過程,通常用來處理一個過程,一個過程可以是一次函數(shù)調(diào)用,或者是一條語句。 |
F11 | 逐語句,每次只執(zhí)行一條語句,我們可以通過這個快捷鍵使我們的執(zhí)行邏輯進入函數(shù)內(nèi)部(這是F10所不具備的,F(xiàn)11是我們在調(diào)試過程中最常用的) |
F5 | 啟動調(diào)試到下一斷點處,需要配合F9進行使用,如果程序沒有斷點則無異于Ctrl+F5 |
快捷鍵用法
我們還可以對某一斷點設(shè)置停止條件,方法是 右鍵斷點->單擊條件->輸入斷點條件->關(guān)閉
例如:我們需要讓程序停止在第4次循環(huán)處,我們可以輸入i==4
此時單擊F5運行到斷點,我們查看自動窗口發(fā)現(xiàn)程序在停止時i的值為4
3.2 在調(diào)試過程中查看程序當前信息
開啟調(diào)試后,我們可以在VS上方的調(diào)試->窗口看到許多用來查看數(shù)據(jù)信息的窗口:
3.3.1 監(jiān)視窗口
通過監(jiān)視窗口我們可以查看我們想要查看的 變量甚至是表達式的值在程序運行過程中的變化,十分靈活,這是我們調(diào)試中用得最多的窗口之一。如下:
3.3.2 自動窗口
打開自動窗口后,編譯器會將一些可能需要觀察的變量顯示在窗口中,較為方便。其缺陷是可能無法顯示我們真正需要觀察的變量,并且 無法靈活顯示表達式等的值。如下:
3.3.3 局部變量窗口
打開局部變量變量窗口,會將程序中的所有 局部變量全部顯示出來。如下:
3.3.4 調(diào)用堆棧窗口
通過調(diào)用堆棧,可以清晰的反應(yīng) 函數(shù)的調(diào)用關(guān)系以及當前調(diào)用所處的位置。如下:
通過調(diào)用堆棧窗口,我們可以發(fā)現(xiàn),show函數(shù)棧幀在main函數(shù)棧幀之上,即 show函數(shù)是由main函數(shù)調(diào)用的。并且可以看出此時show函數(shù)運行到第20行。
3.3.5 內(nèi)存窗口
通過內(nèi)存窗口我們可以看到內(nèi)存中的信息,可以 觀察變量在內(nèi)存中的存儲情況。如下:
3.3.6 反匯編
我們可以查看當前程序轉(zhuǎn)化后的匯編代碼,進而從更底層的角度觀察程序的執(zhí)行過程。如下:
3.3.7 寄存器
通過寄存器窗口,我們可以觀察在當前環(huán)境下CPU內(nèi)的寄存器的使用信息,如ebp棧底寄存器、esp棧頂寄存器等等。
3.3 調(diào)試實例
我們上面通過調(diào)試分析了數(shù)組越界陷入死循環(huán)的問題。下面,我們再通過一道實例來掌握調(diào)試的技巧:
實現(xiàn)代碼:求 1!+2!+3! ...+ n! ;不考慮溢出。我們可能會寫出以下代碼:
#include<stdio.h> int main() { int i = 0; int sum = 0;//保存最終結(jié)果 int n = 0; int ret = 1;//保存n的階乘 scanf("%d", &n); for(i=1; i<=n; i++) { int j = 0; for(j=1; j<=i; j++) { ret *= j; } sum += ret; } printf("%d\n", sum); return 0; }
當你輸入3時,理論上應(yīng)該輸出9,但實際上程序卻輸出了15:
是什么問題導致出錯了呢?這就需要我們動手進行調(diào)試。在調(diào)試之前我們可以先 預(yù)測問題的所在,比如 算階乘時出錯、 求和時出錯等等。做到心里有數(shù),而不是盲目的進行調(diào)試。
我們可以在ret*=j處設(shè)置一個斷點,打開監(jiān)視窗口監(jiān)視ret和sum觀察每個數(shù)階乘的值和累加后的值,如下:
我們單擊F10逐過程執(zhí)行,當外層循環(huán)i的值為3時,即計算3的階乘時,我們發(fā)現(xiàn)ret的初始值并非為1,而是2的階乘。此時再計算3的階乘,就是2*1*2*3=12 != 6,結(jié)果出錯:
最后sum+ret的值就為15,與我們的輸出相符:
可見,每次在求階乘時,我們都應(yīng)該把ret重置為1,正確的代碼如下:
int main() { int i = 0; int sum = 0;//保存最終結(jié)果 int n = 0; int ret = 1;//保存n的階乘 scanf("%d", &n); for (i = 1; i <= n; i++) { int j = 0; ret = 1; //將ret置1 for (j = 1; j <= i; j++) { ret *= j; } sum += ret; } printf("%d\n", sum); return 0; }
3.4 實踐出真知
任何事情都不可能一蹴而就,一定要多加練習,才能熟練掌握調(diào)試技巧。
一個初學者可能80%的時間在寫代碼,20%的時間在進行調(diào)試;而一個程序員,可能80%的時間在進行調(diào)試,剩余20%的時間才是在寫代碼。
隨之學習的深入,后續(xù)可能會出現(xiàn)很多更加復(fù)雜的調(diào)試場景,如多線程等。只有打好基礎(chǔ),在未來才能融會貫通,利于不敗之地。
多多使用快捷鍵可以很大程度上提高效率。
四.如何寫出便于調(diào)試的優(yōu)秀代碼
4.1 什么是優(yōu)秀的代碼
1. 代碼運行正常
2. bug很少
3. 效率高
4. 可讀性高
5. 可維護性高
6. 注釋清晰
7. 文檔齊全
4.2 常用coding(編碼)技巧
1.多使用assert斷言,可以告知你出錯的位置
2.盡量使用const,避免意外修改
3.養(yǎng)成良好的編碼風格
4.添加必要的注釋
5.避免編碼的陷阱
4.3 實例
試模擬實現(xiàn)一個strcpy函數(shù),盡量用到上述的編碼技巧。如下:
char* strcpy(char* dst, const char* src) //const修飾防止對源字符串進行修改 { assert(dst && src); //避免傳入空指針 char cur = dst; //保存起始位置 //將src的字符一個個拷貝到det中,包括'\0' while ((*dst++ = *src++)!='\0') { ; } return cur; //返回目標字符串,以便鏈式訪問 }
五.編程中常見的錯誤
5.1 編譯型錯誤
在 編譯期間出現(xiàn)的錯誤。直接看錯誤提示信息(雙擊鼠標),解決問題?;蛘邞{借經(jīng)驗就可以搞定。相對來說簡單。
5.2 鏈接型錯誤
在 鏈接期間出現(xiàn)的錯誤??村e誤提示信息,主要在代碼中找到錯誤信息中的標識符,然后定位問題所在。一般是 標識符名不存在或者 拼寫錯誤。
5.3 運行時出錯
在 運行期間出現(xiàn)的錯誤。借助 調(diào)試,逐步定位問題。最難搞的一種錯誤
不要害怕遇到錯誤,每出現(xiàn)一次的錯誤就是一次突破自我的機會。
學會積累排錯經(jīng)驗,勇于嘗試。不經(jīng)風雨 ?? ,怎能見彩虹 ??
以上,就是本期的全部內(nèi)容啦??
總結(jié)
到此這篇關(guān)于Visual Studio實用調(diào)試技巧大全的文章就介紹到這了,更多相關(guān)VS調(diào)試技巧內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
算法系列15天速成——第十五天 圖【下】(大結(jié)局)
今天是大結(jié)局,說下“圖”的最后一點東西,“最小生成樹“和”最短路徑“2013-11-11