较详细的gdb入门教程
本文主要介绍gdb的基础使用。若需了解一些技巧,请访问此篇博客:点这里
本篇教程适用于Windows,macOS及Linux,但由于Windows的自带终端很难用,所以体验可能不太好。Windows 10建议安装Windows Terminal以取得最佳体验。
1. 前言
你是否为C/C++下的调试而苦恼?你是否苦于Dev-C++调试烦人的问题(如调不了STL、结构体数组要一层一层展开)?那么,gdb很可能是你的最佳选择。
gdb是一个命令行下的、功能强大的调试器。看到命令行下,是不是有点害怕?没关系,本文最后会介绍一些图形前端,但建议先学习一些基础命令。
示例代码:(example.cpp,以下调试命令均以此代码为准)
博主太懒了,只写了个求阶乘
#include <iostream>
#include <cstdio>
using namespace std;
int f(int x){
int ans=1;
for(int i=1;i<=x;i++) ans*=i;
return ans;
}
int main(){
int a;
scanf("%d",&a);
printf("%d\n",f(a));
return 0;
}
系统环境:Linux Mint 19.3 64位。
2. 调试
2.1 启动gdb,载入文件,打印源代码,退出gdb
首先,在编译选项里加上 -g ,以生成调试用的符号表。建议不要同时开 -O2 等优化选项,否则可能会有奇奇怪怪的问题。
打开终端,输入 gdb [可执行文件名] ,载入程序(注意,是可执行文件名(比如1.exe),不是你的源文件名)。比如这样:
> g++ example.cpp -o example -g
[编译,无提示]
> gdb ./example
然后,你可能会见到如下的界面:
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./example...done.
(gdb)
这样,你就进入gdb的命令行环境里了。
其中,第一行是版本信息,倒数第二行表示正载入符号表,最后一行 (gdb) 则是gdb的提示符。
请注意,若你倒数第二行有 (no debugging symbols found) 字样,请确保在编译选项里加上 -g 选项。
当然,如果你直接输入 gdb 启动,不加文件名,也可以。只是,你要使用 file 命令手动载入可执行文件。
以后出现的所有命令,都是在gdb的环境,而非系统shell的环境执行的。
命令:
file(简写fil)
格式:file 可执行文件名
作用:载入当前目录下的对应名称的可执行文件。
例子:
(gdb) file example
Load new symbol table from "example"? (y or n) y
Reading symbols from example...done.
命令:
list(简写为l)
格式:list [行号]
作用:打印给定行号周围10行的源代码。若不提供行号,则接续打印上次的源代码。
这里提到了一个简写的概念。什么是简写呢?简写是为了简化命令的。比如,打一个 list 还是有些麻烦的。这时,我们可以输入它的简写 l 。你可以认为一个命令与它的简写是完全等价的。以后若提到简写,不再解释。
例子:
(gdb) l 7
2 #include <cstdio>
3 using namespace std;
4 int f(int x){
5 int ans=1;
6 for(int i=1;i<=x;i++) ans*=i;
7 return ans;
8 }
9
10 int main(){
11 int a;
(gdb) l
12 scanf("%d",&a);
13 printf("%d\n",f(a));
14 return 0;
15 }
(gdb)
最后,用 quit 命令退出gdb。
命令:
quit(简写为q)
格式:quit(无参数)
作用:退出gdb。
2.2 设置断点
要调试程序,我们必须让它在某个地方停下来。否则,让它一直执行下去,那和普通的执行程序有什么区别呢?
因此,我们需要用 break 来设置断点。此后,程序将会在设定的断点处停下来。
命令:
break(简写为b)
格式:break 函数名|行号
作用:在给定函数名或行数处设置断点。
例子:
(gdb) break main //main函数处设置断点
Breakpoint 1 at 0x874: file example.cpp, line 10.
(gdb) break 11 //在第11行处设置断点
Breakpoint 2 at 0x883: file example.cpp, line 11.
当然可以用它的简写:
(gdb) b main
Breakpoint 1 at 0x874: file example.cpp, line 10.
(gdb) b 11
Breakpoint 2 at 0x883: file example.cpp, line 11.
2.3 运行
命令:
run(简写为r)
格式:run(无参数)
作用:从头运行程序。
命令:
continue(简写为c)
格式:continue(无参数)
作用:从当前位置继续运行程序,直到遇到下一个断点或程序运行完毕。
命令:
until(简写为u)
格式:until 行号(无参数)
作用:从当前位置继续运行程序,直到指定行号处才停下来。
当然,有时候,你可能发现运行上述命令后gdb会停住。这有两种情况:
- 你的程序用了标准输入,gdb在等待输入。
- 数据规模太大或程序效率太低,以至于运行到断点的时间较长。
例子:
(gdb) r
Starting program: /home/acceptedzhs/example
Breakpoint 1, main () at example.cpp:10
10 int main(){
(gdb) c
Continuing.
Breakpoint 2, main () at example.cpp:12
12 scanf("%d",&a);
(gdb)
2.4 单步执行
很多时候,我们要一步一步地执行程序。无疑,反复地 break 与 continue 十分麻烦。gdb有两个命令 next 与 step,可实现单步执行。
命令:
next(简写为n)
格式:next(无参数)
作用:单步执行。若当前行有函数调用,则把这个函数作为一个整体执行(即不进入函数内部)。
命令:
step(简写为s)
格式:step(无参数)
作用:单步执行。若当前行有函数调用,则进入该函数内部。
但是,又有人想偷懒了。反复敲 n 与 s 依然很麻烦。怎么办呢?
gdb有个特性:若什么都不输,直接按回车,则会执行上一次执行的命令。
所以,只要开始敲个 n 或 s,然后一直敲回车就行了。
举个例子好了:
(gdb) b 13
Breakpoint 1 at 0x89b: file example.cpp, line 13.
(gdb) r
Starting program: /home/acceptedzhs/example
10
Breakpoint 1, main () at example.cpp:13
13 printf("%d\n",f(a));
(gdb) n
3628800
14 return 0;
(gdb) [回车] //看到没,执行了上次的命令,即next
15 }
(gdb)
Starting program: /home/acceptedzhs/example
10
Breakpoint 1, main () at example.cpp:13
13 printf("%d\n",f(a));
(gdb) s
f (x=10) at example.cpp:5 //step命令,进入了f函数内部
5 int ans=1;
(gdb)
2.5 输出变量/函数值
有时,我们想要打印某些变量或函数的值,看它是否符合期望。这又怎么办呢?
命令:
print(简写为p)
格式:print 变量名
作用:打印一次变量名/函数调用对应的值。
命令:
display(简写为disp)
格式:display 变量名
作用:设置在每一次停下来时(如到断点时,单步执行等)都打印该变量名/函数调用对应的值。
例子:
(gdb) b 13
Breakpoint 1 at 0x89b: file example.cpp, line 13.
(gdb) r
Starting program: /home/acceptedzhs/example
9
Breakpoint 1, main () at example.cpp:13
13 printf("%d\n",f(a));
(gdb) p a
$1 = 9
(gdb) n
362880 //输出9!
14 return 0; //这只会显示一次,下一步就不会再打印该变量值了
(gdb) p f(2) //当然,调用函数也可以
$2 = 2
(gdb)
(gdb) b f
Breakpoint 1 at 0x841: file example.cpp, line 5.
(gdb) r
Starting program: /home/acceptedzhs/example
9
Breakpoint 1, f (x=9) at example.cpp:5
5 int ans=1;
(gdb) disp ans
1: ans = 0
(gdb) n
6 for(int i=1;i<=x;i++) ans*=i;
1: ans = 1
(gdb)
7 return ans;
1: ans = 362880 //每次停下来时,该变量都会显示
(gdb)
8 }
1: ans = 362880
(gdb)
有时,你可能会见到 <optimized out> 的提示。此时,请检查编译时是否开了优化(如 -O2 )。
2.6 查看某些信息
命令:
info(简写为i)
格式:info 类型
作用:打印对应类型的信息。
其中,类型可以是breakpoints(断点,简写b)、locals(局部变量,简写lo)、display(被设为总是显示的变量,简写disp)等。具体可以通过 help info 查看。
比如:
(gdb) b 13
Breakpoint 1 at 0x89b: file example.cpp, line 13.
(gdb) r
Starting program: /home/acceptedzhs/example
10 //程序的标准输入
Breakpoint 1, main () at example.cpp:13
13 printf("%d\n",f(a));
(gdb) i lo
a = 10
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000055555555489b in main() at example.cpp:13
breakpoint already hit 1 time
(gdb) disp a
1: a = 10
(gdb) i display
Auto-display expressions now in effect:
Num Enb Expression
1: y a
(gdb)
我们发现输出了很多信息。其中Num是编号。编号有什么用呢?我们待会儿就要见到。
2.7 删除/禁用/启用某些东西
命令:
disable(简写为dis)
格式:disable 类型 [编号]
作用:临时禁用某些类型的对应编号的东西,待会儿讲。
命令:
delete(简写为d)
格式:delete 类型 [编号]
作用:删除某些类型的对应编号的东西。
命令:
enable(简写为en)
格式:enable 类型 [编号]
作用:启用某些类型的对应编号的东西。
其中,类型就是讲述 info 命令时中的类型,编号就是 info 命令输出的一堆东西中的Num那一栏。
注意:delete可能用不了类型的简写。
举个例子:
(gdb) b 12
Breakpoint 1 at 0x883: file example.cpp, line 12.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep n 0x0000555555554883 in main() at example.cpp:12
(gdb) dis b 1 //禁用1号断点
(gdb) r
Starting program: /home/acceptedzhs/example
10
3628800 //不经过该断点了
[Inferior 1 (process 2695) exited normally]
(gdb) en b 1 //启用该断点
(gdb) r
Starting program: /home/acceptedzhs/example
Breakpoint 1, main () at example.cpp:12
12 scanf("%d",&a); //又经过该断点了
(gdb) d breakpoints 1 //删除
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/acceptedzhs/example
10
3628800 //又不经过断点了
[Inferior 1 (process 3516) exited normally]
(gdb)
2.8 获取帮助
有时,我们可能忘记某个命令的用法。这该怎么办呢?
命令:
help(简写为h)
格式:help 待查询的命令(待查询的命令可以用简写)
作用:显示待查询的命令的帮助。
例子:
(gdb) h b
Set breakpoint at specified location.
break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
PROBE_MODIFIER shall be present if the command is to be placed in a
probe point. Accepted values are `-probe' (for a generic, automatically
guessed probe type), `-probe-stap' (for a SystemTap probe) or
`-probe-dtrace' (for a DTrace probe).
LOCATION may be a linespec, address, or explicit location as described
below.
With no LOCATION, uses current execution address of the selected
stack frame. This is useful for breaking on return to a stack frame.
...(省略若干行)...
3. 命令一览表
| 命令 | 简写 | 作用 |
|---|---|---|
file |
fil |
载入可执行文件 |
list |
l |
打印源代码 |
quit |
q |
退出gdb |
break |
b |
设置断点 |
run |
r |
从头运行程序 |
continue |
c |
从当前位置继续运行程序 |
until |
u |
从当前位置继续运行,直到指定行号 |
next |
n |
单步执行 |
step |
s |
单步执行 |
print |
p |
打印一次值 |
display |
disp |
设置某个变量/函数总是显示 |
info |
i |
打印相关类型的信息 |
disable |
dis |
临时禁用某些东西 |
delete |
d |
删除某些东西 |
enable |
en |
启用某些东西 |
help |
h |
获取帮助 |
4. 图形界面?
gdb作为一个命令行调试器,对于某些人来说可能望而生畏。
所以,很多人为其开发了图形前端,以方便大家使用。
这里,我推荐nemiver、ddd、gdbgui。(貌似不支持win)
如果你是个Vim爱好者,vim-vebugger也不错。
对上面不满意?可以试试gdb自带的伪图形界面,只要启动gdb时加上 -tui 选项即可。
5. 结语
以上便是全部内容了。
蒟蒻写博客不易,恳请大佬点个赞!
较详细的gdb入门教程的更多相关文章
- 超强、超详细Redis数据库入门教程
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...
- 超强、超详细Redis数据库入门教程(转载)
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...
- 超详细Redis数据库入门教程
[本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用redis4.学会安装redis5.学会启动redis6.使用redis客户端7.redis数据结构 – 简介8.redis ...
- 有shi以来最详细的正则表达式入门教程
本篇文章文字内容较多,但是要学习正则就必须耐心读下去,正则表达式是正则表达式其实并没有想像中的那么困难,但是想要熟练的掌握它,还是需要下功夫勤加练习的.这里讲一些正则表达式的语法和学习方法,大家还要多 ...
- 一份详细的asyncio入门教程
asyncio模块提供了使用协程构建并发应用的工具.它使用一种单线程单进程的的方式实现并发,应用的各个部分彼此合作, 可以显示的切换任务,一般会在程序阻塞I/O操作的时候发生上下文切换如等待读写文件, ...
- gulp详细入门教程
本文链接:http://www.ydcss.com/archives/18 gulp详细入门教程 简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优 ...
- ant使用指南详细入门教程
这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装.验证安装.使用方法.使用实例.ant命令等内容,需要的朋友可以参考下 一.概述 ant 是一个将软件编译.测试.部署等步骤联系在一起 ...
- gulp详细入门教程(转载)
本文转载自: gulp详细入门教程
- <转载>ant使用指南详细入门教程 http://www.jb51.net/article/67041.htm
这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装.验证安装.使用方法.使用实例.ant命令等内容,需要的朋友可以参考下 一.概述 ant 是一个将软件编译.测试.部署等步骤联系在一起 ...
随机推荐
- 基于COCA词频表的文本词汇分布测试工具v0.2
update: 简单整理了一下代码的组织. 处理的单词封装成类,单词的修正,信息的显示都作为其内的方法. 写得还比较糙,工具本身可以封装,还有对于单词的变形基本没什么处理,以后有时间再改. 项目托管到 ...
- (入门)matlab中创建和调用m文件
大学学过的一款软件,说实话没好好学,老师直接讲到高深的做仿真之类的 综合网上的教程讲述基础的matlab创建遇到的问题: 参考: 1. https://blog.csdn.net/weixin_423 ...
- PADS Layout VX.2.3 修改层名
操作系统:Windows 10 x64 工具1:PADS Layout VX.2.3 点击菜单Setup > Layer Definition... 在Layers Setup窗口中,选择相应的 ...
- 题解 P3572 [POI2014]PTA-Little Bird
P3572 [POI2014]PTA-Little Bird 首先,这道题的暴力dp非常好写 就是枚举所有能转移到他的点,如果当前枚举到的位置的值大于 当前位置的话,\(f[i]=min(f[i],f ...
- 多测师讲解ui自动化框架设计思想_高级讲师肖sir
UI自动化框架:UI自动化框架可以分为8个模块,conf.data.public.pageobject.testcase.runner.report.log.conf是用来储存系统环境.数据库.邮件的 ...
- 多测师讲解selenium—自动化测试课堂面试题总结—高级讲师肖sir
1.你有做过自动化?你用什么语言? python2.自动化中如何使用语言打开一个网址?浏览器,浏览器对应驱动,导入库,类,get,url3.在一个浏览器中打开多个窗口?open_windows dri ...
- Chrome浏览器调试移动端网页,测试人员也可以轻松debug
现在的产品,移动端应用占据很大市场,在测试过程中,就会测试各种各样的移动端页面.测试过程,或多或少会发现些问题,无非就是前端.后端问题.后端接口问题,可以利用工具:Fiddler或charles抓包查 ...
- MeteoInfoLab脚本示例:计算涡度、散度
用U/V分量数据计算涡度和散度,计算涡度的函数是hcurl,计算散度的函数是hdivg,参数都是U, V.脚本程序: f = addfile('D:/Temp/GrADS/model.ctl') u ...
- .Net Core中使用Grpc
一.Grpc概述 gRPC 基于如下思想:定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型.gRPC 默认使用protocol buffers作为接口定义语言,来描述服务接口和有效载荷消息 ...
- python虚拟环境的配置-ubuntu 18.04后
python虚拟环境的配置 安装相关包 pip install virtualenv pip install virtualenvwrapper 配置~/.bashrc 加入以下内容: ------- ...