GDB调试学习(二):断点

简介: GDB调试学习(二):断点

调试程序(实验对象)

#include <stdio.h>
      int main(void)
      {
        int sum = 0, i = 0;
        char input[5];
        while (1) {
                      scanf("%s", input);
                      for (i = 0; input[i] != '\0'; i++)
                              sum = sum*10 + input[i] - '0';
                      printf("input=%d\n", sum);
        }
        return 0;
      }

这个程序的作用是:首先从键盘读入一串数字存到字符数组input中,再转换成整型存到sum中,然后打印出来,一直这样循环下去。

scanf(“%s”, input);这个调用的功能是等待用户输入一个字符串并回车,scanf把其中第一段非空白(非空格、Tab、换行)的字符串保存到input数组中,并自动在末尾添加’\0’。

接下来的循环从左到右扫描字符串并把每个数字累加到结果中,例如输入"2345",则循环累加的过程是(((0×10+2)×10+3)×10+4)×10+5=2345。

注意字符型的’2’要减去’0’的ASCII码才能转换成整数值2。下面编译运行程序看看有什么问题:

$ gcc main.c -g -o main
      $ ./main
      123
      input=123
      234
      input=123234
   (按Ctrl+C组合键退出程序)
      $

又是这种现象,第一次是对的,第二次就不对。

可是这个程序我们并没有忘了赋初值,不仅sum赋了初值,连不必赋初值的i都赋了初值。读者先试试只看代码能不能看出错误原因。下面来调试:

$ gdb main
      ...
      (gdb) start
      Temporary breakpoint 1 at 0x804844d: file main.c, line 5.
      Starting program: /home/akaedu/main
      Temporary breakpoint 1, main () at main.c:5
      5               int sum = 0, i = 0;

有了上一次的经验,sum被列为重点怀疑对象,我们可以用display命令使得每次停下来的时候都显示当前sum的值,然后继续往下走:

(gdb) display sum
      1: sum = 2637812
      (gdb) n
      9                     scanf("%s", input);
      1: sum = 0
      (gdb)(直接回车)
      123
      10                    for (i = 0; input[i] != '\0'; i++)
      1: sum = 0

undisplay命令可以取消跟踪显示,变量sum的编号是1,可以用undisplay 1命令取消它的跟踪显示。这个循环应该没有问题,因为上面第一次输入时打印的结果是正确的。如果不想一步一步走这个循环,可以用break命令(简写为b)在第9行设一个断点(Breakpoint):

(gdb) l
      5               int sum = 0, i = 0;
      6               char input[5];
      7
      8               while (1) {
      9                       scanf("%s", input);
      10                      for (i = 0; input[i] != '\0'; i++)
      11                              sum = sum*10 + input[i] - '0';
      12                      printf("input=%d\n", sum);
      13              }
      14              return 0;
      (gdb) b 9
      Breakpoint 2 at 0x804845d: file main.c, line 9.

break命令的参数也可以是函数名,表示在某个函数开头设断点。现在用continue命令(简写为c)连续运行而非单步运行,程序到达断点会自动停下来,这样就可以停在下一次循环的开头:

(gdb) c
      Continuing.
      input=123
      Breakpoint 2, main () at main.c:9
      9                       scanf("%s", input);
      1: sum = 123

然后输入新的字符串准备转换:

(gdb) n
      234
      10                      for (i = 0; input[i] != '\0'; i++)
      1: sum = 123

问题暴露出来了,新的转换应该再次从0开始累加,而sum现在已经是123了,原因在于新的循环没有把sum归零

(所以这个分析问题,是要有自己的先行的思考,对于这个结果有个预期的判断。)

可见断点有助于快速跳过没有问题的代码,然后在有问题的代码上慢慢走慢慢分析,“断点加单步”是使用调试器的基本方法。

