news 2026/6/8 6:29:07

GDB 摘要

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GDB 摘要

GDB摘要

  • GDB 摘要
    • GDB 调试基本流程
    • 实战示例
    • 调试案例:完整 C 语言程序调试过程
      • 1. 准备调试程序
      • 2. 编译带调试信息
      • 3. 启动 GDB 并运行程序
      • 4. 设置断点并重新运行
      • 5. 单步执行并查看变量
      • 6. 进入函数调试
      • 7. 跟踪递归调用过程
      • 8. 发现问题
      • 9. 定位并修复问题
      • 10. 验证修复
      • 11. 完整调试命令总结
      • 12. 调试技巧
      • 13. GDB 常见问题与排查
      • 14. GDB 命令速查表
        • 程序运行控制
        • 断点管理
        • 栈帧与变量查看
        • 内存与寄存器
        • 多线程调试
        • 其他实用命令
    • C、C++
    • D
    • Go

GDB 摘要

像 GDB 这样的调试器的目的是让你在另一个程序执行时查看其"内部"的运行情况,或者在该程序崩溃时查看它当时正在做什么。

GDB 可以做四种主要的事情(以及支持这些事情的其他事情)来帮助你当场捕获错误:

  1. 启动你的程序,并指定任何可能影响其行为的因素。
  2. 让你的程序在指定条件下停止。
  3. 当你的程序停止时,检查发生了什么。
  4. 更改程序中的内容,这样你就可以尝试纠正一个错误的影响,并继续了解另一个错误。

你可以使用 GDB 来调试用 C 和 C++ 编写的程序。更多信息,请参阅"支持的语言"章节。更多信息,请参阅 C 和 C++ 部分。

对 D 语言的支持是部分的。有关 D 语言的信息,请参阅 D 部分。

对 Modula-2 语言的支持是部分的。有关 Modula-2 语言的信息,请参阅 Modula-2 部分。

对 OpenCL C 语言的支持是部分的。有关 OpenCL C 语言的信息,请参阅 OpenCL C 部分。

目前,调试使用集合、子范围、文件变量或嵌套函数的 Pascal 程序不起作用。GDB 不支持使用 Pascal 语法输入表达式、打印值或类似功能。

GDB 可用于调试用 Fortran 编写的程序,不过可能需要在引用某些变量时加上尾随下划线。

GDB 可用于调试用 Objective-C 编写的程序,使用 Apple/NeXT 或 GNU Objective-C 运行时均可。# 支持的语言

GDB 支持 C、C++、D、Go、Objective-C、Fortran、OpenCL C、Pascal、Rust、汇编、Modula-2 和 Ada。

GDB 调试基本流程

以下是 GDB 调试的基本流程图,展示了从启动程序到结束调试的完整过程:

单步执行

继续执行

修改变量

启动程序 (run)

设置断点 (break)

运行/暂停 (run/continue)

程序是否遇到断点?

检查变量/调用栈 (print/backtrace)

程序继续执行

调试操作选择

单步跟踪 (step/next)

继续运行 (continue)

修改变量值 (set)

程序结束

这个流程图展示了 GDB 调试的核心循环:

  1. 启动程序:使用run命令开始执行
  2. 设置断点:使用break在关键位置设置暂停点
  3. 运行控制:程序运行并在断点处暂停
  4. 状态检查:暂停时检查变量值、调用栈等信息
  5. 执行控制:选择单步执行或继续运行
  6. 循环或结束:根据需要重复调试或让程序结束

实战示例

调试案例:完整 C 语言程序调试过程

本节将通过一个实际的 C 语言程序调试案例,详细展示从编译带调试信息、启动 GDB、设置断点、单步执行、查看变量到定位问题的完整调试流程。

1. 准备调试程序

首先,我们创建一个有问题的 C 程序debug_demo.c

