3、断点设置艺术:普通断点、条件断点、函数断点、临时断点
调试这事儿,说白了就是跟代码玩捉迷藏。而断点,就是你喊「停」的那只手。
我刚开始用GDB那会儿,只会一种断点——b main,然后一路next下去。后来被一个bug折磨了三天,才发现原来断点还有这么多门道。今天咱们就把这四种断点聊透。
3.1 普通断点:最基础的「暂停键」
普通断点,就是你在某一行代码上打个标记。程序跑到这一行,立刻停住。
用法很简单:
(gdb) break main.cpp:42
(gdb) b 42 # 简写,当前文件第42行
(gdb) b main.cpp:42 # 指定文件
我个人习惯用b这个简写,少敲几个字母,调试效率能高不少。你想想看,调试的时候谁愿意打那么多字?
list命令先看看代码。我一般会先l一下,找到目标行,再b上去。
普通断点有个特点——每次运行到这儿都会停。这既是优点也是缺点。优点是你不会漏掉,缺点是你可能被烦死。比如循环体里打个断点,循环100次你就得按100次c。
3.2 条件断点:只在「对的时候」停下
条件断点,说白了就是给断点加个「门禁」。只有条件满足时,程序才会停。
我在项目中遇到过这样一个场景:一个链表遍历函数,处理了10万个节点,但只有第99999个节点会触发崩溃。用普通断点?手都按酸了。用条件断点?一行命令搞定。
(gdb) break main.cpp:42 if count == 99999
(gdb) b 42 if count > 1000 && flag == true
条件可以是任何C++表达式。变量、函数调用、甚至复杂的逻辑运算都行。
嗯,这里要注意一点:条件表达式里别用有副作用的函数。比如if ++count == 10这种,调试完你都不知道count变成多少了。我曾经犯过这个错,排查了半天才发现是断点条件改了变量值。
3.3 函数断点:不用找行号
函数断点,就是直接在函数入口处停下。你不需要知道函数在第几行,只需要知道函数名。
(gdb) break MyClass::processData
(gdb) b processData # 简写,如果有重载会列出所有
(gdb) b *processData # 在函数第一条指令处停(汇编级别)
函数断点有个特别实用的场景——调试第三方库。你想想看,你调用了Qt的QString::toUpper(),想知道它内部怎么处理的。你不需要去翻Qt源码找行号,直接b QString::toUpper就行。
函数断点也支持条件:
(gdb) b MyClass::processData if this->m_id == 42
这个在调试多线程或者对象池的时候特别好用。我只想停在这个对象的这个函数里,其他对象别来烦我。
b print可能会列出print(int)、print(string)等,你得选一个。
3.4 临时断点:用完即弃
临时断点,就是「一次性断点」。它停一次之后,自动消失。
用法:
(gdb) tbreak main.cpp:42
(gdb) tb 42 # 简写
(gdb) tbreak MyClass::processData
临时断点什么时候用?我举个例子。你正在调试一个递归函数,你想在递归深度为5的时候停下来看看。你可以这样:
(gdb) b factorial if depth == 5
(gdb) run
# 停住了,查看变量
(gdb) tbreak factorial if depth == 6
(gdb) continue
# 下次停就是在depth==6的时候,而且这个断点只用一次
说白了,临时断点就是「用完就扔」的断点。它不会污染你的断点列表。我经常用它来做「下一步」的标记。
3.5 四种断点对比
| 断点类型 | 命令 | 适用场景 | 我的建议 |
|---|---|---|---|
| 普通断点 | b line |
固定位置,每次都要停 | 最常用,但别滥用 |
| 条件断点 | b line if cond |
循环、大量数据、特定状态 | 调试利器,必学 |
| 函数断点 | b func |
不知道行号、第三方库 | 省去找行号的麻烦 |
| 临时断点 | tb line |
一次性检查、递归、跳转 | 用完即弃,干净利落 |
3.6 断点管理:别让断点失控
调试到一半,你可能打了十几个断点。怎么管理?
(gdb) info breakpoints # 列出所有断点
(gdb) disable 2 # 禁用2号断点(不删除)
(gdb) enable 2 # 重新启用
(gdb) delete 2 # 删除2号断点
(gdb) clear main.cpp:42 # 清除该行的所有断点
我个人习惯用disable而不是delete。为什么?因为有时候你只是暂时不想用这个断点,后面可能还要用。删了还得重新打,多麻烦。
3.7 实战:断点组合拳
最后,分享一个我常用的调试套路。假设我在调试一个Qt的信号槽:
// 我想知道当用户点击按钮时,数据是怎么流转的
(gdb) b MainWindow::onButtonClicked # 函数断点,入口
(gdb) tb MainWindow::onButtonClicked if this->m_counter > 10 # 临时条件断点
(gdb) run
# 第一次停在onButtonClicked,查看参数
(gdb) continue
# 第二次停在临时断点,此时m_counter > 10
# 临时断点自动删除,不会影响后续调试
你看,普通断点、函数断点、条件断点、临时断点,四种配合起来用,调试效率直接翻倍。
断点设置这事儿,说白了就是「在哪里停、什么时候停、停几次」的问题。掌握了这四种断点,你就能精准地控制程序的执行节奏。下次遇到bug,别急着加qDebug,先想想——我该用哪种断点?
可可精品资料库