在啟動調試以及設置斷點之後,就到了我們非常關鍵的一步-查看變數。GDB調試最大的目的之一就是走查代碼,查看運行結果是否符合預期。既然如此,我們就不得不瞭解一些查看各種類型變數的方法,以幫助我們進一步定位問題。 ...
前言
在啟動調試以及設置斷點之後,就到了我們非常關鍵的一步-查看變數。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
(內容過多未顯示完全)
總結
通過不同方式查看變數值或者記憶體值能夠極大的幫助我們判斷程式的運行是否符合我們的預期,如果發現觀察的值不是我們預期的時候,就需要檢查我們的代碼了。