本文主要介绍gdb的基础使用。若需了解一些技巧,请访问此篇博客:点这里

本篇教程适用于Windows,macOS及Linux,但由于Windows的自带终端很难用,所以体验可能不太好。Windows 10建议安装Windows Terminal以取得最佳体验。

1. 前言

你是否为C/C++下的调试而苦恼?你是否苦于Dev-C++调试烦人的问题(如调不了STL、结构体数组要一层一层展开)?那么,gdb很可能是你的最佳选择。

gdb是一个命令行下的、功能强大的调试器。看到命令行下,是不是有点害怕?没关系,本文最后会介绍一些图形前端,但建议先学习一些基础命令。

示例代码:(example.cpp,以下调试命令均以此代码为准)

博主太懒了,只写了个求阶乘

  1. #include <iostream>
  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. int main(){
  10. int a;
  11. scanf("%d",&a);
  12. printf("%d\n",f(a));
  13. return 0;
  14. }

系统环境:Linux Mint 19.3 64位。

2. 调试

2.1 启动gdb,载入文件,打印源代码,退出gdb

首先,在编译选项里加上 -g ,以生成调试用的符号表。建议不要同时开 -O2 等优化选项,否则可能会有奇奇怪怪的问题。

打开终端,输入 gdb [可执行文件名] ,载入程序(注意,是可执行文件名(比如1.exe),不是你的源文件名)。比如这样:

  1. > g++ example.cpp -o example -g
  2. [编译,无提示]
  3. > gdb ./example

然后,你可能会见到如下的界面:

  1. GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
  2. Copyright (C) 2018 Free Software Foundation, Inc.
  3. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  4. This is free software: you are free to change and redistribute it.
  5. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  6. and "show warranty" for details.
  7. This GDB was configured as "x86_64-linux-gnu".
  8. Type "show configuration" for configuration details.
  9. For bug reporting instructions, please see:
  10. <http://www.gnu.org/software/gdb/bugs/>.
  11. Find the GDB manual and other documentation resources online at:
  12. <http://www.gnu.org/software/gdb/documentation/>.
  13. For help, type "help".
  14. Type "apropos word" to search for commands related to "word"...
  15. Reading symbols from ./example...done.
  16. (gdb)

这样,你就进入gdb的命令行环境里了。

其中,第一行是版本信息,倒数第二行表示正载入符号表,最后一行 (gdb) 则是gdb的提示符

请注意,若你倒数第二行有 (no debugging symbols found) 字样,请确保在编译选项里加上 -g 选项。

当然,如果你直接输入 gdb 启动,不加文件名,也可以。只是,你要使用 file 命令手动载入可执行文件。

以后出现的所有命令,都是在gdb的环境,而非系统shell的环境执行的。

命令:file(简写fil)

格式:file 可执行文件名

作用:载入当前目录下的对应名称的可执行文件。

例子:

  1. (gdb) file example
  2. Load new symbol table from "example"? (y or n) y
  3. Reading symbols from example...done.

命令:list(简写为l)

格式:list [行号]

作用:打印给定行号周围10行的源代码。若不提供行号,则接续打印上次的源代码。

这里提到了一个简写的概念。什么是简写呢?简写是为了简化命令的。比如,打一个 list 还是有些麻烦的。这时,我们可以输入它的简写 l 。你可以认为一个命令与它的简写是完全等价的。以后若提到简写,不再解释。

例子:

  1. (gdb) l 7
  2. 2 #include <cstdio>
  3. 3 using namespace std;
  4. 4 int f(int x){
  5. 5 int ans=1;
  6. 6 for(int i=1;i<=x;i++) ans*=i;
  7. 7 return ans;
  8. 8 }
  9. 9
  10. 10 int main(){
  11. 11 int a;
  12. (gdb) l
  13. 12 scanf("%d",&a);
  14. 13 printf("%d\n",f(a));
  15. 14 return 0;
  16. 15 }
  17. (gdb)

最后,用 quit 命令退出gdb。

命令:quit(简写为q)

格式:quit(无参数)

作用:退出gdb。

2.2 设置断点

要调试程序,我们必须让它在某个地方停下来。否则,让它一直执行下去,那和普通的执行程序有什么区别呢?

因此,我们需要用 break 来设置断点。此后,程序将会在设定的断点处停下来。

