在Linux环境下调试C++代码有多种方法,以下是常用的调试技术:
1. GDB (GNU Debugger) - 最常用的调试器
编译准备
# 编译时加上调试信息
g++ -g -o program program.cpp
基本GDB使用
# 启动调试
gdb ./program
# 常用GDB命令
(gdb) run # 运行程序
(gdb) break main # 在main函数设置断点
(gdb) break 15 # 在第15行设置断点
(gdb) continue # 继续执行
(gdb) next # 单步执行(不进入函数)
(gdb) step # 单步执行(进入函数)
(gdb) print variable # 打印变量值
(gdb) info locals # 查看局部变量
(gdb) backtrace # 查看调用栈
(gdb) quit # 退出调试器
实用GDB技巧
# 监视变量变化
(gdb) watch variable
# 条件断点
(gdb) break 20 if x > 10
# 查看内存
(gdb) x/10i $pc # 查看当前指令
(gdb) x/10x ptr # 查看指针指向的内存
# 调试core dump
gdb program core
2. 使用打印调试(Print Debugging)
基本打印调试
#include <iostream>
#include <iomanip>
// 调试宏
#define DEBUG_PRINT(x) std::cout << "[DEBUG] " << #x << " = " << x << std::endl
int main() {
int value = 42;
DEBUG_PRINT(value);
// 打印函数进入/退出
std::cout << "[TRACE] Entering function" << std::endl;
// ... 函数逻辑
std::cout << "[TRACE] Exiting function" << std::endl;
return 0;
}
高级打印调试
#include <iostream>
#include <chrono>
#include <iomanip>
// 带时间戳的调试输出
#define LOG(msg) do { \
auto now = std::chrono::system_clock::now(); \
auto time_t = std::chrono::system_clock::to_time_t(now); \
std::cout << "[" << std::put_time(std::localtime(&time_t), "%H:%M:%S") << "] " << msg << std::endl; \
} while(0)
// 条件调试
#ifdef DEBUG
#define DBG(x) std::cout << "[DEBUG] " << x << std::endl
#else
#define DBG(x)
#endif
3. 使用断言 (Assertions)
#include <cassert>
#include <iostream>
int divide(int a, int b) {
assert(b != 0); // 运行时检查
return a / b;
}
// 自定义断言
#define ASSERT(condition, message) \
if (!(condition)) { \
std::cerr << "Assertion failed: " << message << std::endl; \
std::cerr << "File: " << __FILE__ << ", Line: " << __LINE__ << std::endl; \
std::abort(); \
}
int main() {
int result = divide(10, 2);
ASSERT(result == 5, "Division result incorrect");
return 0;
}
4. Valgrind - 内存错误检测
# 安装 valgrind
sudo apt-get install valgrind
# 检查内存泄漏
valgrind --leak-check=full ./program
# 检查内存错误
valgrind --tool=memcheck ./program
# 性能分析
valgrind --tool=callgrind ./program
5. AddressSanitizer (ASan) - 编译时内存检查
# 编译时启用AddressSanitizer
g++ -g -fsanitize=address -o program program.cpp
# 运行程序,自动检测内存错误
./program
6. 使用IDE调试
VS Code 配置
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug C++",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/program",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"preLaunchTask": "build"
}
]
}
7. 日志系统
#include <iostream>
#include <fstream>
#include <string>
#include <chrono>
class Logger {
private:
std::ofstream logFile;
public:
Logger(const std::string& filename) : logFile(filename) {}
template<typename T>
void log(const T& message) {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
logFile << "[" << std::ctime(&time_t) << "] " << message << std::endl;
std::cout << "[LOG] " << message << std::endl;
}
};
// 使用示例
int main() {
Logger logger("debug.log");
logger.log("Program started");
logger.log("Processing data...");
return 0;
}
8. 核心转储文件调试
# 启用core dump
ulimit -c unlimited
# 程序崩溃后生成core文件
# 使用gdb分析
gdb ./program core
# 查看崩溃时的状态
(gdb) backtrace
(gdb) info registers
(gdb) print variable
9. 静态分析工具
# 使用cppcheck
sudo apt-get install cppcheck
cppcheck --enable=all program.cpp
# 使用clang-tidy
clang-tidy program.cpp -- -std=c++17
# 使用编译器警告
g++ -Wall -Wextra -Wpedantic -o program program.cpp
10. 性能调试
# 使用gprof
g++ -pg -o program program.cpp
./program
gprof program gmon.out > analysis.txt
# 使用perf
sudo apt-get install linux-tools-common
perf record ./program
perf report
调试最佳实践
- 编译时启用调试信息: 使用
-g
标志 - 使用编译器警告:
-Wall -Wextra
- 分层调试: 从简单的打印开始,逐步使用高级工具
- 保持代码简洁: 便于调试和理解
- 写测试用例: 帮助隔离问题
- 使用版本控制: 便于回滚和比较
选择合适的调试方法取决于具体的问题类型和复杂程度。对于简单问题,打印调试可能就足够了;对于复杂的内存问题,可能需要使用Valgrind或AddressSanitizer。