優(yōu)秀程序猿調(diào)試技巧Debug與Release
Bug
bug意為臭蟲,計(jì)算機(jī)術(shù)語(yǔ)里就是幺蛾子,對(duì),你的程序又出幺蛾子了。為什么要叫bug?關(guān)于這個(gè)還有段有趣的歷史
有一天赫柏正愉快地敲著Mark Ⅱ的代碼時(shí),計(jì)算機(jī)突然就停止運(yùn)作了,那時(shí)的計(jì)算機(jī)遠(yuǎn)不如現(xiàn)在小巧,赫柏他們只能一個(gè)個(gè)排查計(jì)算機(jī)龐大的處理器群,經(jīng)過(guò)一段時(shí)間的排查后停機(jī)原因終于被找到了。原來(lái)是一只飛蛾被計(jì)算機(jī)的光和熱吸引,觸發(fā)了電腦的短路,當(dāng)然這只可憐的飛蛾也一命嗚呼了按理說(shuō)一般人也就是把飛蛾拿走,然后重啟下電腦也就完事了,但赫柏顯然不是一般人她小心翼翼地把這只飛蛾拿了下來(lái),然后把它工工整整地粘在了記事本上… …
這就是歷史上第一個(gè) bug 的誕生。
調(diào)試的重要性
我估計(jì)前期我們找 bug 都是用眼睛瞅,特別是我們這種大一的剛接觸的,現(xiàn)在還好,到了以后需要寫大工程的時(shí)候,眼瞅不頭疼的才是大哥,對(duì)于一個(gè)成熟程序員20%時(shí)間寫代碼而80%時(shí)間在調(diào)試代碼。
我們寫代碼就是一個(gè)推理的過(guò)程,整個(gè)流程的正確與錯(cuò)誤都是有跡可循的,推理的途徑就是這些跡象。
一名優(yōu)秀的程序員就是一個(gè)優(yōu)秀的偵探,找到跡象,順流而下是錯(cuò)誤,順流而上是真相,調(diào)試就是我們破案的過(guò)程。
調(diào)試基本步驟
1.找錯(cuò)(進(jìn)行隔離,消除來(lái)定位錯(cuò)誤)
2.知道錯(cuò)因
3.尋找解決辦法
4.糾正,重測(cè)
Debug與Release
Debug(Debugging),即排錯(cuò),稱為調(diào)試版本,不作任何優(yōu)化,包含調(diào)試信息,便于我們調(diào)試程序。
Release ,即釋放,成為測(cè)試版本,往往進(jìn)行各種優(yōu)化,讓代碼在大小和運(yùn)行速度上都是最優(yōu)的,面向用戶,可以很好的使用。但是!注意Release版本是沒(méi)法進(jìn)行調(diào)試的,這種觀點(diǎn)僅限于我當(dāng)前知識(shí)面的限制,實(shí)際上Release也是可以的,下面是大佬對(duì)我的指正意見(jiàn):
快捷鍵
在調(diào)試過(guò)程中,掌握一些快捷鍵會(huì)大大增加我們的效率。以vs2019為例,我們先會(huì)設(shè)置斷點(diǎn)如圖(行標(biāo)左側(cè)設(shè)置)
斷點(diǎn)設(shè)置在需檢查代碼的任意位置,運(yùn)行到這一步就會(huì)停下給我們報(bào)告。斷點(diǎn)完F5調(diào)試,執(zhí)行窗口彈出后就會(huì)發(fā)現(xiàn)調(diào)試就會(huì)出現(xiàn)更多內(nèi)容,我框出來(lái)的在之前記錄C語(yǔ)言學(xué)習(xí)時(shí)都有用到。
注意F5是調(diào)試,Ctrl+F5是運(yùn)行,通常會(huì)使用會(huì)F5跳到想要的斷點(diǎn)處,有些電腦上比較裝怪,快捷鍵沒(méi)反應(yīng)的,建議多按一個(gè)Fn鍵試試,F(xiàn)n是功能輔助鍵,相當(dāng)于一個(gè)開(kāi)關(guān),本質(zhì)上 F5+Fn = F5。需要強(qiáng)調(diào)的是逐語(yǔ)句和逐過(guò)程,如果你想看每個(gè)細(xì)節(jié),不放過(guò)每一個(gè)角落就用逐語(yǔ)句,兩者的力度是不一樣的,逐過(guò)程會(huì)跳過(guò)代碼里的函數(shù)部分。
vs玩家重點(diǎn)推薦 Ctrl+k+c,注釋選中行;Ctrl+k+u,取消注釋,熟練運(yùn)用會(huì)很方便。
其余還有很多不贅述,下面準(zhǔn)備了超全的實(shí)用快捷鍵用法:
除了用調(diào)試驗(yàn)證代碼的正確性,還可以用于研究具體的問(wèn)題,舉個(gè)栗子:
這道題是Nice的面試真題
請(qǐng)說(shuō)明下面代碼是否能正常運(yùn)?運(yùn)行結(jié)果是什么?為什么會(huì)出現(xiàn)這個(gè)結(jié)果?
int main() { int i = 0; int arr[10] = { 0 }; for (i = 0; i <= 12; i++) { arr[i] = 0; printf(“hehe\n”); } return 0; }
這里當(dāng)我們打開(kāi)調(diào)試窗口直接開(kāi)調(diào):
很直觀的可以發(fā)現(xiàn),哦,原來(lái)是i與arr[12]相同,我們發(fā)現(xiàn)在arr[12]改變時(shí)i第的值也會(huì)隨之改變,那我就直接取出他們都地址看一看是不是一樣的。
OMG,是一樣的。但我們這里的調(diào)試只能看到現(xiàn)象,他底層的原理我們要自己思考。
其死循環(huán)的邏輯大致是這樣的,我們創(chuàng)建了一個(gè)變量i,arr,他們都是局部變量,而局部變量時(shí)放在棧上的,棧區(qū)上內(nèi)存使用習(xí)慣是先使用高地址存儲(chǔ)空間,再使用低地址。這里注意,我們開(kāi)始給的十個(gè)大小的空間,i的變量是到12,這里明顯是越界訪問(wèn),但為什么沒(méi)有報(bào)錯(cuò)停下來(lái)?結(jié)合我們剛剛監(jiān)視的結(jié)果,我們?cè)侔迅窬执蜷_(kāi):
數(shù)組隨著下標(biāo)的增長(zhǎng),地址是由低到高的變化,在我數(shù)組適當(dāng)越界時(shí),如果i和arr之間的空間適當(dāng)?shù)脑?,就有可能使arr向后越界時(shí)就訪問(wèn)到了i,造成了循環(huán)變量的i改變,最終會(huì)死循環(huán)。
這種錯(cuò)誤其實(shí)存在偶然性,首先i和arr[12]相同,只是恰巧,但如果我把i換成11,結(jié)果就大相徑庭了,我只形成了越界但沒(méi)有改變循環(huán)變量i的值。其次,這個(gè)代碼是嚴(yán)重依賴環(huán)境的,比如在VC 6.0里面i和arr就是連續(xù)的,gcc里面i和arr之間有一個(gè)空間。
打趣的是,我們?cè)赗elease版本里面是不會(huì)報(bào)錯(cuò)并且會(huì)停下來(lái),其實(shí)在剛剛的截圖里面也是會(huì)報(bào)錯(cuò)的,但是!死循環(huán)停不下來(lái),他根本沒(méi)時(shí)間來(lái)報(bào)錯(cuò)。Rlease的優(yōu)化并不是萬(wàn)能的,不要期待利用Release版本來(lái)掩蓋代碼的bug,最好的做法就是不要越界。
如何寫出易于調(diào)試(優(yōu)秀)的代碼
1.硬性要求運(yùn)行正常
2.bug少(估計(jì)沒(méi)人敢保證零bug吧)
3.效率高
4.可讀性高
5.可維護(hù)性(容易修改與二創(chuàng))
6.注釋(方便閱讀)
7.文檔齊全
常見(jiàn)的coding技巧
1.使用assert
意為斷言,在代碼執(zhí)行前設(shè)的前哨,比如我們函數(shù)傳參時(shí),當(dāng)我傳的內(nèi)容變成空指針,后面如果函數(shù)進(jìn)行解引用操作,對(duì)于空指針解引用是會(huì)造成程序崩潰的,是很危險(xiǎn)的,所以我們用assert當(dāng)監(jiān)護(hù)人能快一步該訴我們問(wèn)題在這里;設(shè)置assert也是一個(gè)好習(xí)慣,面試官見(jiàn)了直呼老司機(jī)!
# include<assert.h> void my_str(char* a, char* b) { assert(a != NULL && b != NULL);//斷言 while (*b != '\0') { *a = *b; b++; a++; } *a = *b; } int main() { char arr[10] = {0}; char arr2[] = "bit"; my_str(NULL, arr2); // 故意設(shè)成NULL程序會(huì)崩潰 printf("%s\n", arr); return 0; }
效果如上圖就會(huì)顯示斷言失敗。
2.盡量使用const
const常變量修飾符。
就上面模擬strcpy函數(shù),如果有天有個(gè)內(nèi)鬼改了你的代碼,寫成 *b = *a,就拷反了,編譯器也會(huì)傻不拉嘰的輸出,盡管結(jié)果什么都沒(méi)有。那怎么辦呢?我在開(kāi)頭就定義好
char* my_str(const char *a,const char* b)
這樣不管你咋改,我 *a,*b都是無(wú)法改變的。
注意const int *p=&a , const 在 * 左邊時(shí),修飾的是指針指向的內(nèi)容,指針變量不影響 ;在 * 右邊是,修飾指針本身,指針變量不能修改,其內(nèi)容可以通過(guò)指針來(lái)改變。
3,形成良好的編碼風(fēng)格
4.注釋!注釋!注釋?。ê昧?xí)慣講三次)
5.避免編碼陷阱
今天就到這里了,摸了家人們,更多關(guān)于Debug與Release調(diào)試技巧的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用let's?encrypt申請(qǐng)免費(fèi)的SSL證書
這篇文章主要為大家介紹了如何使用let's?encrypt申請(qǐng)免費(fèi)的SSL證書示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05為Jenkins創(chuàng)建定時(shí)構(gòu)建任務(wù)
這篇文章介紹了為Jenkins創(chuàng)建定時(shí)構(gòu)建任務(wù)的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03Git基礎(chǔ)學(xué)習(xí)之分支操作的示例詳解
這篇文章主要為大家詳細(xì)介紹了Git基礎(chǔ)學(xué)習(xí)中分支的基本操作,文中的示例代碼講解詳細(xì),對(duì)我們了解Git有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-10-10vs?code開(kāi)發(fā)中語(yǔ)法正確但顯示報(bào)錯(cuò)問(wèn)題分析解決
這篇文章主要為大家介紹了vs?code開(kāi)發(fā)中語(yǔ)法正確但顯示報(bào)錯(cuò)問(wèn)題分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06VSCode gdb 調(diào)試 qemu u-boot 的方法詳解
這篇文章主要介紹了VSCode gdb 調(diào)試 qemu u-boot 的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06