4. 基本断点:break命令、设置行号断点、函数断点、条件断点
调试这活儿,说白了就是让程序在关键时刻停下来,让你看看它到底在搞什么鬼。而断点,就是我们让程序停下来的那个「刹车」。今天咱们就聊聊 GDB 里最基础的四种断点玩法。
4.1 break 命令:一切断点的起点
GDB 里设置断点的核心命令就是 break,简写为 b。我个人习惯用简写,毕竟调试时敲命令越快越好。
基本语法很简单:
(gdb) break 位置
(gdb) b 位置
这个「位置」可以是行号、函数名,也可以是文件名加行号的组合。咱们一个个来看。
4.2 行号断点:最直观的断点
行号断点是最常用的。你想想看,当你知道某一行代码有问题,直接在那行停住,多直接。
// 示例代码:test.c
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int c = a + b; // 我想在这行停住
printf("c = %d\n", c);
return 0;
}
设置行号断点:
(gdb) b 7
Breakpoint 1 at 0x4004f6: file test.c, line 7.
(gdb) r
Starting program: /home/user/test
Breakpoint 1, main () at test.c:7
7 int c = a + b; // 程序停在这里了
嗯,这里要注意:行号是相对于当前源文件的。如果你的项目有多个源文件,可以用 文件名:行号 的格式:
(gdb) b main.c:42
list 命令看看源代码,确认行号没错。我在项目中遇到过好几次因为行号看错,断点打在空行上的尴尬事。
4.3 函数断点:入口处拦截
函数断点更高级一点。你不需要知道函数在哪一行,只要知道函数名就行。程序每次进入这个函数都会停下来。
(gdb) b main
Breakpoint 1 at 0x400526: file test.c, line 4.
(gdb) b printf
Breakpoint 2 at 0x400430
函数断点有个好处:如果函数被重载了(C++ 里常见),GDB 会列出所有匹配的函数让你选:
(gdb) b myFunction
[0] cancel
[1] all
[2] myFunction(int)
[3] myFunction(double)
> 2
Breakpoint 3 at 0x400567: file test.cpp, line 15.
选 2 就是只在这个 myFunction(int) 版本上设断点。这个功能我在调试 C++ 多态代码时帮了大忙。
4.4 条件断点:精准打击
条件断点是我最喜欢的。说白了就是:只有当某个条件满足时,程序才停下来。这在循环里特别有用。
语法:
(gdb) break 位置 if 条件
举个例子,你有一个循环 1000 次的代码,但只在第 500 次时出问题。你总不能手动按 500 次 continue 吧?
for (int i = 0; i < 1000; i++) {
process(i); // 我想在第500次时停住
}
设置条件断点:
(gdb) b 5 if i == 500
Breakpoint 1 at 0x4004f6: file test.c, line 5.
程序运行到第 5 行,且 i == 500 时才会停下。省时省力。
b malloc if size > 1024,一下子就抓住了那个分配大块内存的罪魁祸首。
4.5 断点的管理
设了断点之后,怎么管理它们?GDB 提供了一套完整的断点管理命令:
| 命令 | 作用 |
|---|---|
info breakpoints |
列出所有断点 |
disable 编号 |
禁用某个断点(不删除) |
enable 编号 |
启用某个断点 |
delete 编号 |
删除某个断点 |
clear 行号 |
删除某行的断点 |
我个人习惯用 disable 而不是 delete。因为有时候你只是暂时不想用这个断点,后面可能还会用到。删了还得重新设,麻烦。
b main.c:42 if x > 10 # 检查边界条件。虽然 GDB 不直接支持注释,但你可以用 comment 命令给断点加描述。
4.6 知识体系图
下面这张图帮你理清今天讲的内容:
四种断点各有各的适用场景。行号断点适合精确位置,函数断点适合入口拦截,条件断点适合循环和特定条件。实际调试时,我经常把它们组合起来用,效果出奇的好。
好了,断点的基本用法就这些。记住一点:断点不是越多越好,而是越精准越好。设太多断点反而会让你迷失在调试的海洋里。
可可精品资料库