目录
1.cgdb
好,那么咱们今天来学习Linux中的调试器-cgdb:
那么咱们在学习这个之前,咱们必须得了解一件事情:
cgdb调试的时候,必须得使用debug版本,不可以使用release版本
(1)你直接用gcc/g++ -o code code.c编译出来的程序,是Release 版本。
- 特点:体积小、运行快,但不带任何调试信息。
- 后果:用 GDB 打开时会提示
(no debugging symbols found)------ 就像给你一本没有页码和目录的书,你根本没法定位到具体某一行代码。
(2)怎么编译出可调试的 Debug 版本?
需要加 -g 参数:gcc -o code-debug code.c -g
- 加了
-g后,编译器会在可执行文件里插入调试信息(比如行号、变量名、函数名)。 - 代价:可执行文件体积变大(图里对比:code 8520 字节,code-debug 9752 字节)。

- 验证:用
readelf -S code-debug | grep -i debug能看到一堆.debug_*段,这就是调试信息的藏身之处。 
(3)ELF 格式是什么?
图里提到 ELF 64-bit LSB executable,大白话讲:
- 可执行文件不是一堆乱码,而是有固定格式的「数据包」,Linux 下这个格式就叫ELF。
- 它里面分了很多段:代码段、数据段、调试段等,
readelf就是用来拆开这个包看里面结构的工具。
Debug vs Release 在整个开发流程里的角色:
(1)Debug 模式(开发阶段用)
- 给谁用:程序员自己。
- 作用:方便调试、定位 Bug,能一步步看代码执行、变量变化。
- 缺点:体积大、运行慢,因为带了一堆调试信息,给用户用完全没必要,还会暴露代码细节。
(2)Release 模式(上线 / 测试 / 给用户用)(不支持调试)
- 给谁用:测试人员、最终用户、老板。
- 作用:去掉所有调试信息,优化运行速度、减小体积,是最终交付的版本。
- 特点 :
- 测试人员测的就是这个版本(因为用户最终用的也是它)。
- 给用户的软件必须是 Release 版,不然体积太大,还可能泄露源码。
- 老板要的是能赚钱、能上线的版本,肯定是 Release 版。
(3)完整开发流程
开发写代码(Debug 模式)→ 提交给项目经理审核 → 合并代码 → 联合测试(用 Release 版测)→ 没问题就上线推广。
- 开发时:用 Debug 版,方便自己调试 Bug。
- 测试时:用 Release 版,模拟用户真实环境。
- 上线时:只发 Release 版,保证性能和体积。
用生活例子类比:
把写程序比作做奶茶:
- Debug 模式:就是你在店里试做的版本,杯上贴满便签,记录糖放了多少、茶煮了几分钟、奶盖厚度多少,方便你调整口味(调试 Bug),但给顾客喝肯定不行,太乱、太丑。
- Release 模式:就是最终卖给顾客的成品,包装干净、口感最优、体积刚好,没有任何多余的便签(调试信息),顾客喝着舒服,你也能卖钱。
总结:
- 默认编译 = Release 版:没法调试,适合上线给用户。
- 加
-g编译 = Debug 版:能调试,适合开发阶段自己用。 - 开发用 Debug,测试 / 上线用 Release:这是行业通用规则,既保证开发效率,又保证产品质量。
- 体积差异:Debug 版因为带调试信息,会比 Release 版大一些,这是正常的。
2.cgdb调试时的指令
那么l(或者不是数字1,是字母l,list):罗列你的代码
l 0:第0行居中,然后上下文显示代码
b:(breakpoint):打断点
b 20:给第20行打断点
r(run):开始调试
但是在这里,我需要强调一件事情,就是这个r跟start的区别
| 维度 | r (run) 指令 |
start 指令 |
|---|---|---|
| 核心行为 | 直接启动程序,无默认断点,程序会「一口气运行到结束」(除非你手动设了断点) | 启动程序时自动在main函数第一行设临时断点,程序启动后立刻暂停(不会直接退出) |
| 对你的实际影响 | 你执行r后,程序直接跑完sum函数、打印 10、退出进程,后续再执行r/until就会提示「The program is not being run」 |
你执行start后,程序停在int start=6;这一行,进程处于「运行但暂停」状态,此时执行until 13/next等指令都不会报错 |
| 重复执行的表现 | 第一次执行后进程退出,第二次执行会提示「程序已启动」,无论选 y/n 最终都无活跃进程 | 重复执行会自动终止旧进程,重新启动并停在main入口,始终有活跃进程,不会提示无进程 |
| 适用场景 | 1. 想快速验证程序整体运行结果(比如只看最终输出 10);2.已手动设断点(如b main)后,启动程序并停在断点处 |
1. 新手分步调试(逐行看代码执行);2. 避免程序直接退出导致的调试指令报错(你遇到的核心问题) |
| 调试流程复杂度 | 高:需要手动设断点才能避免程序直接退出,否则易出现「无进程」报错 | 低:自动设临时断点,开箱即用,新手友好 |
就是r是一口气全部执行完的,那么一口气全部执行完了,你就没有这个进程就退出了,那么自然后面再执行until,n,s,这样的指令也就没有办法进行了。但是你要是想不让程序一口气的执行退出,你就可以先打断点,然后再r,就会停在那个断点的地方。或者是采用start的指令,就是可以自动的停在main函数那里,之后你按until,n,s指令也就是没有任何问题了。
你遇到的「执行r后再执行指令提示无进程」,本质是:
r就像你按了播放键,电影(程序)直接从头播到尾,播完就结束了,再按播放键(r/until),播放器(GDB)会告诉你「没电影可播了」;start就像你按了播放 + 暂停键,电影刚开场(main函数开头)就暂停,此时你可以随意快进(until)、逐帧看(next),播放器始终有「正在播放的电影」,不会提示无内容。

