如何使用 GDB 调试 C/C++ 程序及常用命令详解
一、GDB 在不同场景下的使用方法及常用命令
场景 1:基础程序调试(单进程、无复杂逻辑)
适用于调试简单的单文件程序,定位语法外的逻辑错误(如变量计算错误、循环异常等)。
核心流程与命令:
-
编译带调试信息的程序
必须在编译时添加-g选项(生成调试符号,包含变量名、行号等信息),否则 GDB 无法正常调试。示例:gcc -g -o test test.c(C 程序);g++ -g -o test test.cpp(C++ 程序)。 -
启动 GDB
- 命令:
gdb ./test(直接启动调试);或gdb -q ./test(安静模式,不显示 GDB 版本信息)。 - 退出 GDB:
quit(缩写q)。 - 查看命令帮助:
help <命令>(缩写h),如help break可查看断点命令说明。
- 命令:
-
查看源代码
list <位置>(缩写l):显示指定位置的源代码,如l 10显示第 10 行附近代码,l main显示 main 函数代码,默认每次显示 10 行。list <start>,<end>:显示从 start 到 end 的源代码,如l 5,20显示 5-20 行。show listsize:查看list命令每次显示的行数(默认 10 行);set listsize <N>:设置每次显示 N 行,如set listsize 20。
-
设置断点
断点是调试的核心,用于在指定位置暂停程序执行。-
基础断点:
break 位置(缩写b),位置支持行号(b 10)、文件名 + 行号(b test.c:20)、函数名(b main)、地址(b *0x400520)。 -
临时断点:
tbreak 位置(缩写tb),触发一次后自动删除。 -
条件断点:
break 位置 if 条件,仅当条件满足时中断,如break test.c:20 if i == 10。 -
断点触发后自动执行命令:
break 位置 command <命令序列> end, 如在 test.c:10 设置断点后,自动打印 i 和调用栈:1
2
3
4
5b test.c:10
command
p i
bt
end
-
-
断点管理
- 查看断点:
info breakpoints(缩写info b),显示所有断点编号、状态、命中次数等。 - 删除断点:
delete 断点编号(缩写d),如d 1删除编号 1 的断点;d不带参数删除所有。 - 禁用 / 启用断点:
disable 断点编号(缩写dis)、enable 断点编号(缩写en);enable once 断点编号仅生效一次。 - 清除指定位置断点:
clear <位置>,如clear main删除 main 函数入口的断点。
- 查看断点:
-
运行程序
- 启动程序:
run [参数](缩写r),如run 10 20传递命令行参数。 - 启动并在 main 函数第一行中断:
start。 - 程序中断后继续执行:
continue(缩写c),执行到下一个断点。
- 启动程序:
-
单步执行
next(缩写n):执行当前行,不进入函数调用。step(缩写s):执行当前行,进入函数调用。- 按汇编指令单步:
nexti(ni)、stepi(si),分别对应不进入和进入函数的汇编级执行。 - 跳出当前函数:
finish,执行完当前函数并返回到调用处。 - 执行到指定行:
until <行号>(缩写u),适合快速跳过循环。
-
查看 / 修改变量
- 查看变量 / 表达式:
print 变量/表达式(缩写p),如p a + b;按格式打印(x十六进制、d十进制等):p /x num。 - 打印数组:
print *数组名@长度,如p *arr@5打印数组前 5 个元素。 - 修改变量:
set variable 变量名=值(缩写set var),如set var i=5。 - 自动显示变量:
display 变量名(缩写disp),程序每次中断时自动打印;undisplay 编号取消。 - 查看局部变量:
info locals;查看函数参数:info args。 - 查看变量类型:
whatis 变量(如whatis ptr);查看详细类型定义:ptype 变量/类型(如ptype struct Student)。
- 查看变量 / 表达式:
-
查看调用栈
backtrace(缩写bt):查看函数调用链;bt N显示前 N 层,bt -N显示后 N 层。- 切换栈帧:
frame N(缩写f N),查看第 N 层栈的局部变量;up <N>、down <N>上下移动栈帧。 - 查看当前栈帧详情:
info frame(缩写info f)。
场景 2:多线程程序调试
多线程程序易出现竞态条件、死锁等问题,GDB 提供专门的多线程调试命令。
核心命令:
-
查看线程信息:
info threads,显示所有线程编号、状态、当前执行位置(*标记当前线程)。 -
切换线程:
thread 线程编号,如thread 2切换到线程 2。 -
对线程执行命令:
thread apply <线程号> <命令>,如thread apply 2 bt打印线程 2 的调用栈;thread apply all bt打印所有线程栈。 -
控制线程调度:
set scheduler-locking [on|off|step]on:仅当前线程执行,其他线程暂停(避免干扰);off:所有线程正常调度(默认);step:单步执行时仅当前线程移动。
场景 3:程序崩溃定位(Core Dump 调试)
当程序因内存错误(如段错误)崩溃时,可生成core文件(内存转储),通过 GDB 分析崩溃原因。
核心流程:
-
开启 Core Dump 功能
- 临时开启:
ulimit -c unlimited(当前终端有效); - 永久开启:修改
/etc/security/limits.conf,添加* soft core unlimited(需重启)。
- 临时开启:
-
生成并分析 core 文件
程序崩溃后生成core.PID文件,用 GDB 加载:gdb ./程序名 core文件(如gdb ./test core.12345)。加载后执行backtrace(bt),查看崩溃时的调用栈,定位崩溃位置。- 补充命令:
core-file <core文件>,在 GDB 交互界面中加载 core 文件。
- 补充命令:
在使用 systemd 作为初始化系统的 Linux 发行版使用coredumpctl来处理程序崩溃
1 | coredumpctl list #列出所有core dump记录 |
场景 4:远程调试(如嵌入式设备、服务器程序)
适用于调试运行在远程设备上的程序,本地通过 GDB 连接远程进程。
核心流程:
-
远程端启动 GDB Server
远程设备安装gdbserver,启动命令:gdbserver 远程IP:端口 程序名(如gdbserver 192.168.1.100:1234 ./test)。 -
本地 GDB 连接远程
本地启动 GDB:gdb ./test,执行target remote 远程IP:端口(如target remote 192.168.1.100:1234),连接后即可像调试本地程序一样操作。
场景 5:内存与寄存器调试
用于直接检查内存数据或 CPU 寄存器状态,适合调试内存错误(如越界、野指针)。
核心命令:
-
查看内存:
x/<n><f><u> 地址(x即 examine)n:单元数;f:格式(x/d/u/o/t/c/f);u:单元大小(b/h/w/g)。- 示例:
x/10xw 0x7fffffffde40(以 16 进制显示 10 个 4 字节单元);x/s 地址以字符串格式查看。
-
查看寄存器
info registers:显示所有通用寄存器;info registers <寄存器名>:显示指定寄存器(如info registers rip)。- 修改寄存器:
set $<寄存器>=<值>,如set $rip=0x400520(谨慎使用)。
场景 6:多进程调试
针对fork()创建的多进程程序,控制跟踪父进程或子进程。
核心命令:
-
set follow-fork-mode [parent|child]:设置fork()后跟踪的进程(默认跟踪父进程)。 -
set detach-on-fork [on|off]:设置是否分离未跟踪的进程(默认分离,未跟踪进程继续运行)。 -
info inferiors:显示所有进程信息;inferior <进程号>:切换到指定进程。
二、GDB 使用注意事项
-
编译必须带
-g选项
若未加-g,GDB 无法识别行号、变量名等调试信息,只能看到汇编代码,失去调试意义。 -
避免高优化级别编译
用-O2、-O3等优化选项编译时,编译器可能删除变量、重排代码,导致 GDB 无法查看变量或单步混乱。调试时建议用-O0(无优化)。 -
处理信号时需谨慎
程序可能收到信号(如SIGINT、SIGSEGV),默认 GDB 会捕获并中断。可通过handle 信号 动作设置:- 如
handle SIGINT nostop(收到Ctrl+C时不中断);handle SIGSEGV stop print(段错误时中断并打印)。
- 如
-
区分
next与stepnext:不进入函数,适合快速跳过;step:进入函数,适合调试函数内部逻辑。新手易混淆导致效率低。
-
Core 文件的存储与清理
core 文件可能很大(与程序内存占用一致),生产环境需限制大小(如ulimit -c 1024),并及时清理无用文件。 -
远程调试需保持程序一致性
本地与远程程序需为同一源码、同一编译选项编译,否则调试信息不匹配。
三、GDB 扩展知识点
1. 监视点(高级断点)
-
watch 变量:变量被修改时中断; -
rwatch 变量:变量被读取时中断; -
awatch 变量:变量被读取或修改时中断。示例:watch count,当count值变化时自动中断,无需手动设置断点。
2. GDB 脚本与自动化
-
.gdbinit配置文件:启动 GDB 时自动加载,可定义别名或自动执行命令。示例:在~/.gdbinit中添加alias ll = info locals(用ll快速查看局部变量)。 -
自定义命令:通过
define命令名定义批量操作,如:
1
2
3
4
5define dbg
bt # 打印调用栈
info locals # 打印局部变量
print i # 打印变量i
end执行
dbg即可一次性执行上述命令。
3. 宏调试
C/C++ 宏在预处理阶段展开,默认 GDB 无法调试。需编译时添加-ggdb3(更详细调试信息)和-fmacro-prefix-map=.=(保留宏路径),然后:
-
info macro 宏名:查看宏定义; -
macro expand 宏调用:查看宏展开结果。
4. 断点保存与恢复
-
save breakpoints <文件>:将当前断点保存到文件(如save breakpoints bps.txt); -
source <文件>:恢复断点(如source bps.txt)。
5. 与 IDE 集成
GDB 可与 VS Code、Clion 等 IDE 集成,通过图形界面操作(点击设置断点、单步),底层依赖 GDB 核心功能。以 VS Code 为例,需在.vscode/launch.json中配置miDebuggerPath为 GDB 路径。