命令:break(简写为b)

格式:break 函数名|行号

作用:在给定函数名或行数处设置断点。

例子:

  1. (gdb) break main //main函数处设置断点
  2. Breakpoint 1 at 0x874: file example.cpp, line 10.
  3. (gdb) break 11 //在第11行处设置断点
  4. Breakpoint 2 at 0x883: file example.cpp, line 11.

当然可以用它的简写:

  1. (gdb) b main
  2. Breakpoint 1 at 0x874: file example.cpp, line 10.
  3. (gdb) b 11
  4. Breakpoint 2 at 0x883: file example.cpp, line 11.

2.3 运行

命令:run(简写为r)

格式:run(无参数)

作用:从头运行程序。

命令:continue(简写为c)

格式:continue(无参数)

作用:从当前位置继续运行程序,直到遇到下一个断点或程序运行完毕。

命令:until(简写为u)

格式:until 行号(无参数)

作用:从当前位置继续运行程序,直到指定行号处才停下来。

当然,有时候,你可能发现运行上述命令后gdb会停住。这有两种情况:

  1. 你的程序用了标准输入,gdb在等待输入。
  2. 数据规模太大或程序效率太低,以至于运行到断点的时间较长。

例子:

  1. (gdb) r
  2. Starting program: /home/acceptedzhs/example
  3. Breakpoint 1, main () at example.cpp:10
  4. 10 int main(){
  5. (gdb) c
  6. Continuing.
  7. Breakpoint 2, main () at example.cpp:12
  8. 12 scanf("%d",&a);
  9. (gdb)

2.4 单步执行

很多时候,我们要一步一步地执行程序。无疑,反复地 breakcontinue 十分麻烦。gdb有两个命令 nextstep,可实现单步执行。

命令:next(简写为n)

格式:next(无参数)

作用:单步执行。若当前行有函数调用,则把这个函数作为一个整体执行(即不进入函数内部)。

命令:step(简写为s)

格式:step(无参数)

作用:单步执行。若当前行有函数调用,则进入该函数内部

但是,又有人想偷懒了。反复敲 ns 依然很麻烦。怎么办呢?

gdb有个特性:若什么都不输,直接按回车,则会执行上一次执行的命令

所以,只要开始敲个 ns,然后一直敲回车就行了。

举个例子好了:

  1. (gdb) b 13
  2. Breakpoint 1 at 0x89b: file example.cpp, line 13.
  3. (gdb) r
  4. Starting program: /home/acceptedzhs/example
  5. 10
  6. Breakpoint 1, main () at example.cpp:13
  7. 13 printf("%d\n",f(a));
  8. (gdb) n
  9. 3628800
  10. 14 return 0;
  11. (gdb) [回车] //看到没,执行了上次的命令,即next
  12. 15 }
  13. (gdb)
  1. Starting program: /home/acceptedzhs/example
  2. 10
  3. Breakpoint 1, main () at example.cpp:13
  4. 13 printf("%d\n",f(a));
  5. (gdb) s
  6. f (x=10) at example.cpp:5 //step命令,进入了f函数内部
  7. 5 int ans=1;
  8. (gdb)

2.5 输出变量/函数值

有时,我们想要打印某些变量或函数的值,看它是否符合期望。这又怎么办呢?

命令:print(简写为p)

格式:print 变量名

作用:打印一次变量名/函数调用对应的值。

命令:display(简写为disp)

格式:display 变量名

作用:设置在每一次停下来时(如到断点时,单步执行等)都打印该变量名/函数调用对应的值。

例子:

  1. (gdb) b 13
  2. Breakpoint 1 at 0x89b: file example.cpp, line 13.
  3. (gdb) r
  4. Starting program: /home/acceptedzhs/example
  5. 9
  6. Breakpoint 1, main () at example.cpp:13
  7. 13 printf("%d\n",f(a));
  8. (gdb) p a
  9. $1 = 9
  10. (gdb) n
  11. 362880 //输出9!
  12. 14 return 0; //这只会显示一次,下一步就不会再打印该变量值了
  13. (gdb) p f(2) //当然,调用函数也可以
  14. $2 = 2
  15. (gdb)
  1. (gdb) b f
  2. Breakpoint 1 at 0x841: file example.cpp, line 5.
  3. (gdb) r
  4. Starting program: /home/acceptedzhs/example
  5. 9
  6. Breakpoint 1, f (x=9) at example.cpp:5
  7. 5 int ans=1;
  8. (gdb) disp ans
  9. 1: ans = 0
  10. (gdb) n
  11. 6 for(int i=1;i<=x;i++) ans*=i;
  12. 1: ans = 1
  13. (gdb)
  14. 7 return ans;
  15. 1: ans = 362880 //每次停下来时,该变量都会显示
  16. (gdb)
  17. 8 }
  18. 1: ans = 362880
  19. (gdb)

