使用gdb进行调试
我们将使用GNU的调试器gdb调试这个程序。gdb是一个功能很强大的调试器,它是一个自由软件,能够用在许多UNIX平台上。它同时也是Linux系统中的默认调试器。gdb已被移植到许多其他的计算机平台上,并且能够用于调试嵌入式实时系统。
单进程、单线程基础调试命令
l //显示 main 函数所在的文件的源代码
list 文件名:num //显示 filename 文件 num 行上下的源代码
b 行号 //给指定行添加断点
b 函数名 //给指点函数的第一有效行添加一个断点
info break //显示断点信息
delete 断点号 //删除指定断点
disable 断点号 //将断点设定为无效的,不加断点号,将所有断点设置为无效
enable 断点号 //将断点设定为有效的,不加断点号,将所有断点设置为有效
r(run) //运行程序
n(next) //单步执行
c (continue) //继续执行,直接执行到下一个断点处
s //进入将要被调用的函数中执行
finish //跳出函数
q //退出调试
p val //打印变量 val 的值
p &val //打印变量 val 的地址
p a+b //打印表达式的值
p arr(数组名) //打印数组所有元素的值
p *parr@len //用指向数组的指针打印数组所有元素的值
display //自动显示,参数和 p 命令一样
info display //显示自动显示信息
undisplay + 编号 //删除指定的自动显示
ptype val //显示变量类型
bt //显示函数调用栈
Debug 版本和 Release 版本
Debug 版本
Debug 版本为可调式版本,生成的可执行文件中包含调试需要的信息。我们作为开发人员,最常用的就是 debug 版本的可执行文件。
Debug 版本的生成:
因为调试信息是在编译过程时加入到中间文件(.o)中的,所以必须在编译时控制其生成包含调试信息的中间文件。
gcc -c hello.c -g —> 生成包含调试信息的中间文件
gcc -o hello hello.o
或者 gcc -o hello hello.c -g
Release 版本
Release 版本为发行版本,是提供给用户使用的版本。用 gcc 默认生成的就是 Release 版本。
首先将源代码编译、链接生成 Debug 版本的可执行文件,然后通过gdb Debug 版本的可执行文件名进入调试模式
1.写一个有问题的C语言程序,从键盘上输入一个字符串,当字符串不为“end”时,一直输入,直到输入字符串“end”退出程序
具体演示过程如下:
接下来运行以下程序看看
编译:
运行: (发现输入end后仍然会打印end,而没有正常退出)
我们发现当输入字符串“end”时,并未退出程序,所以要对程序进行调试,寻找错误
接下来调试:
当遇到这样的情况时,说明main程序没有包含调试信息:
解决方法:gcc -o main main.c -g
启动gdb调试方法:
l:显示代码 一次显示十行 (l+行号 指定想要跳转到的行号)
启动程序:r: 运行程序 或者启动程序
单步执行:n
打印 :p
找到问题 end\n fgets会读到\n:
打印buff,我们发现buff是“end\n”,并不是“end”,显然与“end”不相等,找到问题所在
退出: q
修改代码:
1 #include<stdio.h>
2 #include<string.h>
3 int main()
4 {
5 while(1)
6 {
7 printf("input:\n");
8 char buff[128] = {0};
9 fgets(buff,128,stdin);
10 buff[strlen(buff)-1] = '\0';
11 // buff[strlen(buff)-1] = 0;
12 if(strcmp(buff,"end") == 0)
13 {
14 break;
15 }
16 printf("read:%s\n",buff);
17 }
18 return 0;
19 }
重新编译和运行。
2.调试程序有多个文件
写一个add.c
在main.c中引用
声明add
一步完成编译
在add.c的第四行加断点,在main.c的第十二行加断点:
b+函数名: 在函数的入口加断点
运行程序 单步执行:
c (continue) //继续执行,直接执行到下一个断点处
3. 演示gdb调试怎么进入函数
s //进入将要被调用的函数中执行
总结:
以后程序出现问题的时候调试,跟踪代码