Skip to content

LV081-gdb-print

一、print

1. 语法格式

print 命令,它的功能就是在 GDB 调试程序的过程中,输出或者修改指定变量或者表达式的值。

Tips:所谓表达式,简单理解就是由多个变量构成的式子。例如 a、b、c 为单个变量,a+b、a+b*c 即为表达式。

print 命令可以缩写为 p,最常用的语法格式如下所示:

shell
# 输出 val 的值
(gdb) print val
(gdb) p val

# 修改 val的值为num
(gdb) print val=num
(gdb) p val=num

其中,参数 val 用来代指要查看或者修改的目标变量或者表达式。

2. 其他用法

print 命令还有更高级的功能和用法,例如以指定的格式输出变量或者表达式的值、输出数组中指定区间内的所有元素等等。和 print 命令最基本的用法相比,该命令的完整语法格式如下所示:

shell
(gdb) print [options --] [/fmt] expr

格式中用 [ ] 括起来的部分是可选的,可以使用也可以省略。

其中,各个参数的含义如下:

  • options:表示该命令所支持的选项,这些选项可以控制 print 命令输出指定内容的变量或者表达式的值;

  • fmt:指定输出变量或表达式值时所采用的格式;

  • expr:指定要查看的变量或表达式。

2.1 options

表 1 列举了常用的几个 options 参数值。

表 1 print options 参数的取值
options 参数 功 能
-address on|off 查看某一指针变量的值时,是否同时打印其占用的内存地址,默认值为 on。该选项等同于单独执行 set print address on|off 命令。
-array on|off 是否以便于阅读的格式输出数组中的元素,默认值为 off。该选项等同于单独执行 set printf array on|off 命令。
-array-indexes on|off 对于非字符类型数组,在打印数组中每个元素值的同时,是否同时显示每个元素对应的数组下标,默认值为 off。该选项等同于单独执行 set print array-indexes on|off 命令。
-pretty on|off 以便于阅读的格式打印某个结构体变量的值,默认值为 off。该选项等同于单独执行 set print pretty on|off 命令。

注意,options 参数和 /fmt 或者 expr 之间,必须用 --( 2 个 - 字符)分隔。此外,options 参数还有很多选项可以使用,可以 GDB 官网 查看。

2.2 fmt

print 命令可允许自定义输出格式,表 2 罗列了几个常用的 /fmt 参数:

表 2 /fmt 常用的值
/fmt 功 能
/x 以十六进制的形式打印出整数。
/d 以有符号、十进制的形式打印出整数。
/u 以无符号、十进制的形式打印出整数。
/o 以八进制的形式打印出整数。
/t 以二进制的形式打印出整数。
/f 以浮点数的形式打印变量或表达式的值。
/c 以字符形式打印变量或表达式的值。

当 print 命令不指定任何 options 参数时,print 和 /fmt 之间不用添加空格,例如以十六进制的形式输出 num 整形变量的值,执行命令为 (gdb) print/x num。

2.3 目标是表达式

当print指定目标表达式时,除了表达式本身外,GDB 调试器还支持使用@::运算符。

2.3.1 @

@运算符用于输出数组中指定区域的元素,使用格式如下:

shell
(gdb) print first@len

其中,参数 first 用于指定数组查看区域内的首个元素的值;参数 len 用于指令自 first 元素开始查看的元素个数。假设有一个 array 数组,其定义如下:

c
int array[5] = {1,2,3,4};

如果我们想查看第 1 个元素和第 2 个元素的值,可以执行如下指令:

shell
(gdb) print array[0]@2
$1 = {1, 2}
2.3.2 ::

当程序中包含多个作用域不同但名称相同的变量或表达式时,可以借助::运算符明确指定要查看的目标变量或表达式。::运算符的语法格式如下:

shell
(gdb) print file::variable
(gdb) print function::variable

其中 file 用于指定具体的文件名,funciton 用于指定具体所在函数的函数名,variable 表示要查看的目标变量或表达式。举个例子:

c
#include <stdio.h>
int num = 10;
int main()
{    
    int num = 20;    
    return 0;
}

假设该程序存储在 main.c 文件中,则使用 GDB 调试至第 5 行(return 0)处暂停后,通过执行如下命令,即可查看 num 全局变量的值:

shell
(gdb) print 'main.c'::num
$1 = 10

而通过执行如下命令,可以查看 num 局部变量的值:

shell
(gdb) p main::num
$1 = 20

当然,由于 GDB 调试就暂停在 main() 函数中,因此即便不指明main::,这里的 num 默认指代的也是 num 局部变量。

二、使用示例

1. 基础用法

1.1 测试程序

c
#include <stdio.h>
int main(int argc, const char *argv[]) {
    int num, result = 0, i = 0;
    scanf("%d", &num);
    while (i <= num) {
        result += i;
        i++;
    }
    printf("result = %d\n", result);
    return 0;
}

我们在ubuntu中测试,所以直接用下面的命令编译:

shell
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 033-gdb-print-basic.c -g

1.2 调试示例

shell
 sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main  +1 ~0 -0 !]
