這篇文章將為大家詳細講解有關通過GDB學習C語言的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:國際域名空間、網(wǎng)站空間、營銷軟件、網(wǎng)站建設、金灣網(wǎng)站維護、網(wǎng)站推廣。GDB 簡介
從創(chuàng)建一個簡單的 C 程序開始,minimal.c:
int main() { int i = 1337; return 0; }
注意這個程序并沒有做任何事情,也沒有一條輸出指令。擁抱使用 GDB 學習 C 語言的美麗新世界吧!
使用 -g 參數(shù)進行編譯,這樣會生成一些有助于 debug,gdb 可以利用的信息,編譯后用 GDB 運行起來:
$ gcc -g minimal.c -o minimal $ gdb minimal
你現(xiàn)在應該能看到明顯的 GDB 提示行。我之前告訴你這是一個 REPL,下面我們就來試試:
(gdb) print 1 + 2 $1 = 3
多么神奇! print 是 GDB 的內(nèi)置命令,他能夠打印出一個 C 語言命令的返回值。如果你不確定一個 GDB 命令是做什么,嘗試在 GDB 提示下運行命令 help。
然后是一個更有趣的例子:
(gbd) print (int) 2147483648 $2 = -2147483648
這里我先忽略為什么 2147483648 == -2147483648;我想要說明的是即使是算術運算在 C 語言中也是有很多坑的,GDB 能夠理解運行 C 語言中的算術運算。
現(xiàn)在讓我們在主函數(shù)中設置一個斷點然后運行程序:
(gdb) break main (gdb) run
現(xiàn)在程序在第 3 行處暫停,正好在 i 進行初始化之前。有趣的是,盡管 i 還沒有被初始化,我們依然能夠使用 print 命令看到它的值。
(gdb) print i $3 = 32767
在C語言中,一個未被初始化的局部變量的值是沒有定義的,所以你用 GDB 打印出的值可能與這里的不一樣。
我們可以用 next 命令來執(zhí)行當前斷點這一行:
(gdb) next (gdb) print i $4 = 1337
使用x 命令檢查內(nèi)存
在C語言中變量用來標示一塊連續(xù)的內(nèi)存區(qū)間。一個變量的內(nèi)存區(qū)間由兩個數(shù)字決定:
這塊內(nèi)存第一個字節(jié)數(shù)的數(shù)值地址
內(nèi)存的大小,單位是字節(jié)。變量所占內(nèi)容的大小取決于變量的類型。
C 語言中一個獨特的特性是你能夠直接訪問變量所占的內(nèi)存。操作符 & 可以計算一個變量的地址,操作符 sizeof 計算變量所占內(nèi)存的大小。
你可以在 GDB 中測試以上兩個概念:
(gdb) print &i $5 = (int *) 0x7fff5fbff584 (gdb) print sizeof(i) $6 = 4
字面上看,i 所占內(nèi)存起始于地址 0x7fff5fbff5b4,占內(nèi)存 4 個字節(jié)。
我前面提到的變量在內(nèi)存中的大小取決于它的類型,所以操作符 sizeof 能夠直接作用于類型:
(gdb) print sizeof(int) $7 = 4 (gdb) print sizeof(double) $8 = 8
以上顯示意味著,至少在我的計算機上 int 變量占 4 個字節(jié)空間,double 變量占 8 個字節(jié)。
GDB 帶來了一個功能強大的工具,能夠直接檢測內(nèi)存:x 命令。x 命令從一個特定的地址開始檢測內(nèi)存。結合一些結構化的命令和這些已給的命令能精確控制你想檢測多少字節(jié),你想怎樣打印它們。當你有疑問時,嘗試在 GDB 提示下運行 help x。
& 操作符計算變量的地址,這意味著我們能將 &i 返回給 x,從而看到 i 值背后原始的字節(jié)。
(gdb) x/4xb &i 0x7fff5fbff584: 0x39 0x05 0x00 0x00
標識參數(shù)表示我想要檢查 4 個值,格式是十六進制,一次顯示一個字節(jié)。我選擇檢查 4 個字節(jié),是因為 i 在內(nèi)存中的大小是 4 字節(jié);逐字節(jié)打印出 i 在內(nèi)存中的表示。
在 Intel 機器上有一個坑應當記得,逐字節(jié)檢測時字節(jié)數(shù)是以“小端”順序保存:不像人類一般使用的標記方法,一個數(shù)字的低位在內(nèi)存中排在前面(個位數(shù)在十位數(shù)之前)。
為了讓這個問題更加明顯,我們可以為 i 賦一個特別的值,然后重新檢測所占內(nèi)存。
(gdb) set var i = 0x12345678 (gdb) x/4xb &i 0x7fff5fbff584: 0x78 0x56 0x34 0x12
使用 ptype 檢查類型
ptype 命令可能是我最喜愛的命令。它告訴你一個 C 語言表達式的類型。
(gdb) ptype i type = int (gdb) ptype &i type = int * (gdb) ptype main type = int (void)
C 語言中的類型可以變得很復雜,但是好在 ptype 允許你交互式地查看他們。
指針和數(shù)組
數(shù)組在C語言中是非常難以捉摸的概念。這節(jié)的計劃是寫出一個簡單的程序,然后在 GDB 中運行,直至它的意義變得清晰易懂。
編寫如下的程序,array.c:
int main() { int a[] = {1,2,3}; return 0; }
使用 -g 作為命令行參數(shù)進行編譯,在 GDB 中運行,然后輸入 next,執(zhí)行初始化那一行
$ gcc -g arrays.c -o arrays $ gdb arrays (gdb) break main (gdb) run (gdb) next
在這里,你應該能夠打印出 a 的內(nèi)容并檢查它的類型:
(gdb) print a $1 = {1, 2, 3} (gdb) ptype a type = int [3]
現(xiàn)在我們的程序已經(jīng)在 GDB 中運行起來了,我們應該做的第一件事是使用 x 看看 a 在內(nèi)存中是什么樣子。
(gdb) x/12xb &a 0x7fff5fbff56c: 0x01 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x7fff5fbff574: 0x03 0x00 0x00 0x00
以上意思是 a 所占內(nèi)存開始于地址 0x7fff5fbff5dc。起始的四個字節(jié)存儲 a[0], 隨后的四個字節(jié)存儲 a[1], 最后的四個字節(jié)存儲 a[2]。事實上你可以通過 sizeof 得到,a 在內(nèi)存中的大小是 12 字節(jié)。
(gdb) print sizeof(a) $2 = 12
現(xiàn)在,數(shù)組好像確實有個數(shù)組的樣子。他們有自己的數(shù)組類型,在連續(xù)的內(nèi)存空間中存儲自己的成員。然而在某些情況下,數(shù)組表現(xiàn)得更像指針。例如,我們能在 a 上進行指針運算。
= preserve do :escaped (gdb) print a + 1 $3 = (int *) 0x7fff5fbff570
字面上看,a+1 是一個指向 int 的指針,占據(jù)地址 0x7fff5fbff570。這時,你應該反過來將指針傳遞給 x 命令,讓我們看看會發(fā)生什么:
= preserve do :escaped (gdb) x/4xb a + 1 0x7fff5fbff570: 0x02 0x00 0x00 0x00
注意 0x7fff5fbff570 比 0x7fff5fbff56c 大 4,后者是 a 在內(nèi)存地址中的第一個字節(jié)??紤]到 int 值占 4 字節(jié),這意味著 a+1 指向 a[1].
事實上,在 C 語言中數(shù)組索引是指針運算的語法糖:a[i] 等于 *(a+i)。你可以在 GDB 中嘗試一下。
= preserve do :escaped (gdb) print a[0] $4 = 1 (gdb) print *(a + 0) $5 = 1 (gdb) print a[1] $6 = 2 (gdb) print *(a + 1) $7 = 2 (gdb) print a[2] $8 = 3 (gdb) print *(a + 2) $9 = 3
我們已經(jīng)看到在某些情況下,a 表現(xiàn)的像一個數(shù)組,在另一些情況下表現(xiàn)得像一個指向它首元素的指針。接下來會發(fā)生什么呢?
答案是當一個數(shù)組名在 C 語言表達式中使用時,它“退化”成指向這個數(shù)組首元素的指針。這個規(guī)則只有兩個例外:當數(shù)組名傳遞給 sizeof 時,當數(shù)組名傳遞給操作數(shù) & 時。
事實上,a 在傳遞給操作數(shù) & 時并沒有“退化”成一個指針,這就帶來一個有趣的問題:由“退化”變成的指針和 &a 存在區(qū)別嗎?
數(shù)值上講,他們都表示相同的地址:
= preserve do :escaped (gdb) x/4xb a 0x7fff5fbff56c: 0x01 0x00 0x00 0x00 (gdb) x/4xb &a 0x7fff5fbff56c: 0x01 0x00 0x00 0x00
然而,他們的類型是不同的。我們已經(jīng)看到 a 退化的值是指向 a首元素的指針;這個必須是類型 int *。對于類型 &a,我們可以直接詢問 GDB:
= preserve do :escaped (gdb) ptype &a type = int (*)[3]
從顯示上看,&a 是一個指向 3 個整數(shù)數(shù)組的指針。這就說明:當傳遞給 & 時,a 沒有退化,a 有了一個類型,是 int[3]。
通過測試他們在指針運算時的表現(xiàn),你可以觀察到 a 的退化值和 &a 的明顯區(qū)別。
= preserve do :escaped (gdb) print a + 1 $10 = (int *) 0x7fff5fbff570 (gdb) print &a + 1 $11 = (int (*)[3]) 0x7fff5fbff578
注意到對 a 增加 1 等于對 a 的地址增加 4,與此同時,對 &a 增加 1 等于對 a 的地址增加 12!
實際上 a 退化成的指針是 &a[0];
= preserve do :escaped (gdb) print &a[0] $11 = (int *) 0x7fff5fbff56c
結論
希望我已經(jīng)向你證明 GDB 是學習 C 語言的一個靈巧而有富有探索性的環(huán)境。你能使用 print 打印表達式的值,使用 x 查看內(nèi)存中原始字節(jié),使用 ptype 配合類型系統(tǒng)進行問題修補。
如果你想要進一步對使用 GDB 學習 C 語言進行嘗試,我有一些建議如下:
1.用 gdb 通過 Ksplice 指針挑戰(zhàn)。
2.研究結構體是怎樣在內(nèi)存中存儲的? 他們與數(shù)組比較又有什么異同?
3.使用 GDB 的 disassemble 命令學習匯編語言!一個特別有趣的練習是研究函數(shù)調(diào)用棧是如何工作的。
4.試試 GDB 的 “ tui ”模式,這個模式在常規(guī) GDB 頂層提供一個圖像化的 ncurses 層(Ncurses 提供字符終端處理庫,包括面板和菜單)。在 OS X 系統(tǒng)中,你可能需要用源代碼安裝 GDB。
關于“通過GDB學習C語言的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)建站www.rwnh.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
當前題目:通過GDB學習C語言的示例分析-創(chuàng)新互聯(lián)
標題URL:http://www.rwnh.cn/article36/cseipg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、面包屑導航、網(wǎng)站策劃、用戶體驗、外貿(mào)網(wǎng)站建設、網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容