GDB调试

  程序中的语法错误通常可以在翻译阶段就能被诊断出来,但逻辑错误却很难被发现和纠正,比如在解决问题时使用了错误或者不完备的思路。在这种情况下,调试可能是唯一的救命稻草。通过设置适当的断点,你可以观察结果并和预期的结果进行比较以缩小问题代码的范围,并最终发现问题所在。

  所谓调试(Debug),就是让代码一步一步慢慢执行,跟踪程序的运行过程。比如,可以让程序停在某个地方,查看当前所有变量的值,或者内存中的数据;也可以让程序一次只执行一条或者几条语句,看看程序到底执行了哪些代码。

  在调试的过程中,我们可以监控程序的每一个细节,包括变量的值、函数的调用过程、内存中数据、线程的调度等,从而发现隐藏的错误或者低效的代码。

  编译器可以发现程序的语法错误,调试可以发现程序的逻辑错误。所谓逻辑错误,是指代码思路或者设计上的缺陷。

  现在主流C/C++调试器有下面几种:

  (1)Remote Debugger:Remote Debugger 是 VC/VS 自带的调试器,与整个IDE无缝衔接,使用非常方便,初学者建议使用该调试器,本教程也以 VS2010 为例讲解调试技巧。

  (2)WinDbg:大名鼎鼎的 Windows 下的调试器,它的功能甚至超越了 Remote Debugger,它还有一个命令行版本(cdb.exe),但是这个命令行版本的调试器指令比较复杂,不建议初学者使用。

  (3)LLDB:XCode 自带的调试器,Mac OS X 下开发必备调试器。

  (4)GDB:Linux 下使用最多的一款调试器,也有 Windows 的移植版。

 

  GDB 是 Linux 下的 C/C++ 程序调试工具,它开源免费,功能强大,但是命令众多。虽然 Linux 系统下读者编写 C/C++ 代码的 IDE 可以自由选择,但是调试生成的 C/C++ 程序一定是直接或者间接使用 GDB。

GDB调试流程:

1、编译文件

  使用 GCC 编译源文件:$ gcc main.c -o main.out -g

  首先使用 GCC 来编译 main.c,编译的时候使用-g选项,它的目的是向可执行程序中加入调试信息,包括源代码、符号表等,GDB 需要这些额外的信息来完成调试工作。

  此外还建议关闭编译器的程序优化选项。编译器的程序优化选项一般有五个级别,从 O0 ~ O4, O0 表示不优化,从 O1 ~ O4 优化级别越来越高,O4 最高。这样做的目的是为了调试的时候,符号文件显示的调试变量等能与源代码完全对应起来。

2、 启动 GDB 调试器

  接下来启动 GDB 并调试 main.out:$ gdb main.out

   $ gdb main.out -silent

  选项-silent用于屏蔽 GDB 的前导信息,否则它会在屏幕上打印一堆免责条款。

  启动 GDB 后,它输出的信息表明已经读入了 mian.out 的符号表。接下来,GDB 会显示自己的提示符(gbd),提示并等待你输入调试命令。

3、输入调试命令开始调试

  (1)l 或者list:显示源文件内容

    l 命令默认每次显示 10 行源代码,但我们关心的那一行显然还没有出来。为此,可继续使用 l 命令来显示后面的行。

  (2)b 或者break:设置断点

    调试一个程序的时候,应该在我们关注的地方,或者在故障点的前边设置一个断点(Breakpoint),让程序执行到这里停下来,这样我们就可以慢慢地用别的调试命令进行观察。

    在 GDB 中,设置断点的方法很多,包括在指定的内存地址处设置断点、在源代码的某一行设置断点,或者在某个函数的入口处设置断点,等等。

b+函数名                       设置对应函数为断点
b+行号                         设置对应行号为断点
b+文件名:函数名                设置某个文件中的某个函数为断点
b+文件名:行号                  设置某个文件中的第几行为断点
tbreak+行号或函数名            设置临时断点,达到后自动删除
b+行号+if+条件                 用于设置条件断点,在循环中使用非常方便
b+偏移量                       在当前位置的后
b-偏移量
b+0x..                         以内存地址作为断点
info b                         查看断点处的情况

  (3)删除断点

delete                         删除所有断点
delete+断点编号                删除对应编号的断点
clear+行号                     删除对应行号断点
delete+(编号起点-编号终点)     删除起点和终点的所有断点(包括起点和终点)
disable+断点编号               可以禁用断点

clear
clear 函数名
clear 行号
clear 文件名:行号
clear 文件名:函数名

  (4)r或者run:执行程序

    一旦设置了断点,下一步就是用r或者run命令执行被调试的程序,执行后会自动在第一个断点处停下来.

  (5)p 或者print:打印变量的值

    GDB 的 p 命令用于打印一个表达式的值,GDB 先计算表达式的值,并把它保存在一个存储区中,存储区的名字用$外加数字来表示,并且这个数字会随着调试过程的进行而不断递增(这意味着存储区也是不断开辟的)。

    注意,在你的计算机上,变量 n 和 sum 的当前值可能和这里显示的不同。这很好理解,内存是反复使用的,当一个程序终止后,它占用的内存会分配给其他程序使用;当一个变量不再使用后,它占用的内存也会重新分配,并成为另一个变量。因为变量 n 和 sum 刚刚分配,还没有往里面保存任何数值,故它们的内容是随机的,是其他程序或者变量用过的垃圾值。

  (6)watch:观察变量的值

    在某一循环处,如果想观察一个变量的变化情况,就可以使用watch来观察变量的变化情况。例如:watch i    观察变量i的值 。

awatch+变量                    设置一个观察点,当变量被读出或写入时程序被暂停
rwatch+变量                    设置一个观察点,当变量被程序读时,程序被暂停  
watch+变量                     同awtch

  (7) n或者next:单步调试

    调试命令n或者next用于继续执行源文件中的下一行。

  (8)c或者continue:继续执行程序

    用命令c或者continue可以持续执行程序,直至遇到断点或者程序结束。

  (9)q或者quit:退出调试

    用q或者quit可以结束本次调试工作,这将使得调试器 GDB 结束运行并返回到操作系统。


posted @ 2020-02-27 16:30  孤情剑客  阅读(568)  评论(0)    收藏  举报