一、引言:为什么gdb是Linux开发者的必备技能?
在Linux环境下开发C/C++程序时,遇到bug是家常便饭。无论是逻辑错误、内存泄漏还是运行时崩溃,一款强大的调试工具都能帮我们快速定位问题根源。gdb(GNU Debugger) 作为Linux系统中最常用的命令行调试工具,支持断点设置、变量查看、单步执行等核心功能,是开发者排查问题的"利器"。
本文将从最基础的"如何生成可调试程序"讲起,逐步介绍gdb的启动方式、核心调试命令及实战技巧,帮助新手快速掌握gdb的使用方法。
@[toc]
二、前提:生成可调试的程序
2.1 程序的两种发布模式
Linux下用gcc/g++编译的程序有两种发布模式,调试前必须了解两者的区别:
- release模式:默认编译模式,生成的二进制文件体积小、运行效率高,但不包含调试信息(如行号、变量类型等),无法用gdb调试。
- debug模式 :需在编译时添加
-g
选项,会在二进制文件中嵌入调试信息,供gdb识别。只有这种模式的程序才能被gdb调试。
2.2 实战:编译可调试程序
以一个简单的C程序test.c
为例:
c
#include <stdio.h>
int main() {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
}
printf("sum = %d\n", sum);
return 0;
}
编译生成可调试文件的命令:
bash
gcc -g test.c -o test # 关键:-g选项必须添加,否则无法调试
若未加-g
选项,使用gdb调试时会提示无法识别调试信息,导致无法正常调试。
三、gdb基础操作:启动与退出
3.1 启动gdb
gdb只能调试**二进制文件**(编译后的可执行程序),不能直接调试源文件
。 启动命令格式:
bash
gdb 二进制文件名
示例:调试上面生成的test
程序
bash
gdb test
执行后会进入gdb调试界面,显示版本信息及命令提示:
scss
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 Free Software Foundation, Inc.
...
(gdb) # 命令输入提示符
3.2 退出gdb
退出调试界面有两种方式:
- 输入
quit
命令并回车 - 按下
Ctrl + d
组合键
四、查看源代码:list命令详解
在调试过程中,我们需要随时查看源代码以确定调试位置。gdb的list
(缩写l
)命令用于显示源代码,支持多种灵活用法。
4.1 基本用法
list
(或l
):默认从当前位置显示10行源代码,多次执行会继续显示后续内容。list 行号
(如l 5
):显示指定行号附近的代码(以该行号为中心,显示前后内容)。list 函数名
(如l main
):显示指定函数的源代码,方便快速定位函数入口。list 文件名:行号
(如l test.c:8
):当调试多文件项目时,可指定文件名和行号查看代码。
4.2 示例
在test
程序的gdb界面中执行:
bash
(gdb) l main # 查看main函数代码
1 #include <stdio.h>
2 int main() {
3 int sum = 0;
4 for (int i = 1; i <= 5; i++) {
5 sum += i;
6 }
7 printf("sum = %d\n", sum);
8 return 0;
9 }
五、程序运行控制:从启动到单步执行
掌握程序运行的控制命令是调试的核心,gdb提供了丰富的命令用于控制程序执行流程。
5.1 启动程序:run命令
- 命令:
run
(缩写r
) - 功能:启动程序运行,若程序有断点,会在第一个断点处暂停;若无断点,程序会直接运行结束。
- 示例:
bash
(gdb) r # 启动test程序
Starting program: /home/user/test
sum = 15
[Inferior 1 (process 12345) exited normally]
5.2 单步执行:next与step
单步执行用于逐行检查代码执行过程,两者的区别在于是否进入函数内部:
next
(缩写n
):单步执行,不进入函数内部(适合跳过无关函数,关注当前逻辑)。step
(缩写s
):单步执行,进入函数内部(适合调试函数调用时的逻辑)。
5.3 结束当前函数:finish命令
- 命令:
finish
- 功能:当进入函数内部调试时,执行该命令可直接运行到函数返回处并暂停,适合快速跳出无关函数。
- 示例:若在
sum += i
处单步进入了某个函数,执行finish
可直接回到循环逻辑。
5.4 继续运行:continue命令
- 命令:
continue
(缩写c
) - 功能:从当前暂停位置继续运行程序,直到遇到下一个断点或程序结束。
- 场景:常用于多个断点之间的跳转,避免重复单步执行。
六、变量与表达式操作:查看与修改
调试的核心目的之一是观察变量值的变化,gdb提供了多种命令用于操作变量和表达式。
6.1 打印变量/表达式值:print命令
- 命令:
print
(缩写p
) - 功能:打印变量值或表达式计算结果。
- 示例:
bash
(gdb) p sum # 打印变量sum的值
$1 = 0
(gdb) p i # 打印变量i的值
$2 = 1
(gdb) p sum + i # 计算表达式结果
$3 = 1
6.2 修改变量值:set var命令
- 命令:
set var 变量名=值
- 功能:直接修改程序中变量的值,无需重新编译即可测试不同场景(如强制跳过错误分支)。
- 示例:
bash
(gdb) set var i=5 # 将i的值改为5,强制循环提前结束
(gdb) p i
$4 = 5
6.3 跟踪变量:display命令
- 命令:
display 变量名
- 功能:设置变量跟踪,程序每次暂停时会自动打印该变量的值,无需反复输入
print
。 - 示例:
bash
(gdb) display sum # 跟踪sum的值
1: sum = 0
(gdb) n # 单步执行后自动显示sum
4 for (int i = 1; i <= 5; i++) {
1: sum = 0
(gdb) n
5 sum += i;
1: sum = 0
(gdb) n
4 for (int i = 1; i <= 5; i++) {
1: sum = 1
6.4 取消跟踪:undisplay命令
- 命令:
undisplay 编号
(编号可通过display
命令的输出查看,如上述示例中的1
) - 功能:取消对指定变量的跟踪显示。
- 示例:
bash
(gdb) undisplay 1 # 取消对sum的跟踪
七、断点管理:精准控制暂停位置
断点是调试中最常用的功能,用于在指定位置暂停程序,以便观察状态。
7.1 设置断点:break命令
-
命令:
break
(缩写b
) -
功能:在指定行号、函数或文件中设置断点。
-
常用用法:
b 行号
(如b 5
):在当前文件第5行设置断点。b 函数名
(如b main
):在函数入口处设置断点。b 文件名:行号
(如b test.c:5
):在指定文件的指定行设置断点。
-
示例:在
test.c
第5行(sum += i
)设置断点:
bash
(gdb) b test.c:5
Breakpoint 1 at 0x400526: file test.c, line 5.
7.2 查看断点:info break命令
- 命令:
info break
(缩写info b
) - 功能:显示当前所有断点的信息,包括编号、位置、状态等。
- 示例:
bash
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000400526 in main at test.c:5
7.3 删除断点:delete命令
- 命令:
delete
(缩写d
) - 功能:删除断点,支持删除单个或所有断点。
- 用法:
delete 断点编号
(如delete 1
):删除指定编号的断点。delete breakpoints
:删除所有断点。
7.4 禁用/启用断点:disable与enable
disable 断点编号
:临时禁用断点(断点仍存在,不触发暂停)。enable 断点编号
:重新启用被禁用的断点。- 场景:暂时不需要某个断点但不想删除时使用。
八、实战案例:调试一个简单程序
下面通过调试test.c
演示完整流程:
- 编译可调试程序:
bash
gcc -g test.c -o test
- 启动gdb:
bash
gdb test
- 设置断点 :在循环的
sum += i
处(第5行)设置断点:
bash
(gdb) b test.c:5
Breakpoint 1 at 0x400526: file test.c, line 5.
- 启动程序:
bash
(gdb) r
Starting program: /home/user/test
Breakpoint 1, main () at test.c:5
5 sum += i;
- 单步执行并观察变量:
bash
(gdb) n # 执行sum += i
6 }
(gdb) p sum # 此时sum=1
$1 = 1
(gdb) n # 进入下一次循环
5 sum += i;
(gdb) p i # 此时i=2
$2 = 2
(gdb) p sum # 执行sum += i前,sum=1
$3 = 1
(gdb) n # 再次执行sum += i
6 }
(gdb) p sum # 此时sum=3
$4 = 3
- 继续运行到结束:
bash
(gdb) c # 继续执行
Continuing.
sum = 15
[Inferior 1 (process 12345) exited normally]
- 退出gdb:
bash
(gdb) quit
九、总结:必备命令速查表
命令用途 | 命令(全称/缩写) | 示例 |
---|---|---|
启动gdb | gdb 二进制文件 | gdb test |
退出gdb | quit / Ctrl+d | quit |
查看源代码 | list / l 行号/函数名 | l 5、l main |
启动程序 | run / r | r |
单步执行(不进函数) | next / n | n |
单步执行(进函数) | step / s | s |
继续运行 | continue / c | c |
设置断点 | break / b 位置 | b 5、b main |
查看断点 | info break / info b | info b |
删除断点 | delete 编号 / delete breakpoints | delete 1、delete breakpoints |
打印变量 | print / p 变量/表达式 | p sum、p sum+i |
修改变量 | set var 变量=值 | set var i=5 |
通过本文的学习,你已经掌握了gdb的基础用法。下一篇将介绍gdb的进阶技巧,包括条件断点、变量跟踪等高级功能,帮助你更高效地调试复杂程序。