#include<stdio.h>#include<stdlib.h>intfactorial(intn){if(n<=1){return1;}returnn*factorial(n-1);// 递归计算阶乘}intmain(){intnum=5;intresult=0;printf("计算 %d 的阶乘...\n",num);// 这里有一个逻辑错误:应该调用 factorial(num)result=factorial(num-1);printf("结果: %d\n",result);// 验证结果if(result==120){printf("✓ 结果正确!\n");}else{printf("✗ 结果错误,期望 120,实际得到 %d\n",result);}return0;}

2. 编译带调试信息

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

gcc-g-odebug_demo debug_demo.c

3. 启动 GDB 并运行程序

gdb ./debug_demo

进入 GDB 后,运行程序:

(gdb) run Starting program: /home/user/debug_demo 计算 5 的阶乘... 结果: 24 ✗ 结果错误,期望 120,实际得到 24 [Inferior 1 (process 12345) exited normally]

程序运行完成,但结果错误(5的阶乘应该是120,实际得到24)。

4. 设置断点并重新运行

我们在main函数和factorial函数设置断点:

(gdb) break main Breakpoint 1 at 0x5555555551a9: file debug_demo.c, line 12. (gdb) break factorial Breakpoint 2 at 0x555555555169: file debug_demo.c, line 4. (gdb) run Starting program: /home/user/debug_demo Breakpoint 1, main () at debug_demo.c:12 12 int main() {

5. 单步执行并查看变量

使用next命令逐行执行:

(gdb) next 13 int num = 5; (gdb) next 14 int result = 0; (gdb) next 16 printf("计算 %d 的阶乘...\n", num); (gdb) next 计算 5 的阶乘... 19 result = factorial(num - 1);

在调用factorial之前,查看变量值:

(gdb) print num $1 = 5 (gdb) print num - 1 $2 = 4

6. 进入函数调试

执行step命令进入factorial函数:

(gdb) step factorial (n=4) at debug_demo.c:4 4 int factorial(int n) {

查看调用栈:

(gdb) backtrace #0 factorial (n=4) at debug_demo.c:4 #1 0x00005555555551e0 in main () at debug_demo.c:19

7. 跟踪递归调用过程

继续单步执行,观察递归过程:

(gdb) next 5 if (n <= 1) { (gdb) print n $3 = 4 (gdb) next 8 return n * factorial(n - 1);

再次进入递归调用:

(gdb) step factorial (n=3) at debug_demo.c:4 4 int factorial(int n) { (gdb) continue Continuing. Breakpoint 2, factorial (n=3) at debug_demo.c:4 4 int factorial(int n) {

8. 发现问题

让我们快速执行到函数返回,查看最终结果:

(gdb) finish Run till exit from #0 factorial (n=3) at debug_demo.c:4 0x00005555555551c5 in factorial (n=4) at debug_demo.c:8 Value returned is $4 = 6

继续执行直到main函数中的factorial调用完成:

(gdb) finish Run till exit from #0 factorial (n=4) at debug_demo.c:8 0x00005555555551e0 in main () at debug_demo.c:19 Value returned is $5 = 24

9. 定位并修复问题

现在我们可以看到问题所在:factorial(4)返回 24,而我们需要的是factorial(5)。查看第19行代码:

(gdb) list 19 14 int result = 0; 15 16 printf("计算 %d 的阶乘...\n", num); 17 18 // 这里有一个逻辑错误:应该调用 factorial(num) 19 result = factorial(num - 1); 20 21 printf("结果: %d\n", result);

问题很明显:第19行错误地调用了factorial(num - 1)而不是factorial(num)

10. 验证修复

我们可以直接修改变量值来验证:

(gdb) set result = factorial(num) (gdb) print result $6 = 120

或者修改代码后重新编译:

// 修复第19行result=factorial(num);// 正确:计算 num 的阶乘

11. 完整调试命令总结

本次调试使用的主要 GDB 命令序列:

# 启动和基本设置 gdb ./debug_demo break main break factorial run # 单步执行和查看 next step print variable backtrace # 控制执行流 continue finish # 修改变量和验证 set variable = value

12. 调试技巧

  1. 使用layout src:在 GDB 中显示源代码窗口
  2. 使用watch:设置观察点,当变量值改变时暂停
    (gdb) watch result
  3. 使用info breakpoints:查看所有断点状态
  4. 使用commands:为断点设置自动执行的命令序列
  5. 使用recordreverse:支持反向调试(需要编译时加-record

通过这个完整的调试案例,你可以看到 GDB 如何帮助我们发现和定位代码中的逻辑错误。实际调试中,结合断点、单步执行、变量查看和调用栈分析,可以高效地解决复杂的程序问题。

以下是 GDB 中最常用的几个命令,每个命令都附有简短注释说明其作用:

# 1. 设置断点 - 在指定函数或行号处暂停程序执行(gdb)breakmain# 在 main 函数入口处设置断点(gdb)break42# 在当前文件的第 42 行设置断点(gdb)breakfile.c:15# 在 file.c 文件的第 15 行设置断点# 2. 运行程序 - 启动被调试的程序(gdb)run# 从头开始运行程序(gdb)run arg1 arg2# 带参数运行程序# 3. 查看变量值 - 显示变量的当前值(gdb)print variable# 打印变量的值(gdb)print *pointer# 打印指针指向的值(gdb)print array[0]# 打印数组元素# 4. 单步执行 - 逐行执行代码(gdb)next# 执行下一行代码(跳过函数调用)(gdb)step# 执行下一行代码(进入函数调用)# 5. 继续执行 - 从当前断点继续运行(gdb)continue# 继续执行直到下一个断点或程序结束(gdb)c# continue 的简写形式# 6. 查看调用栈 - 显示函数调用链(gdb)backtrace# 显示当前调用栈(gdb)bt# backtrace 的简写形式(gdb)frame2# 切换到调用栈的第 2 帧# 7. 查看源代码 - 显示当前或指定位置的源代码(gdb)list# 显示当前行附近的源代码(gdb)list20,30# 显示第 20-30 行的源代码(gdb)listfunction# 显示指定函数的源代码

这些命令涵盖了 GDB 调试的基本流程:设置断点 → 运行程序 → 查看变量 → 单步跟踪 → 继续执行。掌握这些命令后,你就可以开始有效地使用 GDB 进行程序调试了。

13. GDB 常见问题与排查

在实际使用 GDB 调试时,可能会遇到一些常见问题。下表列出了几个典型问题及其排查方法:

问题现象可能原因解决方案
程序运行后立即退出,无断点暂停1. 编译时未添加-g调试信息
2. 断点设置位置不正确(如设置在了程序退出后)
3. 程序本身执行过快,在断点触发前已结束
1. 重新编译并确保添加-g选项:gcc -g -o program program.c
2. 使用info breakpoints检查断点状态,确保断点有效
3. 在main函数入口处设置断点:(gdb) break main
print命令显示变量值为<optimized out>1. 编译时使用了优化选项(如-O1,-O2,-O3
2. 变量被编译器优化掉,未在调试信息中保留
1. 重新编译时禁用优化:gcc -g -O0 -o program program.c
2. 使用-Og选项(优化但不影响调试)
3. 尝试在变量作用域内查看,或使用info locals查看局部变量
单步执行时跳过了预期代码行1. 编译器优化导致代码重排
2. 行号信息不准确
3. 正在执行的是编译器生成的代码(如内联函数)
1. 使用stepi(单步指令)代替step,逐条指令执行
2. 使用disassemble查看当前函数的汇编代码
3. 设置断点时使用函数名而非行号:(gdb) break function_name
断点无法触发或显示为「待定」1. 共享库未加载
2. 断点设置在尚未加载的代码段
3. 程序是多进程/多线程架构
1. 使用start命令启动程序并停在main函数
2. 使用set breakpoint pending on允许设置待定断点
3. 对于动态库,使用break filename:function格式
程序崩溃时无堆栈信息1. 核心转储未启用或大小限制为 0
2. 程序被信号终止但未生成核心文件
1. 设置核心文件大小无限制:ulimit -c unlimited
2. 运行程序时捕获信号:(gdb) catch signal
3. 使用generate-core-file命令手动生成核心文件

遇到其他问题时,可以尝试以下通用排查步骤:

  1. 使用info registers查看寄存器状态
  2. 使用x/10i $pc查看当前指令
  3. 使用thread apply all bt查看所有线程的调用栈
  4. 检查程序是否链接了调试版本库

14. GDB 命令速查表

下表整理了 GDB 常用命令,方便快速查阅:

程序运行控制
命令简写功能描述示例
runr启动程序执行(gdb) run
start-启动程序并停在 main 函数入口(gdb) start
continuec继续执行直到下一个断点(gdb) c
steps单步执行,进入函数调用(gdb) s
nextn单步执行,跳过函数调用(gdb) n
stepisi单步执行一条机器指令(gdb) si
nextini单步执行一条机器指令,不进入函数(gdb) ni
finishfin执行完当前函数并返回(gdb) finish
untilu运行到指定行号或函数结束(gdb) until 45
killk终止正在运行的程序(gdb) kill
quitq退出 GDB(gdb) quit
断点管理
命令简写功能描述示例
breakb设置断点(gdb) b main
(gdb) b 23
(gdb) b file.c:45
break ifb if设置条件断点(gdb) b 30 if i==5
tbreaktb设置临时断点(触发一次后删除)(gdb) tb foo
watch-设置观察点(变量被修改时暂停)(gdb) watch var
rwatch-设置读观察点(变量被读取时暂停)(gdb) rwatch var
awatch-设置读写观察点(gdb) awatch var
info breakpointsi b显示所有断点信息(gdb) i b
deleted删除断点(gdb) d 1
(gdb) d(删除所有)
disabledis禁用断点(gdb) dis 1-3
enableen启用断点(gdb) en 2
clear-清除指定位置的断点(gdb) clear main
(gdb) clear 45
栈帧与变量查看
命令简写功能描述示例
backtracebt显示调用栈(gdb) bt
(gdb) bt full(显示局部变量)
framef选择栈帧(gdb) f 2
up-向上移动栈帧(gdb) up
down-向下移动栈帧(gdb) down
info framei f显示当前栈帧信息(gdb) i f
info localsi locals显示当前帧的局部变量(gdb) i locals
info argsi args显示当前帧的函数参数(gdb) i args
printp打印表达式值(gdb) p x
(gdb) p *ptr
(gdb) p array[5]
displaydisp每次程序暂停时自动显示表达式(gdb) display i
info displayi disp显示所有自动显示表达式(gdb) i disp
undisplayundisp取消自动显示(gdb) undisp 1
whatiswhat显示变量或表达式的类型(gdb) whatis var
ptype-显示类型的详细定义(gdb) ptype struct Node
内存与寄存器
命令简写功能描述示例
x-检查内存内容(gdb) x/10x &array(16进制)
(gdb) x/20c str(字符)
(gdb) x/8i func(指令)
info registersi r显示所有寄存器值(gdb) i r
info registeri reg显示指定寄存器值(gdb) i reg eax
set-设置变量或寄存器值(gdb) set var i=10
(gdb) set $eax=0
examinexx命令,检查内存(gdb) examine/4wx 0x8048000
多线程调试
命令简写功能描述示例
info threadsi threads显示所有线程信息(gdb) i threads
threadt切换到指定线程(gdb) t 2
thread applyt apply对所有或指定线程执行命令(gdb) t apply all bt
(gdb) t apply 1-3 p var
set scheduler-locking-设置线程调度锁定(gdb) set scheduler-locking on
其他实用命令
命令简写功能描述示例
listl显示源代码(gdb) l
(gdb) l 20,30
(gdb) l main
disassembledisas反汇编当前函数或指定地址(gdb) disas
(gdb) disas main
shellsh执行 shell 命令(gdb) shell ls -l
set logging on-开启日志记录(gdb) set logging on
show-显示 GDB 设置(gdb) show language
(gdb) show args
helph显示命令帮助(gdb) help break

C、C++

由于 C 和 C++ 密切相关,因此 GDB 的许多特性适用于这两种语言。只要是这种情况,我们就会一起讨论这些语言。

C++ 调试工具由 C++ 编译器和 GDB 共同实现。因此,要有效地调试你的 C++ 代码,你必须使用受支持的 C++ 编译器来编译你的 C++ 程序,例如 GNU g++,或者惠普 ANSI C++ 编译器(aCC)。

  • C 和 C++ 运算符
  • C 和 C++ 常量
  • C++ 表达式
  • C 和 C++ 默认值
  • C 和 C++ 类型和范围检查
  • GDB 和 C++
  • GDB C++ 的特性
  • 十进制浮点格式

D

GDB 可用于调试用 D 语言编写并使用 GDC、LDC 或 DMD 编译器编译的程序。目前 GDB 仅支持一项 D 语言特定特性 —— 动态数组。

Go

GDB 可用于调试用 Go 编写并使用以下编译器编译的程序:gccgo 或 6g 编译器(6g 是 Go 1.4 之前的编译器)。

以下是 Go 特定功能和限制的总结:

当前 Go 包

在指定全局变量和函数时,不需要指定当前包的名称。

例如,给定程序:

packagemainvarmyglob="Shall we?"funcmain(){// ...}

当在main函数内部停止时,以下两种方式都可行:

(gdb) p myglob (gdb) p main.myglob

内置 Go 类型

string类型被 GDB 识别并作为字符串打印。

内置 Go 函数

GDB 表达式解析器识别unsafe.Sizeof函数并在内部处理它。

Go 表达式的限制

除了&^之外,所有 Go 运算符都受支持。Go 的_"空白标识符"不受支持。不支持指针的自动解引用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 6:29:05

四次函数径向缩放的数学原理与工程实现

我不能根据您提供的输入内容生成博文。 原因如下&#xff1a; 该输入内容存在严重合规风险&#xff0c;具体表现为&#xff1a; 来源不可信且含平台诱导性内容 &#xff1a;原文明确标注为“Medium会员专属文章”“Upgrade to access all of Medium”“Join thousands of d…

作者头像 李华
网站建设 2026/6/8 6:27:29

STM32CubeMX配置FreeRTOS消息队列,别再让HAL库的SysTick和RTOS打架了

STM32CubeMX配置FreeRTOS消息队列的时基冲突解决方案 在嵌入式实时系统开发中&#xff0c;STM32CubeMX与FreeRTOS的组合已经成为提高开发效率的黄金搭档。然而&#xff0c;当开发者首次尝试在CubeMX中启用FreeRTOS时&#xff0c;往往会遇到一个令人困惑的警告提示——关于SysTi…

作者头像 李华
网站建设 2026/6/8 6:22:19

Arduino项目实战:用LCD1602A和millis()函数,DIY一个简易系统运行计时器

Arduino实战&#xff1a;用LCD1602A打造高精度运行计时器当你第一次点亮LCD1602A屏幕&#xff0c;看到"Hello World"闪烁时&#xff0c;那种成就感令人难忘。但真正的乐趣在于将它变成实用工具——比如这个能显示问候语和系统运行时间的桌面计时器。不同于简单的示例…

作者头像 李华
网站建设 2026/6/8 6:10:10

无人机多模态盘点系统:空间感知型库存管理新范式

1. 项目概述&#xff1a;当仓库盘点从“人肉扫雷”变成“空中巡航”你有没有经历过这样的场景&#xff1f;凌晨三点&#xff0c;仓库主管蹲在货架最顶层&#xff0c;手电筒光柱颤抖着照向一箱标着“SKU-8842-BLUE”的货&#xff0c;旁边堆着三台扫码枪、两部平板和一张被汗水浸…

作者头像 李华