使用 gdb(GNU Debugger)对文件进行调试是 C/C++ 开发过程中非常重要的一项技能。gdb 可以帮助你设置断点、跟踪代码执行、检查变量状态和调用堆栈等。下面是使用 gdb 进行调试的基本步骤和一些常用命令。

1. 编译代码时生成调试信息

在使用 gdb 之前,你需要确保编译时生成了调试信息。通常在编译时添加 -g 选项即可生成调试信息。

编译示例:

1
g++ -g -o my_program my_program.cpp
  • -g 选项告诉编译器生成调试信息。
  • -o my_program 指定输出文件名为 my_program

编译后会生成带有调试信息的可执行文件 my_program

2. 启动 gdb 调试程序

你可以通过以下命令启动 gdb 并加载可执行文件:

1
gdb my_program

启动 gdb 后,你会进入 gdb 的交互式调试界面,看到类似这样的提示:

1
2
3
GNU gdb (GDB) 10.1
...
(gdb)

3. 运行程序

gdb 中,你可以通过以下命令运行程序:

1
(gdb) run

这会启动 my_program,并按照通常的方式运行程序。如果程序有命令行参数,可以这样传递:

1
(gdb) run arg1 arg2

例如:

1
(gdb) run input.txt output.txt

4. 设置断点

断点告诉调试器在代码的某一行暂停执行,从而让你检查程序的状态。

在函数上设置断点:

1
(gdb) break main

这将在 main 函数的入口处设置断点。你可以在任何函数上设置断点:

1
(gdb) break my_function

在文件的某一行设置断点:

1
(gdb) break my_program.cpp:10

这将在 my_program.cpp 的第 10 行设置断点。

列出所有断点:

1
(gdb) info breakpoints

删除某个断点:

1
(gdb) delete 1

1 是断点的编号,你可以通过 info breakpoints 查找到。

启动程序并在断点处暂停:

1
(gdb) run

程序会运行到断点处并暂停,这时你就可以检查变量的值和程序状态。

5. 单步执行

gdb 提供了多种命令来单步执行程序,帮助你逐行分析代码的执行。

单步执行一行代码:

1
(gdb) step

step 会进入函数内部执行。如果当前行调用了一个函数,step 会进入该函数。

单步执行下一行代码:

1
(gdb) next

next 会执行当前行代码,但不会进入函数内部。

继续执行程序直到下一个断点:

1
(gdb) continue

这会让程序继续执行,直到遇到下一个断点或程序结束。

退出当前函数:

1
(gdb) finish

这会继续执行程序,直到当前函数返回。

6. 查看变量和表达式

在调试过程中,你可以在程序暂停时查看变量的值。

查看变量的值:

1
(gdb) print variable_name

例如:

1
(gdb) print x

查看复杂表达式的值:

你可以使用 print 命令查看任意复杂表达式的值:

1
(gdb) print x + y

查看指针指向的值:

1
(gdb) print *pointer

gdb 还提供了一些快捷方式,例如 pprint 命令的简写:

1
(gdb) p x

查看局部变量:

1
(gdb) info locals

这会列出当前函数中的所有局部变量及其值。

7. 查看内存

如果你想直接查看内存,可以使用 x 命令。

查看某个地址处的内存:

1
(gdb) x addr

例如:

1
(gdb) x 0x7fffffffe4c0

以十六进制格式查看某个变量的内存地址:

1
(gdb) p &variable_name

查看某个地址处的内存值(按字节):

1
(gdb) x /b addr

/b 表示按字节查看。

8. 查看调用堆栈

当程序崩溃或遇到异常时,查看调用堆栈可以帮助你了解程序是如何到达当前状态的。

查看调用堆栈:

1
(gdb) backtrace

backtrace 会显示当前线程的调用堆栈,包括每一层的函数名、参数及位置。

查看特定栈帧:

1
(gdb) frame n

n 是栈帧的编号,frame 命令会切换到指定的栈帧,允许你检查该帧中的变量。

例如,查看第 2 个栈帧:

1
(gdb) frame 2

9. 调试时的程序控制

gdb 提供了一些命令来控制程序的执行状态。

重新启动程序:

1
(gdb) run

重复运行程序从头开始执行。

杀死当前正在调试的程序:

1
(gdb) kill

这会立即停止程序的执行。

退出 gdb:

1
(gdb) quit

10. 调试多线程程序

如果你在调试多线程程序,gdb 提供了查看线程信息的命令。

显示当前所有线程:

1
(gdb) info threads

切换到某个线程:

1
(gdb) thread n

n 是线程编号。你可以通过 info threads 命令查看所有线程的编号。

11. 调试崩溃的程序(core dump)

如果你的程序崩溃了,并生成了 core dump 文件,你可以使用 gdb 来分析该文件。

生成 core dump 文件:

在 Linux 中,你可以通过以下命令启用 core dump 文件:

1
ulimit -c unlimited

当程序崩溃时,会生成一个 core 文件。

使用 gdb 分析 core dump 文件:

1
gdb my_program core

这会加载程序和 core dump 文件,你可以使用 backtrace 或其他命令查看崩溃时的状态。

12. 调试共享库

如果程序依赖共享库,确保在编译时为库生成调试信息,并在 gdb 中加载符号。

1
(gdb) sharedlibrary

这会确保 gdb 正确加载共享库的调试符号。

总结

gdb 是一个功能强大的调试工具,以下是调试的基本流程:

  1. 编译代码时启用调试信息-g)。
  2. 启动 gdb 并加载程序。
  3. 设置断点,开始调试。
  4. 单步执行代码,查看变量和表达式的值。
  5. 查看调用堆栈,找出问题所在。
  6. 使用 日志、内存查看 等工具帮助分析问题。

通过这些步骤,你可以高效地使用 gdb 来调试 C/C++ 程序,查找并修复问题。