GDB 常用命令速查卡学习资料
(调试器入门 + 六大核心命令)
一、GDB 是什么?为什么要学?
GDB是GNU Debugger的缩写,它是一个让你可以逐行运行程序、随时停下来检查变量值、查看函数调用栈的工具。
如果不使用调试器,程序出错时你只能看着代码干瞪眼,或者在代码里插入无数printf。有了 GDB,你就可以:
- 让程序在指定的地方暂停(设置断点)
- 一条一条语句地执行,观察变量的变化
- 在程序崩溃时,查看崩溃时的现场(调用栈、变量值)
GDB 是命令行工具,功能极其强大,我们今天先掌握最常用的六个命令。
二、调试前的准备:用-g编译
要让 GDB 能读懂你的程序,编译时必须加上-g选项,它会把调试信息(比如源代码行号、变量名等)嵌入到可执行文件中。
编译命令:
gcc-ghello.c-ohello如果不加-g,GDB 里看不到源代码,只能看到汇编指令,调试起来很痛苦。
启动 GDB:
gdb ./hello之后就会进入 GDB 的交互界面,提示符是(gdb),你在这里输入命令。
三、六大核心命令逐一精讲
1.break(简写b)—— 设下断点,让程序停住
作用
在程序执行到某一行代码或某个函数时暂停,方便我们检查当时的程序状态。
常用形式
| 写法 | 含义 |
|---|---|
break 行号 | 在当前文件的指定行暂停 |
break 函数名 | 在进入指定函数时暂停 |
break 文件名:行号 | 在某个文件的指定行暂停(用于多文件项目) |
break 行号 if 条件 | 条件断点,只有条件满足时才暂停 |
例子
(gdb) break main # 在 main 函数入口暂停 (gdb) break 12 # 在第12行暂停 (gdb) break calc.c:5 # 在 calc.c 的第5行暂停 (gdb) break 20 if x > 5 # 第20行只有当 x>5 时才停查看已有断点
(gdb) info breakpoints删除断点
(gdb) delete 断点编号 # 例如 delete 12.run(简写r)—— 启动程序开始调试
作用
让程序在 GDB 里开始运行。一旦遇到设置的断点,程序会立即暂停,交还控制权给 GDB。
基本用法
(gdb) run如果程序需要命令行参数,可以跟在run后面:
(gdb) run arg1 arg2相当于在终端里执行./hello arg1 arg2。
程序一旦暂停,你就可以用其他命令查看当前状态了。
3.next(简写n)—— 逐行执行,不进入函数内部
作用
执行当前代码的下一行,如果这一行里有函数调用,next会直接执行完整个函数,不会进入函数体内。可以理解为“单步越过”。
什么时候用
当你确定某函数没问题,不想进去细看它的内部执行时,就用next。
例子
(gdb) break main (gdb) run Breakpoint 1, main () at hello.c:5 5 int x = 10; (gdb) next 6 int y = add(x, 5); # 假设 add 是一个函数 (gdb) next # 直接执行完 add,不会进入 add 内部 7 printf("%d\n", y);如果当前行是add(x,5),按next后程序直接执行完add,然后停在下一行。
4.step(简写s)—— 逐行执行,会进入函数内部
作用
和next类似,也是执行下一行,但如果当前行有函数调用,step会“钻”进那个函数里,停在函数的第一条语句上。可以理解为“单步进入”。
什么时候用
当你怀疑某个函数内部有问题,需要进去逐行查看时,就用step。
例子
(gdb) break main (gdb) run Breakpoint 1, main () at hello.c:5 5 int x = 10; (gdb) step 6 int y = add(x, 5); (gdb) step # 此时会进入 add 函数内部 add (a=10, b=5) at hello.c:12 12 return a + b;注意:如果当前行没有函数调用,step和next的效果是一样的。
5.print(简写p)—— 查看变量或表达式的值
作用
在程序暂停时,查看某个变量当前的值,或者计算一个表达式的值。
基本用法
(gdb) print 变量名 (gdb) print 表达式示例
(gdb) break main (gdb) run Breakpoint 1, main () at hello.c:7 7 int sum = x + y; (gdb) print x $1 = 10 (gdb) print y $2 = 20 (gdb) print x + y $3 = 30 (gdb) print sum $4 = 0 # 因为 sum 这行还没执行,所以还是初始值 0$1、$2等是 GDB 给每次打印结果分配的编号,后面可以用$1直接引用之前的值。
6.backtrace(简写bt)—— 查看函数调用栈
作用
显示当前程序执行到了哪里,以及它是经过哪些函数调用到达当前位置的。在程序崩溃(Segmentation fault)时极其有用。
基本用法
(gdb) backtrace输出是一个栈帧列表,最上面是当前正在执行的函数,往下面是调用它的上层函数,再下层是更早的调用者。
示例
假设func_c()崩了,我们用bt查看:
(gdb) backtrace #0 0x00005555555551a5 in func_c () at prog.c:15 #1 0x0000555555555185 in func_b () at prog.c:10 #2 0x0000555555555165 in func_a () at prog.c:5 #3 0x0000555555555145 in main () at prog.c:20这告诉我们:main调用了func_a,func_a调用了func_b,func_b调用了func_c,问题就出在func_c里的第15行。
四、GDB 调试实战演示
我们以一个简单的有 bug 的程序为例,完整走一遍调试流程。
源代码buggy.c:
#include<stdio.h>intdivide(inta,intb){returna/b;// 如果 b 是 0 就会崩溃}intmain(){intx=10;inty=0;intz=divide(x,y);printf("Result: %d\n",z);return0;}1. 带调试信息编译
gcc-gbuggy.c-obuggy2. 启动 GDB
gdb ./buggy3. 设置断点在 main 函数
(gdb) break main Breakpoint 1 at 0x1145: file buggy.c, line 7.4. 运行程序
(gdb) run Starting program: /tmp/buggy Breakpoint 1, main () at buggy.c:7 7 int x = 10;5. 用next逐行执行,观察变量
(gdb) next 8 int y = 0; (gdb) next 9 int z = divide(x, y); (gdb) print x $1 = 10 (gdb) print y $2 = 0发现y是 0,这会导致除法错误。
6. 用step进入divide函数
(gdb) step divide (a=10, b=0) at buggy.c:4 4 return a / b;现在我们可以确认b是 0。
7. 查看调用栈(如果此时崩溃,就可以用bt)
我们可以先next让它崩溃:
(gdb) next Program received signal SIGFPE, Arithmetic exception.现在用backtrace看看:
(gdb) backtrace #0 divide (a=10, b=0) at buggy.c:4 #1 0x... in main () at buggy.c:9立刻知道崩溃在divide函数的第4行,由main第9行调用,且参数b=0。这就是 GDB 的价值。
五、✅ GDB 常用命令速查卡
| 命令 | 简写 | 作用 | 示例 |
|---|---|---|---|
break | b | 设置断点 | b main,b 12,b file.c:5 |
run | r | 启动程序(可带参数) | r,r arg1 |
next | n | 执行下一行,不进入函数内部 | n |
step | s | 执行下一行,会进入函数内部 | s |
print | p | 打印变量或表达式的值 | p x,p a+b |
backtrace | bt | 显示函数调用栈 | bt |
其他常用小命令(了解即可):
continue(c):继续运行到下一个断点quit(q):退出 GDBlist(l):显示当前行附近的源代码info locals:显示当前函数的所有局部变量
六、巩固练习
请创建一个新的 C 程序debug_practice.c,内容如下:
#include<stdio.h>intsquare(intn){intresult=n*n;returnresult;}intmain(){inta=5;intb=square(a);intc=square(b);printf("a=%d, b=%d, c=%d\n",a,b,c);return0;}练习步骤(要求用 GDB 完成并观察每一步):
- 用
-g编译程序。 - 启动 GDB,设置断点在
square函数。 - 运行程序,在
square第一次被调用时暂停。 - 用
print n查看参数n的值。 - 用
next执行int result = n * n;,然后用print result查看结果。 - 用
backtrace查看是从哪里调用的square。 - 用
continue让程序继续运行,直到第二次调用square暂停,再次用print n和bt观察不同之处。 - 最后用
continue让程序运行结束,退出 GDB。
参考答案(关键观察点)
- 第一次到
square时,n=5,bt会显示#0 square, #1 main。 - 执行完
result赋值后,result=25。 - 第二次到
square时,n=25(因为 b=25),bt仍类似。 - 全程没有崩溃,但你能体会到断点和打印的配合。
七、学习检查清单
在结束之前,问自己这几个问题,能答出来就说明你真正掌握了:
- 设置断点有哪几种写法?如何删除一个断点?
next和step的区别是什么?什么时候必须用step?- 程序崩溃后,哪个命令能最快定位崩溃的函数和行号?
- 如何查看当前函数里某个局部变量的值?
- 启动 GDB 时,要求可执行文件必须有什么特殊信息?编译时加什么选项?
如果都可以流畅答出,恭喜你,GDB 的入门调试你已经拿下。之后遇到任何 C 程序问题,都可以拿出这六张速查卡来逐步排查,就像用ls、cd一样自然。