亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

通過GDB學習C語言的講解

2020-01-26 13:33:42
字體:
來源:轉載
供稿:網友

對于那些具有高級編程語言諸如: Ruby、Scheme、Haskell 等背景的人來說,學習 C 語言是具有挑戰性的。除了糾結于 C  語言中像手動內存管理和指針等底層特性外,你必須在沒有 REPL ( Read-Eval-Print Loop ) 的條件下完成工作。一旦你已經習慣于在 REPL 環境下進行探索性的編程,必須進行“編寫-編譯-運行”這樣循環實在有點令人生厭。

最近我發現其實可以用 GDB 來作為 C 語言的偽 REPL。我一直嘗試使用 GDB 作為學習 C 語言的工具,而不僅僅是用來調試 C 程序,事實上這非常有趣。

這篇文章我的目的就是向你展示 GDB 是一個非常好的學習 C 語言工具。下面我將會向你介紹一些我最喜歡的 GDB 命令,然后我會向你闡述怎樣使用 GDB 來理解 C 語言中一個出了名的復雜問題:數組和指針的區別。

GDB 簡介

從創建一個簡單的 C 程序開始,minimal.c:

int main(){  int i = 1337;  return 0;}

注意這個程序并沒有做任何事情,也沒有一條輸出指令。擁抱使用 GDB 學習 C 語言的美麗新世界吧!

使用 -g 參數進行編譯,這樣會生成一些有助于 debug,gdb 可以利用的信息,編譯后用 GDB 運行起來:

$ gcc -g minimal.c -o minimal$ gdb minimal

你現在應該能看到明顯的 GDB 提示行。我之前告訴你這是一個 REPL,下面我們就來試試:

(gdb) print 1 + 2$1 = 3

多么神奇! print 是 GDB 的內置命令,他能夠打印出一個 C 語言命令的返回值。如果你不確定一個 GDB 命令是做什么,嘗試在 GDB 提示下運行命令 help。

然后是一個更有趣的例子:

(gbd) print (int) 2147483648$2 = -2147483648

這里我先忽略為什么 2147483648 == -2147483648;我想要說明的是即使是算術運算在 C 語言中也是有很多坑的,GDB 能夠理解運行 C 語言中的算術運算。

現在讓我們在主函數中設置一個斷點然后運行程序:

(gdb) break main(gdb) run

現在程序在第 3 行處暫停,正好在 i 進行初始化之前。有趣的是,盡管 i 還沒有被初始化,我們依然能夠使用 print 命令看到它的值。

(gdb) print i$3 = 32767

在C語言中,一個未被初始化的局部變量的值是沒有定義的,所以你用 GDB 打印出的值可能與這里的不一樣。

我們可以用 next 命令來執行當前斷點這一行:

(gdb) next(gdb) print i$4 = 1337

使用x 命令檢查內存

在C語言中變量用來標示一塊連續的內存區間。一個變量的內存區間由兩個數字決定:

這塊內存第一個字節數的數值地址

內存的大小,單位是字節。變量所占內容的大小取決于變量的類型。

C 語言中一個獨特的特性是你能夠直接訪問變量所占的內存。操作符 & 可以計算一個變量的地址,操作符 sizeof 計算變量所占內存的大小。

你可以在 GDB 中測試以上兩個概念:

(gdb) print &i$5 = (int *) 0x7fff5fbff584(gdb) print sizeof(i)$6 = 4

字面上看,i 所占內存起始于地址 0x7fff5fbff5b4,占內存 4 個字節。

我前面提到的變量在內存中的大小取決于它的類型,所以操作符 sizeof 能夠直接作用于類型:

(gdb) print sizeof(int)$7 = 4(gdb) print sizeof(double)$8 = 8

以上顯示意味著,至少在我的計算機上 int 變量占 4 個字節空間,double 變量占 8 個字節。

