程序员在调试时往往分成两派,一派用debugger另一派用print。至于本人嘛,是一个“机会主义者”,有时用print,有时却改投debugger阵营。
实话说,print要比用debugger设下断点更为简单粗暴,有时甚至会更有用。不过debugger对比于print有三个优点:
无需重新编译
可以在调试时改变变量
debugger可以实现print做不到的复杂操作
在本文,我会介绍一些在gdb中自动化操作的技术,保证可以让你大开眼界,见识下gdb真正的力量。
会话/历史/命令文件
通常我们只有在程序出问题才会启动gdb,开始调试工作,调试完毕后退出。不过,让gdb一直开着未尝不是更好的做法。每个gdb老司机都懂得,gdb在r
的时候会加载当前程序的最新版本。也即是说,就算不退出gdb,每次运行的也会是当前最新的版本。不退出当前调试会话有两个好处:
调试上下文可以得到保留。不用每次运行都重新设一轮断点。
一旦core dump了,可以显示core dump的位置,无需带着core重新启动一次。
在开发C/C++项目,我一般是这样的工作流程:一个窗口开着编辑器,编译也在这个窗口执行;另一个窗口开着gdb,这个窗口同时也用来运行程序。一旦要调试了(或者,又segment fault了),随手就可以开始干活。
当然了,劳作一天之后,总需要关电脑回家。这时候只能退出gdb。不想明天一早再把断点设上一遍?gdb提供了保留断点的功能。输入save br .gdb_bp
,gdb会把本次会话的断点存在.gdb_bp
中。明天早上一回来,启动gdb的时候,加上-x .gdb_bp
,让gdb把.gdb_bp
当做命令文件逐条重新执行,一切又回到昨晚。
condition break/watch/catch
下面是一个带bug的二分查找实现:
#include <iostream>
using std::cout;
using std::endl;
int binary_search(int *ary, unsigned int ceiling, int target)
{
unsigned int floor = 0;
while (ceiling > floor) {
unsigned int pivot = (ceiling + floor) / 2;
if (ary[pivot] < target)
floor = pivot