调试试炼开始
調(diào)試修煉
- 前言
- 1. bug的誕生
- 2. 調(diào)試 - Debug
- 2.1 調(diào)試是什么
- 2.2 基本步驟
- 2.3 了解Debug和Release
- 2.3.1 區(qū)別
- 2.3.2 例子 - Visual Studio 2019 為例
- 2.3.2.1 在空間大小上的差異:
- 2.3.2.2 在具體調(diào)試上的差異:
- 2.3.2.3 一個(gè)具體的例子
- 3. windows環(huán)境調(diào)試 -
- 3.1 調(diào)試準(zhǔn)備 - Visual Studio 2019
- 3.2 調(diào)試相關(guān)快捷鍵
- 3.3 調(diào)試下的各種信息的查看
- 4. 寫出優(yōu)秀的代碼
- 4.1 優(yōu)秀的代碼
- 4.2 const的修飾作用
- 4.3 一個(gè)例子 - 模擬實(shí)現(xiàn)strlen()
- 5. 編程常見(jiàn)的錯(cuò)誤
- 5.1 編譯錯(cuò)誤
- 5.2 鏈接錯(cuò)誤
- 5.3 運(yùn)行錯(cuò)誤
- 結(jié)束語(yǔ)
前言
調(diào)試是一個(gè)程序員所要必備的技能,我們?cè)儆龅匠绦蚓幾g器無(wú)法發(fā)現(xiàn)的問(wèn)題時(shí)要能夠通過(guò)調(diào)試一步一步的來(lái)找到問(wèn)題所在。
1. bug的誕生
bug原意指蟲(chóng)子,有一天小飛蛾意外飛進(jìn)了正在工作的計(jì)算機(jī)電路里導(dǎo)致了計(jì)算機(jī)工作發(fā)生故障,工作人員對(duì)當(dāng)時(shí)的計(jì)算機(jī)進(jìn)行了細(xì)致的檢查后最終發(fā)現(xiàn)了這只被夾扁的飛蛾,之后計(jì)算機(jī)便恢復(fù)了正常工作狀態(tài)。這只飛蛾順手被夾在了格蕾絲-霍普的工作筆記里并備注為bug,bug便誕生了。
2. 調(diào)試 - Debug
有了bug就必須要找出這個(gè)bug,這個(gè)操作過(guò)程叫做調(diào)試debug/debugging。
調(diào)試就像推理,從迷霧中尋找真相。
2.1 調(diào)試是什么
調(diào)試是發(fā)現(xiàn)和減少計(jì)算機(jī)程序或電子儀器設(shè)備中程序錯(cuò)誤的一個(gè)過(guò)程。
2.2 基本步驟
- 發(fā)現(xiàn)錯(cuò)誤
- 錯(cuò)誤定位 - 隔離、消除等
- 確定原因
- 提出方法
- 改正錯(cuò)誤
- 再次測(cè)試
2.3 了解Debug和Release
2.3.1 區(qū)別
Debug稱為調(diào)試版本,包含調(diào)試信息,不做任何優(yōu)化,以便于程序員進(jìn)行調(diào)試。
Release稱為發(fā)布版本,不包含調(diào)試信息,進(jìn)行了各種優(yōu)化,程序在代碼大小和運(yùn)行速度上都是最優(yōu)的,以便于用戶使用。
相比調(diào)試版本,發(fā)布版本重點(diǎn)優(yōu)化了體積大小與性能效率兩方面。
不是所有程序都能進(jìn)行調(diào)試,包含調(diào)試信息的程序才能進(jìn)行調(diào)試。
2.3.2 例子 - Visual Studio 2019 為例
對(duì)于同一個(gè)程序分別在Debug和Release版本下的一些差異
#include <stdio.h>int main() {printf("Hello World!\n");return 0; }2.3.2.1 在空間大小上的差異:
2.3.2.2 在具體調(diào)試上的差異:
#include <stdio.h>int main() {int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++) {arr[i] = i;}return 0; }Debug版本下:可以進(jìn)行調(diào)試
Release版本下:不可進(jìn)行調(diào)試,程序直接執(zhí)行完畢
2.3.2.3 一個(gè)具體的例子
注意變量i與數(shù)組arr建立先后關(guān)系的差異導(dǎo)致程序運(yùn)行的不同。
#include <stdio.h>int main() {int i = 0;int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };for(i=0; i<=12; i++){arr[i] = 0;printf("hello!\n");}return 0; }Debug版本下:陷入死循環(huán)
Release版本下:
Debug版本下:
程序崩潰,
Release版本下:
3. windows環(huán)境調(diào)試 -
3.1 調(diào)試準(zhǔn)備 - Visual Studio 2019
選擇Debug模式才能進(jìn)行調(diào)試。
3.2 調(diào)試相關(guān)快捷鍵
F5
**啟動(dòng)調(diào)試,遇到斷點(diǎn)時(shí)停下,如果沒(méi)有斷點(diǎn)就直接完整執(zhí)行程序。 **
如果有多個(gè)斷點(diǎn),按下F5可以使程序從當(dāng)前斷點(diǎn)直接運(yùn)行到下一個(gè)邏輯上的斷點(diǎn)。(注意邏輯斷點(diǎn)與實(shí)際斷點(diǎn)可能并不一定完全等價(jià),例如斷點(diǎn)設(shè)置在一個(gè)循環(huán)的內(nèi)部時(shí),邏輯斷點(diǎn)是下一次循環(huán)的斷點(diǎn),但可能實(shí)際的斷點(diǎn)位置不變)。
ctrl + F5
開(kāi)始執(zhí)行但不調(diào)試。直接運(yùn)行程序,如果程序沒(méi)有編譯鏈接過(guò),該操作還會(huì)進(jìn)行新程序的編譯與鏈接。
F9
在某一行設(shè)置斷點(diǎn)或者取消某一行已有的斷點(diǎn)。
可以在程序的任意位置設(shè)置斷點(diǎn),但在空語(yǔ)句處的斷點(diǎn)沒(méi)有意義。
斷點(diǎn)可以使程序在我們預(yù)期停止的地方停下來(lái).
F10
逐過(guò)程調(diào)試,程序停在main函數(shù)入口處,可以通過(guò)多次按F10來(lái)使程序在可觀察的狀態(tài)運(yùn)行。通常用來(lái)處理一個(gè)過(guò)程,一個(gè)過(guò)程可以是一次函數(shù)調(diào)用、一條語(yǔ)句等。
F11
逐語(yǔ)句調(diào)試,每次都只執(zhí)行一條語(yǔ)句,使用F11可以進(jìn)入到用戶自定義函數(shù)的內(nèi)部,比F10更加細(xì)致(因?yàn)镕10并不能進(jìn)入用戶自定義函數(shù)內(nèi)部)。
3.3 調(diào)試下的各種信息的查看
自動(dòng)窗口
不需要手動(dòng)輸入,隨著調(diào)試的進(jìn)行程序中變量、數(shù)組等信息會(huì)自動(dòng)顯示相關(guān)信息,注意自動(dòng)窗口顯示的是調(diào)試附近的相關(guān)信息,距離較遠(yuǎn)的已經(jīng)調(diào)試過(guò)得或未調(diào)試的都不會(huì)再顯示,也就是說(shuō)信息顯示是不固定的,觀察起來(lái)并不方便。
監(jiān)視
需要手動(dòng)輸入想知道的信息,只有手動(dòng)刪除輸入的信息時(shí)才會(huì)刪除。信息的顯示是固定的,方便觀察。
內(nèi)存
查看程序中各數(shù)據(jù)在內(nèi)存中的信息。
調(diào)用堆棧
調(diào)用堆棧,主要是程序有多個(gè)函數(shù)并且存在嵌套調(diào)用時(shí)可以觀察到函數(shù)的調(diào)用關(guān)系和當(dāng)前調(diào)用所處的位置。
反匯編
查看程序的匯編代碼,更加底層。
寄存器
寄存器是CPU內(nèi)部用來(lái)存放數(shù)據(jù)的一些小型儲(chǔ)存區(qū)域,用來(lái)暫時(shí)存放參與運(yùn)算的數(shù)據(jù)和運(yùn)算結(jié)果,有著非常高的讀寫速度。可以觀察到當(dāng)前運(yùn)行情況下寄存器的使用信息。
4. 寫出優(yōu)秀的代碼
4.1 優(yōu)秀的代碼
便于調(diào)試
運(yùn)行良好
效率高
可讀性高
可維護(hù)性高
注釋清晰
文檔齊全
寫代碼時(shí)的一些技巧:
先思考再動(dòng)手
使用assert(斷言)
使用const
有良好的代碼風(fēng)格
注意寫上應(yīng)有的注釋
4.2 const的修飾作用
const修飾可以減小權(quán)限,使程序更加安全。
對(duì)于普通變量const修飾后普通變量不能直接被修改,否則程序出錯(cuò)。但是可以通過(guò)對(duì)變量的地址解引用修改變量的值。
對(duì)于指針變量有兩種情況:
const在*左邊,此時(shí)const修飾的是指針?biāo)赶虻膶?duì)象,而不是指針本身。不能通過(guò)指針解引用的方式改變指針?biāo)赶虻膶?duì)象,但可以不通過(guò)指針而直接修改那個(gè)對(duì)象。
const在*右邊,此時(shí)const修飾的是指針本身。指針獲得一個(gè)變量的地址后不能在被另一個(gè)地址賦值。
#include <stdio.h>int main(){int a = 10;int b = 10;int c = 10;int d = 10;//無(wú)限制的指針變量p1int *p1 = &a;//指針指向?qū)ο笫艿较拗频闹羔榩2const int *p2 = &b;//int const *p2 = &a;a = 5;//true//指針本身受到限制的指針p3int* const p3 = &c;p3 = &a;//error//指針本身和指針指向?qū)ο蠖际艿较拗频闹羔榩4const int* const p4 = &d;*p4 = 5;//errord = 5;//truep4 = &a;//errorreturn 0; }4.3 一個(gè)例子 - 模擬實(shí)現(xiàn)strlen()
#include <stdio.h> #include <assert.h>//使用const修飾指針str所指向的對(duì)象,使其不能被更改,否則程序出錯(cuò) int my_strlen(const char *str){//傳入的指針可能是空指針NULL,此時(shí)str就是野指針,沒(méi)有指向?qū)ο?#xff0c;會(huì)導(dǎo)致程序出現(xiàn)錯(cuò)誤assert(str != NULL);int cnt = 0;while(*str != NULL){str++;cnt++;}return cnt; } int main(){char str[] = "abcdef";int len = my_strlen();printf("%d\n", len);return 0; }5. 編程常見(jiàn)的錯(cuò)誤
從一個(gè)代碼文件(源文件)經(jīng)過(guò)編譯、鏈接過(guò)程到得到可執(zhí)行程序
5.1 編譯錯(cuò)誤
在編譯期間出現(xiàn)的錯(cuò)誤,編譯器一般會(huì)給出對(duì)應(yīng)錯(cuò)誤的相關(guān)位置代碼行,是語(yǔ)法方面的錯(cuò)誤,相對(duì)簡(jiǎn)單。
5.2 鏈接錯(cuò)誤
在鏈接期間出現(xiàn)的錯(cuò)誤,鏈接器把包括源文件在內(nèi)的多個(gè)文件(如頭文件)鏈接在一起形成一個(gè)可執(zhí)行文件。不是語(yǔ)法錯(cuò)誤,一般是代碼中出現(xiàn)了未定義的函數(shù)等外部符號(hào),鏈接錯(cuò)誤一般不給出錯(cuò)誤出現(xiàn)的代碼行,但會(huì)標(biāo)識(shí)除未定義的符號(hào),可以使用查找功能進(jìn)行排查。
#include <stdio.h> void print(){printf(" world\n") } int main(){printf("hello");test();//該函數(shù)未定義;Print();//該函數(shù)雖然定義了,但定義的函數(shù)名與引用的函數(shù)名不匹配return 0; }5.3 運(yùn)行錯(cuò)誤
邏輯錯(cuò)誤等,需要進(jìn)行調(diào)試找出錯(cuò)誤所在,最不好找!。
結(jié)束語(yǔ)
調(diào)試技能是程序員所要必備的技能,隨著項(xiàng)目代碼量的增加,調(diào)試尋找問(wèn)題也就顯得更加重要。不同編譯器調(diào)試功能可能會(huì)有不同,但調(diào)試的方法是相同的。
END
總結(jié)
- 上一篇: ZXing.Net条形码二维码标签编辑打
- 下一篇: 阿里云被攻击封多久,又该怎么解决?