GDB 帶來了一個功能強大的工具,能夠直接檢測內存:x 命令。x 命令從一個特定的地址開始檢測內存。結合一些結構化的命令和這些已給的命令能精確控制你想檢測多少字節,你想怎樣打印它們。當你有疑問時,嘗試在 GDB 提示下運行 help x。

& 操作符計算變量的地址,這意味著我們能將 &i 返回給 x,從而看到 i 值背后原始的字節。

(gdb) x/4xb &i0x7fff5fbff584: 0x39  0x05  0x00  0x00

標識參數表示我想要檢查 4 個值,格式是十六進制,一次顯示一個字節。我選擇檢查 4 個字節,是因為 i 在內存中的大小是 4 字節;逐字節打印出 i 在內存中的表示。

在 Intel 機器上有一個坑應當記得,逐字節檢測時字節數是以“小端”順序保存:不像人類一般使用的標記方法,一個數字的低位在內存中排在前面(個位數在十位數之前)。

為了讓這個問題更加明顯,我們可以為 i 賦一個特別的值,然后重新檢測所占內存。

(gdb) set var i = 0x12345678(gdb) x/4xb &i0x7fff5fbff584: 0x78 0x56 0x34 0x12

使用 ptype 檢查類型

ptype 命令可能是我最喜愛的命令。它告訴你一個 C 語言表達式的類型。

(gdb) ptype itype = int(gdb) ptype &itype = int *(gdb) ptype maintype = int (void)

C 語言中的類型可以變得很復雜,但是好在 ptype 允許你交互式地查看他們。

指針和數組

數組在C語言中是非常難以捉摸的概念。這節的計劃是寫出一個簡單的程序,然后在 GDB 中運行,直至它的意義變得清晰易懂。

編寫如下的程序,array.c:

int main(){  int a[] = {1,2,3};  return 0;}

使用 -g 作為命令行參數進行編譯,在 GDB 中運行,然后輸入 next,執行初始化那一行

$ gcc -g arrays.c -o arrays$ gdb arrays(gdb) break main(gdb) run(gdb) next

在這里,你應該能夠打印出 a 的內容并檢查它的類型:

(gdb) print a$1 = {1, 2, 3}(gdb) ptype atype = int [3]

現在我們的程序已經在 GDB 中運行起來了,我們應該做的第一件事是使用 x 看看 a 在內存中是什么樣子。

(gdb) x/12xb &a0x7fff5fbff56c: 0x01 0x00 0x00 0x00 0x02 0x00 0x00 0x000x7fff5fbff574: 0x03 0x00 0x00 0x00

以上意思是 a 所占內存開始于地址 0x7fff5fbff5dc。起始的四個字節存儲 a[0], 隨后的四個字節存儲 a[1], 最后的四個字節存儲 a[2]。事實上你可以通過 sizeof 得到,a 在內存中的大小是 12 字節。

(gdb) print sizeof(a)$2 = 12

現在,數組好像確實有個數組的樣子。他們有自己的數組類型,在連續的內存空間中存儲自己的成員。然而在某些情況下,數組表現得更像指針。例如,我們能在 a 上進行指針運算。

= preserve do :escaped  (gdb) print a + 1  $3 = (int *) 0x7fff5fbff570

字面上看,a+1 是一個指向 int 的指針,占據地址 0x7fff5fbff570。這時,你應該反過來將指針傳遞給 x 命令,讓我們看看會發生什么:

= preserve do :escaped  (gdb) x/4xb a + 1  0x7fff5fbff570: 0x02 0x00 0x00 0x00

注意 0x7fff5fbff570 比 0x7fff5fbff56c 大 4,后者是 a 在內存地址中的第一個字節??紤]到 int 值占 4 字節,這意味著 a+1 指向 a[1].

事實上,在 C 語言中數組索引是指針運算的語法糖: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

我們已經看到在某些情況下,a 表現的像一個數組,在另一些情況下表現得像一個指向它首元素的指針。接下來會發生什么呢?

