gdb是GNU debuger的缩写,在使用gcc生成目标代码文件后,可以使用gdb对其进行调试。
gdb的功能
1.随心所欲的运行程序
2.设置断点,断点可以是某一行或是某一个函数的入口,甚至是条件表示
3.每一步执行可以查看当前的所有信息,例如变量的值
4.动态的改变程序的执行环境
gcc -Wall -g hello.c -o hello.o
gdb就是对生成的hello.o进行调试,使用-g选型就是为了生成利于gdb调试的信息,不加上的话将会只显示汇编代码,而不显示程序的源代码
gdb的常用命令
运行程序:
run 直接运行产程序,直到断点处停下来,同时可以使用'r'缩写run
run agr1 arg2 如果程序需要参数的话,直接在run的后面加上所需要的参数
查看源程序(需要gcc -g的支持):
list 查看最近10行代码,可以缩写为'l'
list func 查看函数名称为func的函数的源代码
list file:func 查看file文件下的func函数的源代码,适用于多个文件中可能出现的函数重名
设置断点与观察点:
break 5 在第五行设置断点
break func 在函数func入口处设置断点
break file:行号 在指定的文件中设置行号
break file:func 在指定文件中的func函数入口设置断点
break if <condition> 在指定条件满足时设置断点 例如break if i=5,在i=5时设置断点
info break 查看当前的所有断点,可以缩写为i b
watch expr 观察某个变量,当变量发生改变时设置断点,例如watch i,继续执行程序,只有在i的值发生 改变时才会停下来
delete n 删除第n个断点,查看是第几个断点使用i b
单步调试:
continue 继续执行程序直到断点或函数的结束,可简写为'c'
step 单步跟踪,相当于step in,遇到函数直接进入,可简写为's'
next 单步跟踪,不进入函数,可简写为'n'
finish 直接执行完程序
until 当在循环中进行单步跟踪时,如果想直接跳出循环,使用until
其他:
直接回车 重新执行上一步的指令
查看运行时数据:
print i 查看变量i的值
ptype i 查看变量i的类型
print arrayname 查看一个数组,print arrayname[2],查看数组第三个元素
print *arrayname@len 查看动态内存,其实可以理解为malloc分配的数组使用该语句查看,len表示要看的 长度,当长度大于分配值也是可以的
print i=5 运行时改变数据
程序错误的调试:
1.编译错误:在编译器阶段就可以发现,不需要使用gdb
2.运行时错误:包括段错误,可能的情况就是数组越界,或是在保护的内存地址写数据
例如:
#include <stdio.h>
#include <stdlib.h>
void segfault()
{
int *p = NULL;
*p = 100; //内存写保护错误
}
int main(void)
{
segfault();
char buf[1] = "a";
buf[10003] = 'A'; //数组越界
printf("%c\n",buf[10003]);
return 0;
}
使用gdb进行调试,直接run,程序会直接在出错地点中断,并打印出行号,此时使用bt栈回溯命令可以看到更多的信息。
此外,对于段错误,也可以使用core文件进行调试,在程序崩溃的时候,一般会生成一个core文件,记录程序崩溃时的内存镜像,并加入调试信息,不过一般系统默认不生成core文件,需要手动设置。
ulimit -c 查看core文件的状态,0的话代表不自动生成core文件
ulimit -c 数字 分配给core文件多少块的空间
ulimit -c unimited 分配多少不设限制
调试时,使用命令:
gdb 文件名 core文件名
3.逻辑错误:程序没有干应该干的事
例如:
#include <stdio.h>
int main(void)
{
int i;
char str[6] = "hello";
char reverse_str[6] = "";
printf("%s\n", str);
for (i=0; i<5; i++)
reverse_str[4-i] = str[i];
printf("%s\n", reverse_str);
return 0;
}
这段代码的功能是将str的内容逆序输出,而结果是什么都没有显示
通过gdb调试,设置断点,打印出reverse_str的值,发现其为\0olleh,第一个字符为\0,自然无法输出