[干货] gdb使用指南

忽然发现自己欠缺点调试能力,所以就学习了一下,来写了篇gdb使用指南,以作备用。

Before we start

1.gdb是啥?
是一个UNIX及UNIX-like下的调试工具。使用gdb,需要在cmd命令窗口下操作。

2.为啥要用gdb?
有的时候静态查错,输出中间变量调试信息,devcpp自带的debug都不好使,就得用gdb。

3.gdb好在哪?
据zhx julao说,熟练使用gdb可以在5min内准确找出代码错误。

1.准备工作

首先我们需要配置好系统的环境变量。如果电脑是在Linux环境下,那么第1部分所有工作都已经做好了,可直接跳过。此部分只针对windows系统,且Win7Win10相同。

我们的编译器,大多都是使用devcpp自带的MinGW32/MinGW64。现在我们要找到它。

打开C盘,进入Programs(x86),找到Dev-Cpp文件夹,可以发现里面有一个MinGW32/MinGW64(这个是根据电脑是32位还是64位而定)文件夹,点进去找到bin。

打开bin(垃圾桶)文件夹,我们惊奇地发现所有编译器都安安静静地待在里面(gdb也在呢!)。然后,我们需要复制现在所处的这个位置的地址。

退回到桌面,找到“我的电脑”或者“计算机”,右键进入计算机属性界面,找到“高级系统设置”。

然后在高级系统设置里找到环境变量,新建用户变量。

变量名:大写PATH,变量值:刚才复制下来的bin文件夹地址。

一路确定出去就配好了,不要点取消。

2.检查程序是否CE

我写了一段把0~100之间的整数二进制输出的代码,叫1.cpp。接下来我们就调试它。

#include<cstdio>
#include<cstring>
using namespace std;
int a[100];
void print(int x)
{
	if(!x)
	{
		printf("0
");
		return;
	}
	int i,j=0;
	while(x)
	{
		a[++j]=x&1;
		x>>=1;
	}
	for(i=j;i>=1;i--)
	printf("%d",a[i]);
	printf("
");
	return;
}
int main()
{
	int i;
	for(i=0;i<=100;i++)
	print(i);
	return 0;
}

检查是否CE,我们不要用devcpp,因为有一些库devcpp会自动补全,会造成问题。

我们用cmd指令来编译。

打开1.cpp(我们要调试的代码)的目录,地址栏输入cmd,回车。

我们接下来所有工作都是在这个命令窗口里操作。

输入编译命令g++ 1.cpp -o 1.exe,意思是把1.cpp用g++编译成1.exe。

如果回车之后什么事也没有,那么恭喜,编译成功。

如果提示找不到g++,那么环境变量配错了,再回去配一次。

如果弹出编译信息error,那么是CE了。warning则没有CE。

上图是CE的状况。

如果考试题目pdf的第一页里有编译选项,那就在刚才的指令的后面加上编译选项的全文,比如——

检查完代码,没有CE,我们终于可以开始调试了。

3.简单的调试指令

首先要进入调试工具gdb。我们要重新编译代码,在编译选项的后面加上指令-g。比如,g++ 1.cpp -o 1.exe -O2 -g。编译完成之后,键入指令gdb 1.exe

P.S.上图中只需要打一遍g++ 1.cpp -o 1.exe -O2 -g即可,不用编译两次qwq~

然后会出来这么一大堆东西。

我们不管别的,只看倒数第二行红线画出来的东西。如果显示reading...done,那么表示我们的代码被gdb读取成功了,可以开始调试了。

下面我们开始调试吧。

1)运行整段代码:r

敲进去指令r,我们看到gdb把整段代码运行了一遍,还把输出给了我们。如果不想看到这些输出,可以用文件。

2)在某行设断点:b 1(在第一行设断点,其他以此类推)

有的时候我们需要让程序在某一行停下来,这个停下来的位置就叫断点。在第1行设断点,指令叫做b 1

设好所有断点,然后再敲r开始运行。

假如我们在第25行设断点,那么程序会执行完第25行,停在第26行,如下图。

3)在某个函数设断点:b print(在print()函数设断点,其他以此类推)

设好函数断点之后,会在刚刚进入函数的时候停下来,如下图。

4)条件断点:b 27 if (i==10)(在i==10时在第27行停下来,其他以此类推)

if里面的东西,语法和c++一样。

效果基本和行断点一样qwq。

但是这次是还没有执行第10遍循环就停了下来。

5)一步一步执行:s

比如我们就从刚才停下来的位置开始一步一步执行:

可以看到,每一次都只执行了一句话,而且所有的地方都会被展示出来——例如进出函数,if语句执行与否,等等。

但是这样做会出现问题,比如如果我们继续执行下去——

再按十几次s之后,就出现上图一大堆看不懂的东西了。

什么情况?

其实是程序进到了printf()函数的里面,所以让人看不懂了的。

那怎么避免这种情况嘞?

6)一步执行完当前函数:finish

finish,就直接退回到我们的代码里了。

如果本来就不想进到函数里面呢?

7)执行下一步,如果是函数一步执行完:n

直接敲n,可以看到程序没有进入printf函数,如下图——

8)继续不断执行,直到下一个断点:c

我们先设上下一个断点,然后敲c

可以看到程序执行完86停下,87还没执行。

9)打印某个变量的值:p i(打印i的值,其余以此类推)

很简单。注意,如果是递归的话,只显示当前层上的值

10)持续跟踪某个变量的值:display i(持续跟踪i,其余以此类推)

也很简单。敲完display i之后,执行sn等等都可以。

11)退出gdb:q

有时候gdb还会不舍地和你挽留一句qwq:

4.测试时间和内存

1)测试内存

首先还是编译代码,要加上-g。然后,输入size 1.exe

显示的第三个和第四个数(计算方式有微小的差异)就是以B为单位的运行内存。只要保证这两个数都不MLE,代码一定安全。

2)测试时间

首先还是编译代码,要加上-pg,注意了,是-pg,因为我们使用的工具变了,叫gprof

编译完之后,直接输入1.exe,意思是在cmd里运行一遍1.exe。

然后,我们整个程序所有函数的运行时间信息都被gprof记下来了。现在我们要导出这些数据,用的是指令gprof 1.exe > test.out,意思是把信息导出到test.out里。

打开文件test.out。

所有的运行时间信息都在里面了!

注:如果函数里面只有一句话,c++会把它优化掉,所以这个函数执行时间为0。

End

gdb大法好!

也许它现在看起来比较麻烦,但是我尽量还是坚持zhx学长的建议,常用gdb吧。

祝:NOIP CSP rp++

完结撒花!

orz~

原文地址:https://www.cnblogs.com/Rain142857/p/11830821.html