至于应该在哪里设置断点,怎么知道哪些代码可以跳过而哪些代码要慢慢走,也要通过对错误现象的分析和假设来确定,以前我们用printf打印中间结果时也要分析应该在哪里插入printf,打印哪些中间结果,调试的基本思路是一样的。

一次调试可以设置多个断点,用info命令可以查看已经设置的断点:

(gdb) b 12
      Breakpoint 3 at 0x80484b3: file main.c, line 12.
      (gdb) i breakpoints
      Num    Type          Disp Enb Address   What
      2      breakpoint    keep y   0x0804845d in main at main.c:9
        breakpoint already hit 1 time
      3      breakpoint    keep y   0x080484b3 in main at main.c:12

每个断点都有一个编号,可以用编号指定删除某个断点:

(gdb) delete breakpoints 2
      (gdb) i breakpoints
      Num    Type          Disp Enb Address   What
      3      breakpoint    keep y   0x080484b3 in main at main.c:12

有时候一个断点暂时不用可以禁用掉而不必删除,这样以后想用的时候可以直接启用,而不必重新从代码里找应该在哪一行设断点:

(gdb) disable breakpoints 3
      (gdb) i breakpoints
      Num    Type          Disp Enb Address   What
      3      breakpoint    keep n   0x080484b3 in main at main.c:12
      (gdb) enable 3
      (gdb) i breakpoints
      Num    Type          Disp Enb Address   What
      3      breakpoint    keep y   0x080484b3 in main at main.c:12
      (gdb) delete breakpoints
      Delete all breakpoints? (y or n) y
      (gdb) i breakpoints
      No breakpoints or watchpoints.

gdb的断点功能非常灵活,还可以设置断点在满足某个条件时才激活,例如我们仍然在循环开头设置断点,但是仅当sum不等于0时才中断,然后用run命令(简写为r)重新从程序开头连续运行:

(gdb) break 9 if sum != 0
      Breakpoint 4 at 0x804845d: file main.c, line 9.
      (gdb) i breakpoints
      Num    Type          Disp Enb Address   What
      4      breakpoint    keep y   0x0804845d in main at main.c:9
          stop only if sum != 0
      (gdb) r
      The program being debugged has been started already.
      Start it from the beginning? (y or n) y
      Starting program: /home/akaedu/main
      123
      input=123
      Breakpoint 4, main () at main.c:9
      9             scanf("%s", input);
      1: sum = 123

结果是第一次执行scanf之前没有中断,第二次却中断了。总结一下本节用到的gdb命令,如下表所示。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
3月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
62 10
|
3月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
|
3月前
|
NoSQL
技术分享:如何使用GDB调试不带调试信息的可执行程序
【8月更文挑战第27天】在软件开发和调试过程中,我们有时会遇到需要调试没有调试信息的可执行程序的情况。这可能是由于程序在编译时没有加入调试信息,或者调试信息被剥离了。然而,即使面对这样的挑战,GDB(GNU Debugger)仍然提供了一些方法和技术来帮助我们进行调试。以下将详细介绍如何使用GDB调试不带调试信息的可执行程序。
99 0
|
5月前
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
42 1
|
5月前
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
31 0
|
5月前
|
NoSQL Linux C++
Linux C/C++ gdb调试正在运行的程序
Linux C/C++ gdb调试正在运行的程序
|
5月前
|
NoSQL Linux C++
Linux C/C++ gdb调试core文件
Linux C/C++ gdb调试core文件
|
6月前
|
NoSQL 搜索推荐 openCL
【C/C++ 调试 GDB指南 】gdb调试基本操作
【C/C++ 调试 GDB指南 】gdb调试基本操作
378 2
|
6月前
|
NoSQL Linux 开发工具
【深入解析git和gdb:版本控制与调试利器的终极指南】(下)
【深入解析git和gdb:版本控制与调试利器的终极指南】
|
5月前
|
NoSQL Linux C++
Linux C/C++ gdb调试
Linux C/C++ gdb调试