一、 调试流程概览#

步骤 命令 说明
1. 编译带调试信息 g++ -g program.cpp -o program 生成包含调试信息的可执行文件
2. 启动调试器 gdb program.exe 启动 GDB 并加载程序
3. 设置断点 break mainb 15 在函数或行号设置断点
4. 运行程序 run 开始执行程序
5. 控制执行 next, step, continue 单步执行或继续
6. 检查变量 print variable 查看变量值
7. 退出调试 quit 退出 GDB

详细调试步骤#

1. 编译带调试信息的程序#

首先使用 -g 选项编译,生成包含调试信息的可执行文件:

1
2
3
4
5
6
7
8
9
# 基本编译(包含调试信息)
g++ -g main.cpp -o main.exe

# 推荐编译选项(调试版本)
g++ -g -O0 -Wall -Wextra -std=c++17 main.cpp -o main.exe

# 如果有多个源文件
g++ -g -O0 *.cpp -o program.exe

编译选项说明:

  • -g:生成调试信息
  • -O0:关闭优化(调试时必需,防止代码乱跳)
  • -Wall -Wextra:显示所有警告
  • -std=c++17:使用 C++17 标准

2. 启动 GDB 调试器#

1
2
3
4
5
6
7
# 方式 1:启动 GDB 并直接加载程序
gdb main.exe

# 方式 2:先启动 GDB,再手动加载程序
gdb
(gdb) file main.exe

3. 基本调试命令#

设置断点 (Breakpoints)#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 在函数入口设置断点
(gdb) break main
(gdb) b main

# 在特定行号设置断点
(gdb) break 15
(gdb) b 15

# 在特定文件的特定行设置断点
(gdb) break main.cpp:20

# 设置条件断点
(gdb) break 25 if i == 5

# 查看所有断点
(gdb) info breakpoints

# 删除断点
(gdb) delete 1 # 删除1号断点
(gdb) d # 删除所有断点

运行和控制程序执行#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 运行程序
(gdb) run
(gdb) r

# 传入命令行参数运行
(gdb) run arg1 arg2

# 单步执行(Next,不进入函数内部)
(gdb) next
(gdb) n

# 单步执行(Step,进入函数内部)
(gdb) step
(gdb) s

# 继续执行到下一个断点
(gdb) continue
(gdb) c

# 执行完当前函数并返回到调用处
(gdb) finish

# 直到退出当前循环或到达当前行之后
(gdb) until

查看和修改变量#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 打印变量值
(gdb) print variable
(gdb) p variable

# 打印表达式或数组成员
(gdb) p i + 5
(gdb) p array[3]
(gdb) p object.member

# 手动修改变量值(调试逻辑时很有用)
(gdb) set variable i = 10

# 自动显示变量(每次停顿时都会显示)
(gdb) display variable
(gdb) undisplay 1 # 取消编号为1的自动显示

# 查看变量类型
(gdb) whatis variable

# 查看当前作用域的局部变量
(gdb) info locals

# 查看函数传入参数
(gdb) info args

查看代码和调用栈#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查看当前行附近的源代码
(gdb) list
(gdb) l

# 查看特定位置代码
(gdb) list 15
(gdb) list main

# 查看调用栈(Backtrace)
(gdb) backtrace
(gdb) bt

# 查看当前所在的栈帧信息
(gdb) frame
(gdb) info frame

# 切换到指定的栈帧(用于查看父函数的变量)
(gdb) frame 2


完整调试示例#

示例程序:debug_demo.cpp#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>

int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
int sum = 0;

std::cout << "Calculating factorial sum..." << std::endl;

for (int i = 0; i < numbers.size(); i++) {
int fact = factorial(numbers[i]);
sum += fact;
std::cout << numbers[i] << "! = " << fact << std::endl;
}

std::cout << "Total sum: " << sum << std::endl;
return 0;
}

调试会话#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 编译
g++ -g -O0 -Wall debug_demo.cpp -o debug_demo.exe

# 2. 启动
gdb debug_demo.exe

# 3. 常用操作序列
(gdb) b main # 断在入口
(gdb) b factorial # 断在递归函数
(gdb) r # 开始跑

(gdb) n # 走一步
(gdb) p numbers # 看看 vector 里的东西
(gdb) c # 跳到 factorial 断点
(gdb) p n # 看看递归参数
(gdb) watch sum # 监视 sum 变量,变了就停
(gdb) c # 继续
(gdb) q # 搞定退出


高级调试#

条件断点和观察点#

  • 条件断点(gdb) break 15 if i == 3
  • **观察点 (Watchpoints)**:
  • watch var:当变量被写入时暂停。
  • rwatch var:当变量被读取时暂停。
  • awatch var:当变量被读或写时暂停。

多线程调试#

  • info threads:查看所有线程。
  • thread <ID>:切换到特定线程。
  • break line thread <ID>:只在特定线程中设置断点。

信号处理#

  • info signals:查看 GDB 如何处理各种系统信号。
  • handle SIGINT stop:收到中断信号时停止程序。

实用命令速查#

命令 简写 功能
run r 运行程序
break b 设置断点
next n 单步执行(跳过函数)
step s 单步执行(进入函数)
continue c 继续执行到下个断点
print p 打印变量值
backtrace bt 查看调用栈
list l 查看源代码
info b i b 查看断点列表
delete d 删除断点
quit q 退出 GDB

调试场景#

1. 调试已崩溃 (Segment Fault) 的程序#

利用 Core Dump 文件:

1
2
3
4
gdb program.exe core.dump
(gdb) bt # 直接定位崩溃瞬间的函数调用链
(gdb) p *this # 查看当时对象的状态

2. 调试正在运行的程序#

1
2
3
4
5
6
# 找到进程 PID 后直接附加
gdb -p <PID>

# 或者在 GDB 内部附加
(gdb) attach <PID>


常见问题解决#

  • 找不到调试信息:检查 file program.exe 的输出。如果没有 with debug_info,请确认 g++ 编译时加了 -g
  • **变量被优化掉 (value optimized out)**:确保编译参数是 -O0
  • No symbol table:确认没有执行过 strip 命令,该命令会移除所有调试符号。