答案是當一個數組名在 C 語言表達式中使用時,它“退化”成指向這個數組首元素的指針。這個規則只有兩個例外:當數組名傳遞給 sizeof 時,當數組名傳遞給操作數 & 時。

事實上,a 在傳遞給操作數 & 時并沒有“退化”成一個指針,這就帶來一個有趣的問題:由“退化”變成的指針和 &a 存在區別嗎?

數值上講,他們都表示相同的地址:

= preserve do :escaped  (gdb) x/4xb a  0x7fff5fbff56c: 0x01 0x00 0x00 0x00  (gdb) x/4xb &a  0x7fff5fbff56c: 0x01 0x00 0x00 0x00

然而,他們的類型是不同的。我們已經看到 a 退化的值是指向 a首元素的指針;這個必須是類型 int *。對于類型 &a,我們可以直接詢問 GDB:

= preserve do :escaped  (gdb) ptype &a  type = int (*)[3]

從顯示上看,&a 是一個指向 3 個整數數組的指針。這就說明:當傳遞給 & 時,a 沒有退化,a 有了一個類型,是 int[3]。

通過測試他們在指針運算時的表現,你可以觀察到 a 的退化值和 &a 的明顯區別。

= 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

結論

希望我已經向你證明 GDB 是學習 C 語言的一個靈巧而有富有探索性的環境。你能使用 print 打印表達式的值,使用 x 查看內存中原始字節,使用 ptype 配合類型系統進行問題修補。

如果你想要進一步對使用 GDB 學習 C 語言進行嘗試,我有一些建議如下:

1.用 gdb 通過 Ksplice 指針挑戰。

2.研究結構體是怎樣在內存中存儲的? 他們與數組比較又有什么異同?

3.使用 GDB 的 disassemble 命令學習匯編語言!一個特別有趣的練習是研究函數調用棧是如何工作的。

