GDB调试(一)
1.GDB调试(一)
GDB调试
GDB简介
GDB的功能
GDB(GNU Debugger)是用于调试 C、C++ 等语言的强大工具。它允许开发者执行以下操作:
- 启动程序并按照预期条件暂停(如断点处)。
- 检查程序中的变量和内存状态。
- 单步执行代码,观察每一步的变化。
- 修改运行中的变量值以测试不同的假设。
- 调试程序崩溃、段错误等问题。
GDB的优缺点
虽然现在大多数 IDE 都集成了强大的调试器,但 GDB 依然是开发者的首选工具之一,特别是在特定场景下。GDB 的优点包括:
-
轻量级:作为命令行工具,GDB 占用资源少,适合在远程服务器或嵌入式系统上使用。
功能强大:支持断点、单步执行、变量查看、调用栈查看、远程调试等功能,适合调试复杂的程序。 -
远程调试:GDB 可以在本地调试远程运行的程序,非常适合服务器端应用或嵌入式开发。
但是,GDB 也有一些缺点:
-
命令行操作有学习成本:相比 IDE 的图形化界面,GDB 的命令行操作对新手不太友好。
-
缺乏图形界面:没有图形化界面,查看变量和执行流程不如 IDE 那样直观。
GDB使用
GDB编译
在使用 GDB 调试时,确保编译程序时使用 -g 选项生成调试信息。这些信息会帮助 GDB 识别源代码中的变量名、函数、行号等:
gcc -g -o test1 test1.c
如果没有加 -g,GDB 只能调试汇编层面的代码,无法看到源码中的具体行号和变量名称。
启动GDB
可以通过gdb+可执行文件来调试程序
gdb test1
测试程序
以下程序用于文件拷贝,可以将a.txt种的内容拷贝到b.txt
//此程序演示文本文件拷贝 test1.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "test_show.h"
int main(int argc, char *argv[])
{
if(argc != 3)
{
printf("这是一个文件复制程序\n");
return -1;
}
FILE *srcfp, *dstfp;
srcfp = dstfp = 0;
//以下定义静态数组和动态数据单纯用于gdb学习测试
int a[6] = {1,2,3,4,5,6};
a[1]++;
test2 *new_test = NULL;
new_test = malloc(sizeof(test2));
if(NULL == new_test)
{
printf("new_test is NULL\n");
return -1;
}
for(int i = 0; i < TEST_ARRAY; i++)
{
new_test->fff[i].aaa = i;
new_test->fff[i].bbb = i+1;
new_test->fff[i].ccc = 'a'+i;
new_test->fff[i].ddd = 'a' + i + 1;
}
show_array_result(new_test);
//以只读的方式打开源文件,方式是rb
if((srcfp = fopen(argv[1], "r+")) == 0)
{
printf("fopen %s failed\n", argv[1]);
return -1;
}
//以只写的方式打开源文件,方式是wb
if((dstfp = fopen(argv[2], "w+")) == 0)
{
printf("fopen %s failed\n", argv[2]);
return -1;
}
int nread = 0; //每次读取到内容的字节数
char strbuf[50];//这里特意让数组大小小一点,方便后续多打印一些nloop
int nloop = 0;//记录while循环的次数
while(1)
{
//从源文件中读取数据,并写入到目标文件
memset(strbuf,0,sizeof(strbuf));
if((nread = fread(strbuf,sizeof(char),sizeof(strbuf),srcfp)) == 0)
{
break;
}
nloop++;
showresult(nloop);
fwrite(strbuf,1,nread,dstfp);
}
fclose(srcfp);
fclose(dstfp);
free(new_test);
printf("文件打印成功\n");
return 0;
}
#include "test_show.h"
void showresult(int nloop)
{
int a = 0;
printf("nloop:%d\n", nloop);
a++;
}
void show_array_result(test2* new_test)
{
if(NULL == new_test)
{
printf("new_test is null\n");
return;
}
for(int i = 0; i < TEST_ARRAY; i++)
{
printf("aaa:%d, bbb:%d, ccc:%c, ddd:%c\n", new_test->fff[i].aaa, new_test->fff[i].bbb, new_test->fff[i].ccc, new_test->fff[i].ddd);
}
}
//test_show.c
#include <stdio.h>
#define TEST_ARRAY (4)
typedef struct
{
int aaa;
int bbb;
char ccc;
char ddd;
}test1;
typedef struct
{
test1 eee;
test1 fff[TEST_ARRAY];
}test2;
void showresult(int nloop);
void show_array_result(test2* new_test);
//test_show.h
cc -g -o test1 test1.c test_show.c test_show.h
./test1 a.txt b.txt
//运行结果
aaa:0, bbb:1, ccc:a, ddd:b
aaa:1, bbb:2, ccc:b, ddd:c
aaa:2, bbb:3, ccc:c, ddd:d
aaa:3, bbb:4, ccc:d, ddd:e
nloop:1
nloop:2
nloop:3
nloop:4
nloop:5
nloop:6
nloop:7
nloop:8
文件打印成功
GDB常用命令
1.查看源代码
命令 | 解析 |
---|---|
list | 查看源代码,默认显示10行 |
list number | 指定查看行的代码,比如list 10,未指定行数则默认查看最近十行源码 |
list fun | 查看fun函数源代码 |
list file:fun/number | 查看file文件fun函数/第number行的源代码 |
set listsize | 设置一次显示源代码的行数 |
show listsize | 查看当前listsize的设置 |
2.程序运行参数
命令 | 解析 |
---|---|
set args | 设置运行参数 |
show args | 命令可以查看设置好的运行参数 |
3.断点的添加
使用break或者b命令添加断点
命令 | 解析 |
---|---|
break function | 在进入指定函数时停住 |
break n | 在指定行号停住,如(gdb) break 46 |
break +offset/-offset | 在当前行号的前面或后面的offset行停住。offiset为自然数。 |
break filename:linenum | 在源文件filename的linenum行处停住。 |
break filename:function | 在源文件filename的function函数的入口处停住。 |
break *address | 在程序运行的内存地址处停住,如如:(gdb) b *0x804835c。 |
break条件判断 | 比如break showresult if nloop==5 |
watch 变量 | 相当于给变量设置断点,每当变量发生变化就断在那里 |
4.断点的删除
命令 | 解析 |
---|---|
delete n | 删除n号断点 |
delete | 删除所有断点 |
clear n | 清除行n上所有断点 |
disable/enable n | 临时关闭或者启用n号断点 |
5.程序运行进度调试
命令 | 解析 |
---|---|
run|r | 从头开始执行程序,直到遇到断点 |
continue|c | 继续执行程序(此时已经run过了),直到下个断点 |
next|n | 执行下一行语句 |
step|s | 单步进入 |
finish | 跳出当前函数 |
jump location | 跳转指令 |
6.打印程序相关信息
命令 | 解析 |
---|---|
print file::variable | 打印文件中的变量 |
print function::variable | 打印函数中的变量 |
display 变量名 | 实时跟踪并输出该变量的变化信息。和 print 命令一样,display 命令也用于调试阶段查看某个变量或表达式的值,它们的区别是,使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来,而 print 命令则不会。 |
undisplay | 取消display的设置 |
7.修改程序相关信息
命令 | 解析 |
---|---|
set | 该命令用来改变运行过程中的变量值 |
8.函数调用查看
命令 | 解析 |
---|---|
backtrack | 命令产生一张列表,包含着从最近的过程开始的所有有效过程和调用这些过程的参数。 |
info frame | 显示栈帧信息 |
info frame命令会依次打印出当前栈帧的如下信息:
1、当前栈帧的编号,以及栈帧的地址;
2、当前栈帧对应函数的存储地址,以及该函数被调用时的代码存储的地址
3、当前函数的调用者,对应的栈帧的地址;
4、编写此栈帧所用的编程语言;
5、函数参数的存储地址以及值;
6、函数中局部变量的存储地址;
7、栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用eip 表示)、
堆栈基指针寄存器(64位环境用 rbp表示,32位环境用 ebp表示)等。
9.多线程/进程下调试GDB
命令 | 解析 |
---|---|
-p 进程名 | 对相关进程进行调试 |
attach 进程名 | 切换到相关进程 |
info thread | 列出线程 |
thread 线程名 | 切换至线程 |
info register | 列出寄存器 |
info frame | 列出栈帧 |
info files | 列出当前文件 |
info share | 列出当前共享库 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)