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