欢迎光临
我们一直在努力

Linux 调试神器 gdb/cgdb 实战指南:从基础用法到高级技巧(含 cgdb 可视化操作)

在这里插入图片描述

🔥草莓熊Lotso:个人主页

❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》

✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:

在这里插入图片描述


文章目录

  • 前言:
  • 一. gdb 预备知识:编译与模式
    • 1.1 测试代码准备
    • 1.2 程序的两种发布模式
  • 二. gdb/cgdb 基础命令:核心功能全覆盖
    • 2.1 基础概念和指令速查
    • 2.2 断点设置与管理
    • 2.3 程序运行与暂停
    • 2.4 变量查看与修改
    • 2.5 堆栈跟踪
  • 四. gdb 高级技巧:解决复杂场景
    • 4.1 变量监视(watch):捕捉变量异常修改
    • 4.2 条件断点:精准定位特定场景
  • 五. 实战案例:用 gdb 技巧快速定位真实 bug
  • 结尾:

前言:

在 Linux 开发中,调试是定位问题的核心环节。相比于 Windows 下的可视化 IDE 调试工具,Linux 终端下的 gdb(GNU Debugger)凭借强大的命令行功能,成为 C/C++ 开发者的必备工具 —— 它支持断点设置、变量监视、单步执行、堆栈跟踪等核心功能,甚至能应对多线程、条件断点等复杂场景。本文结合从 gdb 基础配置、cgdb 常用命令,到高级技巧(条件断点、变量监视、修改变量),再到实战案例,带你从零掌握 gdb 调试,解决开发中的各类 bug,大幅提升问题定位效果


一. gdb 预备知识:编译与模式

1.1 测试代码准备

#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;
}

1.2 程序的两种发布模式

Linux 下 gcc/g++ 编译的程序默认是 release 模式(优化编译,无调试信息),无法直接用 gdb 调试。要启用调试,必须在编译时添加-g选项,生成 debug 模式 的可执行文件(包含调试信息)。

  • 程序的发布方式有两种,debug 模式和 release 模式,Linux gcc/g++ 出来的二进制程序,默认是 release 模式。
  • 要使用 gdb 调试,必须在源代码生成二进制程序的时候,加上 -g 选项,如果没有添加,程序无法被编译

✅ 编译命令优化:

# release模式(默认):无调试信息,无法用gdb调试
gcc mycmd.c -o mycmd_release

# debug模式:添加-g选项,生成调试信息
gcc mycmd.c -o mycmd_debug -g

在这里插入图片描述


二. gdb/cgdb 基础命令:核心功能全覆盖

2.1 基础概念和指令速查

gdb/cgdb 命令支持缩写(如break可缩写为b,next可缩写为n),下面列出最常用的基础命令,结合示例帮你快速上手。

  • 开始:gdb binfile(二进制文件) cgdb binfile(二进制文件)
  • 退出:ctrl + d 或 quit 调试命令
  • 推荐使用:cgdb 的核心优势是 “可视化分屏”,上半屏显示代码(带断点标注、当前执行行高亮),下半屏是 gdb 命令行,操作逻辑与 gdb 一致,但更直观。
  • cgdb下载指令:Ubuntu: sudo apt-get install -y cgdb Centos: sudo yum install -y cgdb

在这里插入图片描述 📊 命令速查表:

命令作用样例
list/l 文件名:行号 列出指定文件的源代码 list/l mycmd.c:1
r/run 从程序开始连续执行 run
n/next 单步执行,不进入函数内部(逐过程)F10 next
s/step 单步执行,进入函数内部(逐语句)F11 step
break/b [文件名:]行号 在指定行号设置断点 break/b 10
break/b 函数名 在函数开头设置断点 break/b test.c:10
info break/b 查看当前所有断点的信息 info break
finish 执行到当前函数返回,然后停止 finish
print/p 表达式 打印表达式的值 print/p startend
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

在这里插入图片描述

2.2 断点设置与管理

