首先我们知道在使用vs时,我们时可以通过调式一段代码来找出我们这段代码的逻辑错误在哪里的,但是在Linux中没有对应的图形化界面,我们应该怎么去调试一个代码呢?这里就需要使用道Linux中的一个工具叫做gdb。首先要检测你的操作系统中是否安装了gdb。
检测安装gdb
使用下面的指令
gdb --version
如果出现了你所对应的gdb的版本号,代表已经安装了gdb。
如果没有安装gdb请在已经安装了gcc和g++环境下执行下面的命令
sudo yum install -y gdb
gdb的使用
Linux中的Debug和Release
下面我们就来学习如何使用gdb这个工具。第一步我们肯定要先完成一段简单的易于调式的代码我写了下面的代码
这个函数一定是输出30的,但是如果我是用下面的指令来生成的可执行程序能够被调式吗?
g++ -o mybin test.cpp
如果这样写那么当你直接使用./mybin时能够输出30,但是如果你要使用gdb去调试这个代码是不可行的。
从上面能够看到当我直接执行这个mybin时是可以出现答案的,但是如果我想使用gdb去调试这个代码的时候,我在gdb中输入l指令就直接给我报错了,那么这是为什么呢?在使用vs的时候我们知道要调试一个代码那就需要我们将编译器的编译模式改成Debug模式,如果你在vs中是使用Release模式也是不可以使用调试的。那么这里也是同理,那么怎么证明这个mybin是在Release模式下生成的呢?最直观的方法就是再生成一个新的Debug模式的可执行程序,Debug模式下生成的可执行程序大小一般是比Release要大的,因为要加入一些调试的信息。而在Linux中如果要生成一个Debug模式下的可执行程序就要使用下面的命令。
g++ -o newmybin -g test.cpp
可以很明显的看到newmybin的文件大小是远大于mybin文件的。所以要使用gdb调试文件那么就要在使用g++时加上-g选项。
gdb基本命令
下面我们就拿newmybin文件来演示gdb的使用。
当我们使用
gdb <文件名>
看到下面的这个界面之后,我们就遇到了第一个问题那就是我们连代码都看不到该怎么调试呢?所以这里就要使用list命令。
list命令
这个命令在gdb中可以直接使用l代替。
当执行了这个命令之后可以看到它显示了我所写的代码但是并没有显示完全而且并不是从第0行开始显示的。
那么我们可以想下面这样使用
l <你想要开始显示的行数>
可以看到我输入了l 0之后就从第0行开始往下显示了。如果我想要继续往下打印呢?那就不需要输入任何的命令直接回车即可,gdb会记住上次输入的命令
可以看到我在按了一个回车之后,就直接继续往下打印了,如果我在打印完成之后还按回车就会显示18行已经全部打印完毕了。
打上断电(b)命令和查看断点信息命令
那么现在我已经能够看到我的代码了,下一步自然就是打印断点了,打印断点的命令为
breakpoints(可以直接简写成b) <你要打的行数>
例如上面我打算在第8行打印上一个断点
就可以直接使用 b 8就可以了,那么如果我已经打上了断点我如何查看我打上断点的信息呢?
这里就要使用下面的命令
info b
从上图就可以看到我在第8行设置了一个断点。
开始执行调试命令
现在我已经在代码中加入了断点,现在要运行调试代码了要执行下面的命令
run(可简写成)
这个命令开始执行之后如果没有断点,就会直接出现最后的结果(如果是要打印的话)。
在上面的图上,我第一次运行的时候没有任何的断点所以r后直接出现了30,而第二次我在第8行打上了一个断点所以当我再一次run的时候就直接在第八行停下来了。
删除断点命令
既然能够打上断点和查看断点的信息自然也能够删除一个断点。删除断点需要使用下面的命令。
d <断点的编号>
需要注意的是,如果我想删除我当前的这个带吧在第八行的这个断点,使用b 8是不行的,那么什么是断点的编号呢?断点的编号就是我们在info b时看到的第一行的那个编号。
例如我这个在第八行的断点的编号就是2
那么如果我想要删除这个断点那就直接使用d 8就可以删除这个断点了。
需要注意的是如果我现在依旧是在第8行重新打上断点,这个新断点的编号也不是2而是3。
暂时关闭和打开断点功能命令
那么当我在调试一段代码的时候,我已经不需要使用某一个断点了,但是我并不想删除这个断点有没有什么办法呢?这个办法就是暂时的关闭这个断点的功能。使用下面的命令
enable <断点的编号>
从上图可以看到我在第13行和第15行都设置了一个断点,但是当我将第13行的断点disable之后,再运行run就直接跳到15行了,但是我并没有删除第13行的断点。
如果我想再次启用这个断点则使用enable
enable <断点编号>
可以看到当我使用enable将这个断点的功能重新打开之后再次使用r,就停到了第13行。
逐过程和逐语句命令
在使用vs调试的时候,在进入调试界面后如果遇到一个函数你不想进入函数那么你会选择f10(逐过程),而当你找到你所出错的函数处然后要进入到这个函数中时你会选择f11(逐语句)。那么在Linux中如何实现呢?
n(逐过程,类似于vs中的f10)
s(逐语句类似于vs中的f11)
显示变量的细节(监视功能)
在vs中我们只用打开监视窗口,然后输入我们想要查看的变量的名字即可以查看到这个变量的细节,而在Linux中我们若想要查看一个变量的细节可以使用下面的命令。
display <变量名和变量的地址可以看到变量的细节>
就以上面得那个代码为例子如果我想查看left得值,就可以直接使用display left即可。
可以看到当我使用s进入到这个函数之后,就可以直接使用display left显示left的值了,但是如果此时你在输入n/s那么显示就会消失,那么有没有什么命令能让它一直显示在那里呢?
当然是存在的那就是使用undisplay
undisplay <变量名和变量的地址可以看到变量的细节>
使用这个命令之后,即使你在使用n/s命令,那么你所要查看的变量也不会消失。
until指令
until <行号>
在GDB中,until(简写为u)指令用于继续执行程序直到达到指定行号或函数返回。以下是until指令的使用方法:
在GDB命令提示符下,输入until或u,然后按回车键,程序将继续执行直到下一个断点或程序结束。
- 若要指定行号来执行程序,可以使用
until <行号>
或u <行号>
。例如,until 20
表示程序将继续执行直到达到第20行。 - 若要指定函数来执行程序,可以使用
until <函数名>
或u <函数名>
。例如,until main
表示程序将继续执行直到进入main函数。 - 可以使用
until <行号> if <条件>
或u <行号> if <条件>
来在达到指定行号时执行条件判断。例如,until 20 if i == 10
表示程序将继续执行直到达到第20行,并且只有在变量i等于10时才停止。 - 若要在循环中使用until指令,可以使用
until <行号> while <条件>
或u <行号> while <条件>
。例如,until 20 while i < 10
表示程序将继续执行直到达到第20行,并且只有在变量i小于10时才停止。
需要注意的是,行号可以是源代码中的行号,也可以是汇编代码中的指令地址。使用until指令时,建议在设置断点或单步执行程序后再使用,以确保在正确的位置使用until指令。
finish指令
finish
在gdb中,finish(简写为fin)指令用于连续执行程序,直到当前函数返回为止。以下是finish指令的使用方法:
- 在GDB命令提示符下,输入
finish
或fin
,然后按Enter键,程序将继续执行直到当前函数返回。如果当前函数是顶层函数(如main函数),则程序将执行完毕并停止。 - 可以使用
finish if <条件>
来在函数返回之前执行条件判断。例如,finish if i == 10
表示程序将继续执行直到当前函数返回,并且只有在变量i等于10时才停止。
使用finish指令时,GDB会自动跳过当前函数内部的代码,并在函数返回之前停止执行。这对于跳过复杂的函数调用链以及快速返回到上一层调用函数很有用。需要注意的是,如果当前函数内部还有其他断点,GDB会在到达下一个断点之前停止执行。
c命令
在gdb中,c(简写为continue)命令用于继续执行程序,直到下一个断点或程序结束。以下是c命令的使用方法:
- 在GDB命令提示符下,输入
c
或continue
,然后按Enter键,程序将继续执行直到下一个断点或程序结束。 - 如果程序在运行时遇到断点,可以使用c命令来继续执行程序,跳过当前断点。
- 如果程序没有设置断点,使用c命令将使程序一直执行直到程序结束。
- 在程序执行期间,可以使用c命令来暂停程序的执行。这在你想在特定位置设置断点或观察程序状态时非常有用
希望这篇博客能对你有所帮助,如果有任何错误,请指出感激不尽。