前言
在啟動調試以及設置斷點之后,就到了我們非常關鍵的一步-查看變量。GDB調試最大的目的之一就是走查代碼,查看運行結果是否符合預期。既然如此,我們就不得不了解一些查看各種類型變量的方法,以幫助我們進一步定位問題。
准備工作
在查看變量之前,需要先啟動調試並設置斷點,該部分內容可參考《GDB調試指南-啟動調試》和《GDB調試指南-斷點設置》。后面的內容都基於在某個位置已經斷住。
本文輔助說明程序如下:
testGdb.c
//testGdb.c
#include<stdio.h>
#include<stdlib.h>
#include"testGdb.h"
int main(void)
{
int a = 10; //整型
int b[] = {1,2,3,5}; //數組
char c[] = "hello,shouwang";//字符數組
/*申請內存,失敗時退出*/
int *d = (int*)malloc(a*sizeof(int));
if(NULL == d)
{
printf("malloc error\n");
return -1;
}
/*賦值*/
for(int i=0; i < 10;i++)
{
d[i] = i;
}
free(d);
d = NULL;
float e = 8.5f;
return 0;
}
testGdb.h
int a = 11;
編譯:
$ gcc -g -o testGdb testGdb.o
變量查看
打印基本類型變量,數組,字符數組
最常見的使用便是使用print(可簡寫為p)打印變量內容。
例如,打印基本類型,數組,字符數組等直接使用p 變量名即可:
(gdb) p a
$1 = 10
(gdb) p b
$2 = {1, 2, 3, 5}
(gdb) p c
$3 = "hello,shouwang"
(gdb)
當然有時候,多個函數或者多個文件會有同一個變量名,這個時候可以在前面加上文件名或者函數名來區分:
(gdb) p 'testGdb.h'::a
$1 = 11
(gdb) p 'main'::b
$2 = {1, 2, 3, 5}
(gdb)
這里所打印的a值是我們定義在testGdb.h文件里的,而b值是main函數中的b。
打印指針指向內容
如果還是使用上面的方式打印指針指向的內容,那么打印出來的只是指針地址而已,例如:
(gdb) p d
$1 = (int *) 0x602010
(gdb)
而如果想要打印指針指向的內容,需要解引用:
(gdb) p *d
$2 = 0
(gdb) p *d@10
$3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
(gdb)
從上面可以看到,僅僅使用*只能打印第一個值,如果要打印多個值,后面跟上@並加上要打印的長度。
或者@后面跟上變量值:
(gdb) p *d@a
$2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
(gdb)
由於a的值為10,並且是作為整型指針數據長度,因此后面可以直接跟着a,也可以打印出所有內容。
另外值得一提的是,$可表示上一個變量,而假設此時有一個鏈表linkNode,它有next成員代表下一個節點,則可使用下面方式不斷打印鏈表內容:
(gdb) p *linkNode
(這里顯示linkNode節點內容)
(gdb) p *$.next
(這里顯示linkNode節點下一個節點的內容)
如果想要查看前面數組的內容,你可以將下標一個一個累加,還可以定義一個類似UNIX環境變量,例如:
(gdb) set $index=0
(gdb) p b[$index++]
$11 = 1
(gdb) p b[$index++]
$12 = 2
(gdb) p b[$index++]
$13 = 3
這樣就不需要每次修改下標去打印啦。
按照特定格式打印變量
對於簡單的數據,print默認的打印方式已經足夠了,它會根據變量類型的格式打印出來,但是有時候這還不夠,我們需要更多的格式控制。常見格式控制字符如下:
- x 按十六進制格式顯示變量。
- d 按十進制格式顯示變量。
- u 按十六進制格式顯示無符號整型。
- o 按八進制格式顯示變量。
- t 按二進制格式顯示變量。
- a 按十六進制格式顯示變量。
- c 按字符格式顯示變量。
- f 按浮點數格式顯示變量。
還是以輔助程序來說明,正常方式打印字符數組c:
(gdb) p c
$18 = "hello,shouwang"
但是如果我們要查看它的十六進制格式打印呢?
(gdb) p/x c
$19 = {0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x73, 0x68, 0x6f, 0x75, 0x77, 0x61,
0x6e, 0x67, 0x0}
(gdb)
但是如果我們想用這種方式查看浮點數的二進制格式是怎樣的是不行的,因為直接打印它首先會被轉換成整型,因此最終會得到8:
(gdb) p e
$1 = 8.5
(gdb) p/t e
$2 = 1000
(gdb)
那么就需要另外一種查看方式了。
查看內存內容
examine(簡寫為x)可以用來查看內存地址中的值。語法如下:
x/[n][f][u] addr
其中:
- n 表示要顯示的內存單元數,默認值為1
- f 表示要打印的格式,前面已經提到了格式控制字符
- u 要打印的單元長度
- addr 內存地址
單元類型常見有如下:
- b 字節
- h 半字,即雙字節
- w 字,即四字節
- g 八字節
我們通過一個實例來看,假如我們要把float變量e按照二進制方式打印,並且打印單位是一字節:
(gdb) x/4tb &e
0x7fffffffdbd4: 00000000 00000000 00001000 01000001
(gdb)
可以看到,變量e的四個字節都以二進制的方式打印出來了。
自動顯示變量內容
假設我們希望程序斷住時,就顯示某個變量的值,可以使用display命令。
(gdb) display e
1: e = 8.5
那么每次程序斷住時,就會打印e的值。要查看哪些變量被設置了display,可以使用:
(gdb)info display
Auto-display expressions now in effect:
Num Enb Expression
1: y b
2: y e
如果想要清除可以使用
delete display num #num為前面變量前的編號,不帶num時清除所有。
或者去使能:
disable display num #num為前面變量前的編號,不帶num時去使能所有
微信公眾號【編程珠璣】:專注但不限於分享計算機編程基礎,Linux,C語言,C++,算法,數據庫等編程相關[原創]技術文章,號內包含大量經典電子書和視頻學習資源。歡迎一起交流學習,一起修煉計算機“內功”,知其然,更知其所以然。
公眾號編程珠璣
查看寄存器內容
(gdb)info registers
rax 0x0 0
rbx 0x0 0
rcx 0x7ffff7dd1b00 140737351850752
rdx 0x0 0
rsi 0x7ffff7dd1b30 140737351850800
rdi 0xffffffff 4294967295
rbp 0x7fffffffdc10 0x7fffffffdc10
(內容過多未顯示完全)
總結
通過不同方式查看變量值或者內存值能夠極大的幫助我們判斷程序的運行是否符合我們的預期,如果發現觀察的值不是我們預期的時候,就需要檢查我們的代碼了。