C++代码调试:8种让你省心的技巧

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C++代码调试:如何不再害怕段错误并爱上GDB



您是否曾花费三个小时寻找一个错误,结果发现它只是switch中忘记的break,或是if后面多余的;?如果是——欢迎加入俱乐部。C++调试是一门近乎自虐的艺术。但和任何艺术一样,它是可以掌握的。



在本文中,我们将讨论的不仅仅是“如何运行GDB”,而是真正的技巧,这些技巧将调试从占卜咖啡渣变成福尔摩斯式的侦探工作。我们将讨论现代消毒器、反向调试,甚至如何捕捉那种“只在周五下午出现”的错误。



1. 经典之作:GDB和LLDB



从基础开始。GNU调试器(GDB)及其竞争对手LLDB是调试器中的瑞士军刀。但大多数人只使用了它们10%的功能。让我们改变这一点。



带条件的断点



您在一个有10,000次迭代的循环中设置断点,然后按continue键9,999次?停下来。使用条件断点:



(gdb) break main.cpp:42 if i == 5000


这将节省您数小时的生命。在LLDB中,语法类似:



(lldb) breakpoint set --file main.cpp --line 42 --condition 'i == 5000'


监视点——捕捉变量变化



想象一下:您有一个变量,它在未知的地方被修改。设置一个监视点:



(gdb) watch my_variable(gdb) rwatch my_variable   # 在读取时触发(gdb) awatch my_variable   # 在读取和写入时触发


现在,程序会在有人触碰您的变量的那一刻停止。很神奇,对吧?



2. 反向调试:时间旅行



程序员最可怕的噩梦:程序崩溃了,但您不知道它是如何到达这个状态的。反向调试允许您“回退”执行过程。



在GDB中,这需要record支持:



(gdb) record(gdb) continue... 程序崩溃 ...(gdb) reverse-step   # 向后单步执行(gdb) reverse-continue # 反向继续执行


在商业工具(例如UndoDB或Mozilla的rr)中,这效果更佳。rr(记录和重放)是一个免费工具,它记录程序的执行,然后允许您反复重放:



$ rr record ./my_program$ rr replay(gdb) continue... 找到崩溃位置 ...(gdb) reverse-step


提示:使用rr查找数据竞争——它确定性地重放每次执行。



3. 消毒器:针对未定义行为的疫苗



现代C++(从Clang和GCC 4.8+开始)拥有我们前辈梦寐以求的内置工具。AddressSanitizer (ASan)、UndefinedBehaviorSanitizer (UBSan)和ThreadSanitizer (TSan)是必备工具。



示例:捕捉内存泄漏



// 编译:g++ -fsanitize=address -g main.cpp -o main#include <iostream>

void leak() { int* p = new int(42); // 忘记 delete}

int main() { leak(); return 0;}


运行——然后看到:



==12345==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0x7f... in operator new(unsigned long) #1 0x400... in leak() main.cpp:5 #2 0x400... in main main.cpp:10


ASan将显示精确的调用栈,包括发生分配的行号。再也不会有“某处内存泄漏”的情况。



UBSan对抗未定义行为



// 编译:g++ -fsanitize=undefined -g main.cppint main() {    int x = INT_M                

博客

书籍推荐