软件发布模式主要有 debug 和 release 两种。以 VS2022 为例,一般程序员在写代码开发期间处于 debug 模式,当软件自测通过发布时是以 release 模式来发布的。
Windows 中在 VS 编好的程序默认在 debug 模式下是可以直接调试的,但在 Linux 下编译好的程序能否直接进行调试?答案是不能!下面举个例子。
GDB 是 Linux 下调试 C/C++ 程序的核心工具。编译时需添加-g 选项以包含调试信息。本文介绍了 GDB 的基本操作,包括启动调试、查看源代码、设置断点、单步执行(next/step)、查看变量值及修改变量。进阶功能涵盖条件断点、监视变量变化(watch)以及使用 cgdb 提升体验。通过二分法定位问题区域,结合栈帧信息和局部变量分析,可有效解决程序逻辑错误。

软件发布模式主要有 debug 和 release 两种。以 VS2022 为例,一般程序员在写代码开发期间处于 debug 模式,当软件自测通过发布时是以 release 模式来发布的。
Windows 中在 VS 编好的程序默认在 debug 模式下是可以直接调试的,但在 Linux 下编译好的程序能否直接进行调试?答案是不能!下面举个例子。
随便找一个可执行程序演示一下:
gcc/g++默认的工作模式是 release 模式,编译好的代码不能直接进行调试。所以想要让自己的程序能够在 debug 模式下调试,需要在程序编译进行翻译的时候,需要加一个 -g 选项:
一句话总结:在 Linux 下程序要进行调试,必须处于 debug 模式,也就是在编译时加上 -g 选项。再说得直白一点,gdb 调试就是要调试携带调试信息的可执行文件!
进入 gdb 开始调试:gdb 可执行文件
退出 gdb 环境:quit
进入 gdb 环境后想要查看程序的源代码输入 list(简写为 l 即可)并回车;也可以输入 l 行号,表示从某行开始展示源代码。使用 l 指令只能显示一部分源代码,若想显示剩余部分源代码按回车键即可。
(此处展示源代码列表)
也可以 l 指定文件名 查看指定文件的源代码。
打断点:b 行号
将断点打在第 19 行再输入 r(run)运行,就能直接停到断点处了。
想要直接将程序跑完,输入 c(continue)就可以了:
(此处展示程序运行结果)
前面已经演示了 gdb 的基本使用,但是使用的过程中会发现 gdb 太恶心人了,想查看源代码结果还与调试信息混在一起了,所以后面的演示统一使用 cgdb,cgdb 和 gdb 是一模一样的,但是 cgdb 可以动态呈现我们的源代码。
用于测试 gdb 使用方法的样例代码:
#include <stdio.h>
int Sum(int s, int e){
int result = 0;
for(int i = s; i <= e; i++){
result += i;
}
return result;
}
int main(){
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}
cgdb 在默认环境下是没有安装的,下面推荐直接安装:
Ubuntu:sudo apt-get install -y cgdb
Centos:sudo yum install -y cgdb
安装完成再重新打开使用(cgdb 可执行文件)
可以看到,上面是代码界面,下面是 debug 界面,更加方便我们调试代码了;想要退出还是一样输入 quit 回车
r(run):
r就类似于 vs2022 当中的快捷键F5,意思是调试并运行,直接将程序从入口运行到程序结束。
(此处展示运行结果)
但通常我们习惯上不会直接让程序直接运行结束,而是让其停到程序的某个位置,这时候就需要给程序打上断点
b(break):
b就是 vs 中的F9,给程序的指定行号打上断点用法:b 源文件:行号b 源文件:函数名b 行号
(此处展示打断点)
打好断点之后输入命令 info b,就可以查看所打的断点的信息以及编号
(此处展示断点信息)
使用过的断点不想用了也可以删除,输入 d 断点编号 回车即可删除想删除的断点(d+ 行号删除不了断点,必须输入 d+ 断点编号)
(此处展示删除断点)
断点编号是以线性递增的形式往上加的,即如果打了三个断点但都删除了,接下来再打新的断点那断点编号为 4;如果退出 gdb 再重新进入才会从 1 开始递增。
断点是可以使能的! 也就是说断点使用过后可以不删,直接禁用即可
为什么要有禁用功能而不直接删除?因为有时候比如打了 10 个断点,调试完成后也许要去掉其中 6 个断点,但是如果把断点删除了下次要再调试时会导致自己忘记之前打过的断点在哪。所以这个使能效果还是很有用的,他能帮助记录下之前调试的痕迹!
disable:
disable 断点编号表示禁用断点 enable:enable 断点编号表示给断点使能
(此处展示使能状态)
图中黄色框内 y 表示该断点是否被使能,如果为 n 即表示未被使能,也就是该断点此时已经被禁用
(此处展示颜色区别)
从上图可以看出,被使能的断点显示行号是红色的,未被使能的断点行号显示为黄色
使用 vs 时打完断点,运行程序到断点处停下来时就可以按 F10(逐过程)F11(逐语句)了
gdb 中逐过程和逐语句分别为:
n(next):逐过程(代码一行一行执行,遇到函数时会直接跳到下一行代码,不进入函数内部) s(step):逐语句(一行一行执行,遇到函数时会进入函数内部)
(此处展示逐语句)
当输入 s 进入 sum 函数内部时,如果测试到函数内部某个逻辑没有问题想直接退出函数,可以输入 finish 回车,直接跳回到进入函数的入口行
这里说明一下为什么进入函数前是在第 19 行而函数退出时又是 19 行:因为该行代码是两个调用,一个是 Sum 函数,一个是将 Sum 函数的最终结果返回给 n,所以直接 finish 退出函数时还是在第 19 行。
p : 输入
p 变量名可以查看一个临时变量:
上面提到了 19 行的代码,将 Sum 的返回值赋给 n 是两个步骤,通过下图就可以验证:当退出 Sum 函数时箭头指向 19 行,然后输入
p n来查看 n 的值,可以看到此时 n 为一个随机数;当程序再往下运行时再查看一次 n 的值,这时 n 就为 Sum 函数计算后的返回值了
(此处展示变量值变化)
| 命令 | 作用说明 | 样例示例 |
|---|---|---|
| list/l | 显示源代码,从上次位置开始,每次列出 10 行 | list/l 10 |
| list/l 函数名 | 列出指定函数的源代码 | list/l main |
| list/l 文件名:行号 | 列出指定文件的源代码 | list/l mycmd.c:1 |
| r/run | 从程序开始连续执行 | run |
| n/next | 单步执行,不进入函数内部,逐过程 F10 | next |
| s/step | 单步执行,进入函数内部,逐语句 F11 | step |
| break/b [文件名:] 行号 | 在指定行号设置断点 | break 10 break test.c:10 |
| break/b 函数名 | 在函数开头设置断点 | break main |
| info break/b | 查看当前所有断点的信息 | info break |
| finish | 执行到当前函数返回,然后停止 | finish |
| print/p 表达式 | 打印表达式的值 | print start+end |
| p 变量 | 打印指定变量的值 | p x |
| set var 变量=值 | 修改变量的值 | set var i=10 |
| continue/c | 从当前位置开始连续执行程序 | continue |
| delete/d breakpoints | 删除所有断点 | delete breakpoints |
| delete/d breakpoints n | 删除序号为 n 的断点 | delete breakpoints 1 |
| disable breakpoints | 禁用所有断点 | disable breakpoints |
| enable breakpoints | 启用所有断点 | enable breakpoints |
| info/i breakpoints | 查看当前设置的断点列表 | info breakpoints |
| display 变量名 | 跟踪显示指定变量的值(每次停止时) | display x |
| undisplay 编号 | 取消对指定编号的变量的跟踪显示 | undisplay 1 |
| until 行号 | 执行到指定行号 | until 20 |
| backtrace/bt | 查看当前执行栈的各级函数调用及参数 | backtrace |
| info/i locals | 查看当前栈帧的局部变量值 | info locals |
| quit | 退出 GDB 调试器 | quit |
断点的本质:是把代码进行块级别的划分,在不清楚程序出错的位置的情况下,以块为单位来快速定位区域,方便查找问题所在。
在代码只有二三十行时给程序分成四等分并打上三个断点,输入 r 回车让程序运行至第一个断点处,如果没有发生错误,说明问题不是出在第一个区域,依次类推,让程序一块块的执行,直到找到出错的位置。
在调试代码时,通常会用二分查找的方式找到程序的问题所在:
在 vs 中,让程序从一个断点处执行到另一个断点位置停下,要按 F5,而 gdb 中则是输入:
c(continue):输入 c 回车,可以让程序从一个断点执行到下一个断点停止。
(此处展示继续执行)
finish 并回车,直接将该函数跑完,查看问题是否出现在该函数内部until 行号 回车直接执行完该部分finish:直接执行完所调用的函数,查看问题是否出现在函数内部 until:
until 行号,表示如果确定函数内部某部分没有问题,可以将局部区域快速执行完
当调试过程中已经将问题出现的范围缩得很小时,就可以通过检查一些变量的值、地址等信息,再推理出问题的根本所在
前面已经提到可以通过 p 变量名 来查看代码运行到某处时某个变量的信息,但这个方法不足以完成监视人任务
想要做到如 vs 的监视功能,可以使用以下命令:
display:
display 地址/变量表示查看变量的值或者地址等信息 undisplay:undisplay 编号表示删除该编号想要查看的信息 info locals:info locals表示进入一个函数时查看还函数内部所有临时变量的值
(此处展示监视变量)
这里注意一下:display 时该信息前都会有一个编号,和断点类似,用 undisplay 删除查看的信息时一样要通过信息编号来删除
watch:
watch 变量名表示执行时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB 会暂停程序的执行,并通知使用者
(此处展示 watch 效果)
在调试过程中某个值会不断发生改变,watch 该变量 就可以监视该变量的值在变化前后的对比。
想要删除它们也是像删除断点一样 d 编号 即可。
watch 最重要的点在于:如果你有一些变量不应该改,但是你怀疑是因为它被修改而导致了问题,你可以 watch 它,如果变化了,就会通知你
set var:
set var 变量名=n表示在调试过程中发现是某个变量导致出现问题,直接给该变量赋值再调试,如果赋值后发现没有错说明就是该变量导致出错,这时再去修改源代码,就不需要先去修改源代码再重新编译再调试了
(此处展示 set var 效果)
图释:本来计算结果是 5050,但是由于 flag 为 0 所以导致结果变为 0;所以此时用 set var flag=1 发现确实是因为 flag 的值有问题。所以只需要在调试时用 set var 给某个变量赋值看是否符合预期,如果是,那么这时再按此修改源代码即可。
前面已经提到了如何打断点,以及断点使能、断点的删除。可是如果我既不想删、也不想使能断点,只想在某个条件下触发断点:
b 行号 if 变量=n:表示当某个变量的值等于 n 时触发该断点;也就是说,如果在循环内部打了一个条件断点,当程序运行时会直接停到该行,但是此时该变量的值为 n,就可以查看该变量值为 n 时其他变量的值为多少
打断点示例:
(此处展示条件断点)
使用条件断点案例:
(此处展示条件断点运行)
如果想要在该条件断点已经存在的条件下新增断点,该如何操作?
condition 断点编号 变量==n:表示给已经存在的断点新增条件
解释新增断点条件:如果已经存在一个条件断点为 i == 10,那运行程序输入 c 到达该条件处停下,那么还可以在该断点下新增条件如 i == 20,此时再输入 c 就会到达 i ==20 处停下来,也就是一个断点如果有两个条件,那么它可以分别在达到各自条件下停下来
条件断点添加常见两种方式:1. 新增 2. 给已有断点追加注意两者的语法有区别,不要写错了。新增:
b (行号/文件名:行号/函数名) if i == 30(条件) 给已有断点追加:condition 2 i==30,其中 2 是已有断点编号,没有 if
cgdb 分屏操作 ESC 进入代码屏,可按键盘上下键查看源代码;i 回到 gdb 调试屏

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online