$ gdb a.out -q
Reading symbols from a.out...
(gdb) l        # <=== 1. 查看源码
1       #include <stdio.h>
2       int main(int argc, const char *argv[]) {
3           int num, result = 0, i = 0;
4           scanf("%d", &num);
5           while (i <= num) {
6               result += i;
7               i++;
8           }
9           printf("result = %d\n", result);
10          return 0;
(gdb) b 3      # <=== 2. 在第3行打断点
Breakpoint 1 at 0x11ab: file 034-gdb-display.c, line 3.
(gdb) r        # <=== 3. 运行程序到断点处暂停
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out 

Breakpoint 1, main (argc=1, argv=0x7fffffffce28) at 034-gdb-display.c:3
3           int num, result = 0, i = 0;
(gdb) n        # <=== 4. 继续执行1行
4           scanf("%d", &num);
(gdb) n        # <=== 5. 再执行1行
3              # <=== 6. 输入num的值为3
5           while (i <= num) {
(gdb) p num    # <=== 7. 打印num的值
$1 = 3
(gdb) p num=4  # <=== 8. 设置num的值为4
$2 = 4 
(gdb) b 9      # <=== 9. 在第9行添加断点
Breakpoint 2 at 0x5555555551e5: file 034-gdb-display.c, line 9.
(gdb) c        # <=== 10. 继续运行
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffce28) at 034-gdb-display.c:9
9           printf("result = %d\n", result);
(gdb) p result # <=== 11. 打印出result的值
$3 = 10
(gdb) p result=20 # <=== 12. 修改 result 的值为20
$4 = 20
(gdb) c
Continuing.
result = 20       # <=== 13. 最终result的值为前面设置的20
[Inferior 1 (process 23956) exited normally]
(gdb)

2. 其他用法

2.1 测试程序

c
#include <stdio.h>
struct student {
    int   id;
    char *name;
    int   age;
};
int num = 20;
int main(int argc, const char *argv[]) {
    int            i = 1;
    int            num = 10;
    int            arr[10] = {0, 1, 2, 3, 4, 5};
    struct student stu = {1001, "sumu", 20};

    printf("num = %d\n", num);
    for (i = 0; i < 10; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    printf("Student ID: %d\n", stu.id);
    printf("Student Name: %s\n", stu.name);
    printf("Student Age: %d\n", stu.age);
    return 0;
}

我们在ubuntu中测试,所以直接用下面的命令编译:

shell
cd ~/workspace/c-learning/02-c-basic/21-debug
gcc 034-gdb-print-advanced -g

2.2 调试示例

shell
 sumu@virtual-machine:~/workspace/c-learning/02-c-basic/21-debug [main  +1 ~0 -0 !]
$ gdb a.out -q
Reading symbols from a.out...
(gdb) l           # <=== 1. 查看源码
warning: Source file is more recent than executable.
1       #include <stdio.h>
2       struct student {
3           int   id;
4           char *name;
5           int   age;
6       };
7       int num = 20;
8       int main(int argc, const char *argv[]) {
9           int            i = 1;
10          int            num = 10;
(gdb) 
11          int            arr[10] = {0, 1, 2, 3, 4, 5};
12          struct student stu = {1001, "sumu", 20};
13
14          printf("num = %d\n", num);
15          for (i = 0; i < 10; i++) {
16              printf("arr[%d] = %d\n", i, arr[i]);
17          }
18          printf("Student ID: %d\n", stu.id);
19          printf("Student Name: %s\n", stu.name);
20          printf("Student Age: %d\n", stu.age);
(gdb) 
21          return 0;
22      }
(gdb) b 14        # <=== 2. 在第14行打断点
Breakpoint 1 at 0x11fd: file 033-gdb-print.c, line 14.
(gdb) r           # <=== 3. 运行程序并暂停
Starting program: /home/sumu/workspace/c-learning/02-c-basic/21-debug/a.out 

Breakpoint 1, main (argc=1, argv=0x7fffffffce28) at 033-gdb-print.c:14
14          printf("num = %d\n", num);
(gdb) print -address on -- stu.name     # <=== 4. 打印指针的同时,输出指针所在的内存地址
$1 = 0x555555556004 "sumu"
(gdb) print -address off -- stu.name    # <=== 5. 打印指针,不输出所在地址
$2 = "sumu"
(gdb) print -pretty on -- stu           # <=== 6. 以便于阅读的方式输出结构体的值
$3 = {
  id = 1001,
  name = 0x555555556004 "sumu",
  age = 20
}
(gdb) print stu                         # <=== 7. 压缩格式输出结构体的值
$4 = {id = 1001, name = 0x555555556004 "sumu", age = 20}
(gdb) print/x num                       # <=== 8. 十六进制格式打印num的值
$5 = 0xa
(gdb) print arr[1]@5                    # <=== 9. 从arr[1]处,输出数组后面5个元素的值
$6 = {1, 2, 3, 4, 5}
(gdb) print num                         # <=== 10.输出局部变量num的值
$7 = 10
(gdb) print '033-gdb-print.c'::num      # <=== 11. 输出全局变量num的值
$8 = 20
(gdb)