GDB 变量查看:print、display 与 watch

前言

在启动调试以及设置断点之后,就到了我们非常关键的一步——查看变量。GDB 调试最大的目的之一就是走查代码,查看运行结果是否符合预期。既然如此,我们就不得不了解一些查看各种类型变量的方法,以帮助我们进一步定位问题。

准备工作

本文辅助说明程序如下:

//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 默认的打印方式已经足够了,它会根据变量类型的格式打印出来,但是有时候这还不够,我们需要更多的格式控制。常见格式控制字符如下:

还是以辅助程序来说明,正常方式打印字符数组 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

其中:

单元类型常见有如下:

我们通过一个实例来看,假如我们要把 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时去使能所有

查看寄存器内容

(gdb) info registers
rax            0x0    0
rbx            0x0    0
rcx            0x7ffff7dd1b00    140737351850752
rdx            0x0    0
rsi            0x7ffff7dd1b30    140737351850800
rdi            0xffffffff    4294967295
rbp            0x7fffffffdc10    0x7fffffffdc10
(内容过多未显示完全)

总结

通过不同方式查看变量值或者内存值能够极大的帮助我们判断程序的运行是否符合我们的预期,如果发现观察的值不是我们预期的时候,就需要检查我们的代码了。


   转载规则


《GDB 变量查看:print、display 与 watch》 由 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
GDB 查看线程信息 GDB 查看线程信息
例子#include <stdio.h> #include <pthread.h> void *thread_func(void *p_arg) { while (1) { printf("%s\
2021-04-13
下一篇 
GDB 常用命令速查 GDB 常用命令速查
gdb 是一个在 UNIX 环境下的命令行调试工具。如果需要使用 gdb 调试程序,请在 gcc 时加上 -g 选项。下面的命令部分是简化版,比如使用 l 代替 list 等等。 1. 基本命令1)进入GDB: # gdb test tes
2021-03-20
  目录