有时,你可能会见到 <optimized out> 的提示。此时,请检查编译时是否开了优化(如 -O2 )。

2.6 查看某些信息

命令:info(简写为i)

格式:info 类型

作用:打印对应类型的信息。

其中,类型可以是breakpoints(断点,简写b)、locals(局部变量,简写lo)、display(被设为总是显示的变量,简写disp)等。具体可以通过 help info 查看。

比如:

  1. (gdb) b 13
  2. Breakpoint 1 at 0x89b: file example.cpp, line 13.
  3. (gdb) r
  4. Starting program: /home/acceptedzhs/example
  5. 10 //程序的标准输入
  6. Breakpoint 1, main () at example.cpp:13
  7. 13 printf("%d\n",f(a));
  8. (gdb) i lo
  9. a = 10
  10. (gdb) i b
  11. Num Type Disp Enb Address What
  12. 1 breakpoint keep y 0x000055555555489b in main() at example.cpp:13
  13. breakpoint already hit 1 time
  14. (gdb) disp a
  15. 1: a = 10
  16. (gdb) i display
  17. Auto-display expressions now in effect:
  18. Num Enb Expression
  19. 1: y a
  20. (gdb)

我们发现输出了很多信息。其中Num是编号。编号有什么用呢?我们待会儿就要见到。

2.7 删除/禁用/启用某些东西

命令:disable(简写为dis)

格式:disable 类型 [编号]

作用:临时禁用某些类型的对应编号的东西,待会儿讲。

命令:delete(简写为d)

格式:delete 类型 [编号]

作用:删除某些类型的对应编号的东西。

命令:enable(简写为en)

格式:enable 类型 [编号]

作用:启用某些类型的对应编号的东西。

其中,类型就是讲述 info 命令时中的类型,编号就是 info 命令输出的一堆东西中的Num那一栏。

注意:delete可能用不了类型的简写。

举个例子:

  1. (gdb) b 12
  2. Breakpoint 1 at 0x883: file example.cpp, line 12.
  3. (gdb) info b
  4. Num Type Disp Enb Address What
  5. 1 breakpoint keep n 0x0000555555554883 in main() at example.cpp:12
  6. (gdb) dis b 1 //禁用1号断点
  7. (gdb) r
  8. Starting program: /home/acceptedzhs/example
  9. 10
  10. 3628800 //不经过该断点了
  11. [Inferior 1 (process 2695) exited normally]
  12. (gdb) en b 1 //启用该断点
  13. (gdb) r
  14. Starting program: /home/acceptedzhs/example
  15. Breakpoint 1, main () at example.cpp:12
  16. 12 scanf("%d",&a); //又经过该断点了
  17. (gdb) d breakpoints 1 //删除
  18. (gdb) r
  19. The program being debugged has been started already.
  20. Start it from the beginning? (y or n) y
  21. Starting program: /home/acceptedzhs/example
  22. 10
  23. 3628800 //又不经过断点了
  24. [Inferior 1 (process 3516) exited normally]
  25. (gdb)

2.8 获取帮助

有时,我们可能忘记某个命令的用法。这该怎么办呢?

命令:help(简写为h)

格式:help 待查询的命令(待查询的命令可以用简写)

作用:显示待查询的命令的帮助。