断点是调试的核心,gdb/cgdb 支持按行号、函数名、文件名设置断点,还能管理断点(删除、禁用、启用)。

🔴 基础断点设置:

# 1. 按行号设置断点(当前文件)
(gdb) break 20 # 缩写:b 20
# 2. 按函数名设置断点
(gdb) break main # 缩写:b main(在main函数开头设断点)
# 3. 按文件名+行号设置断点(多文件项目)
(gdb) break test.c:15 # 缩写:b test.c:15

🔴 断点管理:

# 查看所有断点信息(编号、位置、状态)
(gdb) info break # 缩写:info b
# 删除所有断点
(gdb) delete breakpoints # 缩写:d breakpoints
# 删除指定编号的断点(编号通过info b查看)
(gdb) delete breakpoints 1 # 缩写:d b 1
# 禁用所有断点(不删除,后续可启用)
(gdb) disable breakpoints
# 启用所有断点
(gdb) enable breakpoints

在这里插入图片描述

2.3 程序运行与暂停

命令缩写功能描述示例
run r 从程序开头开始执行,遇到断点暂停 (gdb) r
continue c 从当前位置继续执行,直到下一个断点或程序结束 (gdb) c
next n 单步执行(逐过程,不进入函数内部) (gdb) n(类似 IDE 的 F10)
step s 单步执行(逐语句,进入函数内部) (gdb) s(类似 IDE 的 F11)
finish 执行到当前函数返回,然后暂停 (gdb) finish
until 行号 u 快速执行到指定行(跳过循环,效率更高)(不能跨函数,不能是空行,不能往前) (gdb) until 20(执行到第 20 行)

2.4 变量查看与修改

调试时经常需要查看变量值、修改变量来验证问题原因,gdb 提供了灵活的变量操作命令。

🟡 查看变量:

# 1. 打印变量值(支持表达式)
(gdb) print start # 缩写:p start(打印变量start)
(gdb) print start + end # 打印表达式结果
# 2. 跟踪显示变量(每次暂停时自动打印)
(gdb) display result
# 3. 取消跟踪变量(通过编号,用info display查看)
(gdb) undisplay 1 # 取消编号1的跟踪变量
# 4. 查看当前栈帧的所有局部变量
(gdb) info locals # 缩写:i locals

🟡 修改变量: 通过set var命令可直接修改程序中的变量值,验证假设(如排除变量赋值错误,后面实战示例中会用到):

# 格式:set var 变量名 = 新值
(gdb) set var flag = 1 # 将变量flag改为1

2.5 堆栈跟踪

当程序崩溃(如段错误)时,堆栈跟踪是定位问题的关键,gdb 的backtrace命令可查看函数调用链。

# 查看当前执行栈的函数调用链(包含参数)
(gdb) backtrace # 缩写:bt

🟢 示例输出

#0 Sum (s=1, e=100) at mycmd.c:9
#1 0x00005555555551d2 in main () at mycmd.c:20

  • #0:当前正在执行的函数(Sum);
  • #1:调用当前函数的上层函数(main);
  • 可快速定位崩溃发生在哪个函数、哪一行。

四. gdb 高级技巧:解决复杂场景

基础命令能应对简单问题,而高级技巧(变量监视,set var,条件断点)能帮你处理更复杂的场景(如循环中的 bug、变量异常修改)。

4.1 变量监视(watch):捕捉变量异常修改

如果怀疑某个变量被意外修改,watch命令可监视变量 —— 当变量值发生变化时,gdb 会自动暂停程序,并显示修改前后的值。

⭐ 用法示例:

# 1. 设置监视点(监视变量result)
(gdb) watch result # 缩写:watch result
Hardware watchpoint 2: result

# 2. 继续执行程序
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 0 # 修改前的值
New value = 1 # 修改后的值
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)

🔍 适用场景:

  • 全局变量被意外修改;
  • 函数参数值异常变化;
  • 循环中变量赋值错误。

📌 注意:

  • 如果你有一些变量不应该修改,但是你怀疑它修改导致了问题,你可以 watch 它,如果变化了,就会通知你。
  • 监视变量的变化