4.試試 GDB 的 “ tui ”模式,這個模式在常規 GDB 頂層提供一個圖像化的 ncurses 層(Ncurses 提供字符終端處理庫,包括面板和菜單)。在 OS X 系統中,你可能需要用源代碼安裝 GDB。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對武林網的支持。如果你想了解更多相關內容請查看下面相關鏈接

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产97色在线| 92福利视频午夜1000合集在线观看| 欧美成人黑人xx视频免费观看| 亚洲精品久久久久久下一站| 国产区精品视频| 亚洲午夜久久久久久久| 视频直播国产精品| 欧美多人爱爱视频网站| 色久欧美在线视频观看| 在线播放国产一区二区三区| 国产精品1234| 亚洲丁香婷深爱综合| 国产婷婷97碰碰久久人人蜜臀| 亚洲天堂男人天堂女人天堂| 久久精品久久久久久| 亚洲性av在线| 91在线视频免费| 热99精品里视频精品| 久久天天躁狠狠躁夜夜躁| 91精品免费久久久久久久久| 欧美一区在线直播| 亚洲www在线| 久久久影视精品| 国产美女91呻吟求| 国产日韩欧美在线播放| 国产精品美女久久久久av超清| 国产精品视频自在线| 日韩av一区二区在线| 欧美老女人xx| 欧美成人合集magnet| 成人a级免费视频| 亚洲国产一区自拍| 亚洲最新av在线网站| 国产免费一区二区三区香蕉精| 国产欧美va欧美va香蕉在| 日韩av一区二区在线观看| 亚洲综合色激情五月| 狠狠躁18三区二区一区| 久久精视频免费在线久久完整在线看| 91国产精品电影| 亚洲国产精品福利| 亚洲欧美精品一区二区| 精品亚洲男同gayvideo网站| 51精品国产黑色丝袜高跟鞋| 菠萝蜜影院一区二区免费| 欧美亚洲午夜视频在线观看| 夜夜嗨av色一区二区不卡| 久久精品91久久久久久再现| 91深夜福利视频| 久久夜精品香蕉| 久久国产精品久久久久| 伊人一区二区三区久久精品| 欧美激情精品久久久久久变态| 日韩av网站导航| 国产精品黄页免费高清在线观看| 亚洲黄色www网站| 色偷偷av一区二区三区| 亚洲一区二区三区毛片| 66m—66摸成人免费视频| 丝袜情趣国产精品| 一区二区三区亚洲| 成人国产精品色哟哟| 精品成人69xx.xyz| 日本免费在线精品| 中文字幕精品www乱入免费视频| 国产va免费精品高清在线| 国产精品久久久久福利| 国内精品视频一区| 精品一区二区三区四区| 久久国产精品偷| 亚洲人成在线一二| 欧美影院成年免费版| 国产精品va在线| 久久精品国产99国产精品澳门| 国产精品露脸av在线| 亚洲人成网站在线播| 久久国产精品久久国产精品| 热re91久久精品国99热蜜臀| 成人久久久久爱| 亚洲精品一区二区网址| 精品久久久久久中文字幕一区奶水| 欧美在线激情网| 日韩av电影国产| 欧美成人免费全部观看天天性色| 午夜精品一区二区三区视频免费看| 亚洲精品视频免费在线观看| 亚洲欧美一区二区精品久久久| 国产精品视频成人| 久久综合久久八八| 精品日本美女福利在线观看| 国产日韩欧美影视| 日韩欧美主播在线| 久久久精品国产| 少妇高潮久久77777| 欧日韩在线观看| 欧美电影《睫毛膏》| 97精品国产97久久久久久免费| 国产视频在线观看一区二区| 中文字幕欧美视频在线| 久久久久久有精品国产| 性欧美暴力猛交69hd| 亚洲精品欧美极品| 日韩免费中文字幕| 2019中文字幕全在线观看| 欧美性猛交99久久久久99按摩| 欧美日韩xxxxx| 久久天堂av综合合色| 日韩男女性生活视频| 久久久亚洲精品视频| 欧美日韩精品国产| 亚洲激情第一页| 欧美在线免费观看| 麻豆国产va免费精品高清在线| 亚洲欧美一区二区激情| 国模视频一区二区三区| 日本电影亚洲天堂| 国产一区二区三区在线播放免费观看| 最近免费中文字幕视频2019| 国产精品影片在线观看| 国产精品久久久久久久久久小说| 久久频这里精品99香蕉| 97超碰蝌蚪网人人做人人爽| 中文字幕亚洲欧美日韩在线不卡| 久久av在线看| 精品久久久久久电影| 国产欧美日韩精品丝袜高跟鞋| 久久成人综合视频| 国产成人97精品免费看片| 久久影院在线观看| 国模视频一区二区三区| 亚洲少妇激情视频| 国产精品视频内| 久久久av亚洲男天堂| 亚洲成人a**站| 亚洲欧洲视频在线| 成人福利在线观看| 午夜精品一区二区三区视频免费看| 亚洲国产精品推荐| 亚洲美女视频网站| 丝袜亚洲另类欧美重口| 亚洲一区二区在线播放| 亚洲欧美国产一本综合首页| 狠狠色香婷婷久久亚洲精品| 亚洲片国产一区一级在线观看| 91最新国产视频| 欧美最顶级丰满的aⅴ艳星| 成人精品在线观看| 国内精品久久久久影院优| 国产美女扒开尿口久久久| 在线观看亚洲视频| 欧美性受xxxx黑人猛交| 国产欧美一区二区白浆黑人| 亚洲第一国产精品| 亚洲成人av在线| 精品一区电影国产| 欧美华人在线视频| 成人av.网址在线网站| 亚洲第一天堂无码专区| 日韩在线观看免费全| 57pao成人永久免费视频| 国产精品88a∨| 91久久国产精品| 国产日韩欧美在线观看| 久久精彩免费视频|