例子:

  1. (gdb) h b
  2. Set breakpoint at specified location.
  3. break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
  4. PROBE_MODIFIER shall be present if the command is to be placed in a
  5. probe point. Accepted values are `-probe' (for a generic, automatically
  6. guessed probe type), `-probe-stap' (for a SystemTap probe) or
  7. `-probe-dtrace' (for a DTrace probe).
  8. LOCATION may be a linespec, address, or explicit location as described
  9. below.
  10. With no LOCATION, uses current execution address of the selected
  11. stack frame. This is useful for breaking on return to a stack frame.
  12. ...(省略若干行)...

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入门教程的更多相关文章

  1. 超强、超详细Redis数据库入门教程

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...

  2. 超强、超详细Redis数据库入门教程(转载)

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下   [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...

  3. 超详细Redis数据库入门教程

    [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用redis4.学会安装redis5.学会启动redis6.使用redis客户端7.redis数据结构 – 简介8.redis ...

  4. 有shi以来最详细的正则表达式入门教程

    本篇文章文字内容较多,但是要学习正则就必须耐心读下去,正则表达式是正则表达式其实并没有想像中的那么困难,但是想要熟练的掌握它,还是需要下功夫勤加练习的.这里讲一些正则表达式的语法和学习方法,大家还要多 ...

  5. 一份详细的asyncio入门教程

    asyncio模块提供了使用协程构建并发应用的工具.它使用一种单线程单进程的的方式实现并发,应用的各个部分彼此合作, 可以显示的切换任务,一般会在程序阻塞I/O操作的时候发生上下文切换如等待读写文件, ...

  6. gulp详细入门教程

    本文链接:http://www.ydcss.com/archives/18 gulp详细入门教程 简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优 ...

  7. ant使用指南详细入门教程

    这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装.验证安装.使用方法.使用实例.ant命令等内容,需要的朋友可以参考下 一.概述 ant 是一个将软件编译.测试.部署等步骤联系在一起 ...

  8. gulp详细入门教程(转载)

    本文转载自: gulp详细入门教程

  9. <转载>ant使用指南详细入门教程 http://www.jb51.net/article/67041.htm

    这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装.验证安装.使用方法.使用实例.ant命令等内容,需要的朋友可以参考下 一.概述 ant 是一个将软件编译.测试.部署等步骤联系在一起 ...

随机推荐

  1. CLP(FD)有限域上的约束逻辑式编程

    译自http://www.pathwayslms.com/swipltuts/clpfd/clpfd.html#_simple_constraints,SWI-Prolog官网所推荐的进阶教程.目前还 ...

  2. Tensorflow学习笔记No.2

    使用函数式API构建神经网络 函数式API相比于keras.Sequential()具有更加灵活多变的特点. 函数式API主要应用于多输入多输出的网络模型. 利用函数式API构建神经网络主要分为3步, ...

  3. ubuntu20 使用命令安装 mongodb

    安装 mongodb sudo apt-get install mongodb -y mongodb 服务管理 # 启动 mongodb 服务 service mongodb start # 关闭 m ...

  4. 同一台电脑同时使用gitHub和gitLab

    工作中我们有时可能会在同一台电脑上使用多个git账号,例如:公司的gitLab账号,个人的gitHub账号.怎样才能在使用gitlab与github时,切换成对应的账号,并且免密?这时我们需要使用ss ...

  5. 氵0x a

    从今天开始记录这些东西,希望以后自己不出现在这上

  6. 接入WxPusher微信推送服务出现错误:Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

    背景 使用WxPusher微信推送服务 ,可以及时的将服务的一些运行异常信息,发送到自己的微信上,方便了解服务的运行状态(PS:这个服务是免费的). 你可以在这里看到WxPusher微信推送服务的接入 ...

  7. fastdfs之同一台storage server下包含多个store path

    一,查看本地centos的版本 [root@localhost lib]# cat /etc/redhat-release CentOS Linux release 8.1.1911 (Core) 说 ...

  8. flutter——android报错建议Suggestion: add 'tools:replace="android:label"'

    问题: 安装了一个新包,android出现了报错,建议add 'tools:replace="android:label"'. 原因: 项目application的label属性冲 ...

  9. 主流开源分布式图数据库 Benchmark

    本文由美团 NLP 团队高辰.赵登昌撰写 首发于 Nebula Graph 官方论坛:https://discuss.nebula-graph.com.cn/t/topic/1377 1. 前言 近年 ...

  10. 如何使用性能分析工具定位SQL执行慢的原因?

    但实际上 SQL 执行起来可能还是很慢,那么到底从哪里定位 SQL 查询慢的问题呢?是索引设计的问题?服务器参数配置的问题?还是需要增加缓存的问题呢?性能分析来入手分析,定位导致 SQL 执行慢的原因 ...