4.2 条件断点:精准定位特定场景

当需要在 “满足特定条件” 时才触发断点(如循环中i=30时),普通断点会频繁暂停,条件断点能大幅提升效率。

⚙️ 两种设置方式:

# 方式1:新增断点时直接加条件(推荐)
(gdb) break 9 if i == 30 # 第9行,i等于30时触发断点,需要if

# 方式2:给已有断点追加条件(断点编号通过info b查看)
(gdb) condition 2 i == 30 # 给2号断点追加条件:i==30,没有if

ℹ️ 实战场景:

循环中某个变量达到特定值时才触发断点,避免逐句执行循环:

# 示例:循环变量i=30时暂停,查看result值
(gdb) b 9 if i == 30
(gdb) r # 运行程序
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) p result # 查看此时result的值
$1 = 435

📌 注意:

  • 条件断点添加常见两种方式:1.新增 2.给已有断点追加条件
  • 注意两者的语法区别,不要写错了
  • cgdb 分屏操作 :ESC 进入代码屏,i 回到 gdb 屏

五. 实战案例:用 gdb 技巧快速定位真实 bug

下面通过一个实际案例,演示如何用 gdb 从 “现象” 到 “根源” 定位 bug,加深命令用法理解。

案例代码(cmd.c):

#include <stdio.h>

int flag = 0; // 故意设置错误值

int Sum(int s, int e)
{
int result = 0;
int i = s;
for (; i <= e; i++)
{
result += i;
}
return result * flag; // bug点: flag = 0 导致结果始终为 0
}

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;
}

现象: 编译运行后,结果始终为0(预期是 1-100 的和5050)。

🚀 调试步骤:

  • 编译 debug 版本:gcc -o cmd.exe cmd.c -g;
  • 启动 cgdb:cgdb cmd.exe;

[Lotso@VM-4-4-centos lesson13]$ make
gcc -o cmd.exe cmd.c -g
[Lotso@VM-4-4-centos lesson13]$ cgdb cmd.exe

  • 设置断点:在 main函数第 21 行(int n = Sum(start, end);)设断点:

(gdb) b21
Breakpoint 1 at 0x4005d4: file cmd.c, line 21.

  • 运行程序:(gdb) r ,程序在断点处暂停;
  • 单步进入函数:(gdb) s (进入 Sum 函数)
  • 跟踪循环执行:用 n 单步执行(可以直接使用 until 的技巧),直到循环结束,查看 result 值

(gdb) until 13 # 快速执行到 return 语句
Sum (s=1, e=100) at cmd.c:13
(gdb) presult # 查看 result 值(正确为 5050)
$1 = 5050
(gdb) pflag # 查看flag 值(错误为 0)
$2 = 0
(gdb) presult * flag # 查看返回结果(错误为 0)
$3 = 0

  • 验证假设:set var 修改 flag 值,验证是否是 bug 根源:

(gdb) set var flag=1 # 修改变量 flag 为 1
(gdb) n # 执行 return 语句
(gdb) n
main () at cmd.c:22
(gdb) n # 执行 printf
running done, result is: [1-100]=5050

✅ 结论: bug 根源是flag变量初始值为0,导致Sum函数返回值始终为0,修改为flag=1即可解决。


结尾:

🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:gdb 是 Linux 调试的 “基础核心”,而 cgdb 则是 “可视化增强”—— 两者结合既能发挥 gdb 的强大命令行功能,又能通过分屏可视化解决纯命令行的操作痛点,尤其适合复杂代码的调试场景,建议结合实际项目多练习(如单步执行、条件断点、变量监视),形成肌肉记忆。

✨把这些内容吃透超牛的!放松下吧✨

ʕ˘ᴥ˘ʔ

づきらど

在这里插入图片描述

赞(0)
未经允许不得转载:171主机测评 » Linux 调试神器 gdb/cgdb 实战指南:从基础用法到高级技巧(含 cgdb 可视化操作)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址