包括你的finish也无法使用
那么咱们现在来打断点:

那么咱们现在打了这些断点,然后咱们查看一下这些断点

查看断点就是使用info b指令,并且,咱们可以发现断点前面的Num就是序列都是呈线性递增的关系的。然后咱们r,就是会跳到对应的第一个断点处了。
这里还需要注意一点,你如果说想重新调试的话,你直接再按一次r即可。
实现断点之间的跳跃(一个断点运行到下一个断点):用c(continue)

那么咱们接下来介绍两个指令:
n(next):就是程序逐行运行(单步执行,不进入函数内部)。
s(step):进入函数内部,是进入到函数内部的不包含
这个的第一行,也就是这个图片里面的第四行。然后你按回车也好,按s,n都可以,因为这个是在函数内部走的代码,所以只要不遇到又一个函数,你在这个里面s,n均可。
p 变量 (打印变量的值) 就是查看变量是如何变化的
但是不推荐,因为这样太麻烦了,咱们可以直接使用display,就是起到了监视窗口的功能,比如display result,这个display是,你添加监视之后,下面的每一次n,s,这个监视的变量都会存在,但是p不一样,p打印的变量只会存在于一次,就是你再下次n,s之后就会消失了。
而undisplay +序列号,就可以取消掉经常显示的那个变量(就是display的那个变量)
注意要是序列号,可不是undisplay的变量名

还有一个就是你如果说进入了一个函数之后,但是你不想执行这个函数了,你只想要结果,那么你可以在函数内部,直接finish即可完成。
watch:
啥是 watch?
简单说:watch就是给变量设个 "盯梢的",只要这个变量的值被修改,程序就会立刻暂停,告诉你 "有人改了这个变量!"。比如你怀疑某个变量被莫名改了值,不用逐行找,用 watch 一监控就知道。
怎么用?(大白话步骤)
- 用 CGDB 启动程序:
cgdb 你的程序名(比如cgdb test.out); - 先在变量初始化的地方打断点(比如
b main),然后run运行程序,让程序停在断点处; - 输入
watch 变量名(比如watch count),回车后会提示 "Hardware watchpoint 2: count",说明监控成功; - 继续
continue运行程序,只要count的值被改,程序会立刻暂停,还会告诉你 "旧值是 X,新值是 Y"。

那么咱们可以看到如图所示
注意你要写的是watch count
比如你有一些变量不应该被修改,但是你怀疑它修改而导致了问题,那么就可以去watch它,如果变化了,就会通知你
set var------ 调试时 "强行改值",不用重新编译
啥是 set var?
调试时发现某个变量值错了,想试试 "把它改成正确值后程序会不会正常",总不能改代码、重新编译吧?set var就是干这个的 ------ 直接在调试时修改变量值,实时验证你的猜想。
怎么用?(大白话步骤)
- 程序停在断点处(比如停在循环里);
- 输入
set var 变量名=新值(比如set var count=10); - 输入
print 变量名(比如p count),确认值已经改成 10 了; - 继续
continue运行,程序就会用这个新值执行。

可以改各种类型的变量:整数、字符串、数组元素(比如set var arr[0]=5)都能改。
添加条件断点 ------ 只在 "满足条件时" 暂停(条件满足时触发这个断点,没满足之前会当断点不存在)
啥是条件断点?
普通断点b 行号会让程序每次走到这行都暂停,但有时候我们只想 "当某个条件满足时才暂停"(比如循环跑 100 次,只在 i=50 时暂停),这时候就用条件断点,不用一次次continue跳过,省超多时间。
怎么用?(大白话步骤)
- 输入
b 行号 if 条件(核心格式); - 比如
b 8 if i==5(第 8 行,只有 i 等于 5 时才暂停); run运行程序,只有条件满足时才会停在这行。


disable +Num(断点序列号),是禁用掉这个断点


可以发现,禁用掉的这个断点的Enb是n,就代表不可用这个断点。然后正常的可用断点是红色的,而不可用的断点是黄色的。
好了,那么咱今天的cgdb就讲到这里了。