gdb调试工具
gdb调试的作用
gdb用于调试代码中逻辑错误,而非语法错误
gdb调试流程
- 生成可以使用gdb调试的执行文件
gcc -g xxx.c ---> 生成的文件可以使用gdb调试
- 进入gdb工具
gdb 可执行文件 ---> 使用gdb工具开始调试可执行文件
r/run:运行代码
l/list:显示当前行下面的10行代码
b/break 函数名或者行数:添加断点
info b:查看断点信息
d/delete num:删除断点
n/next:单步调试程序,如果是函数整体执行,不会进入函数内部
s/step:单步调试程序,如果是函数会进入
p/print:查看变量的值
运行代码
查看代码
设置断点
- 在add函数处添加了断点,然后直接运行程序
- 然后就停在了第5行(实际是从第12行调用的),也就是在add中return的位置
- 执行n执行,继续走一步,来到第6行
- 再次执行n指令,也就是num赋值完毕,来到第13行
- 又一次执行n指令,执行printf语句,显示了下一行语句:return 0;
查看变量的值
断点情况
查看断点
删除断点
调试 core 文件
core何时生成:当程序出现重大错误时,会生成一个临时的镜像文件,保存程序状态(段错误)
由于系统的权限问题,不是每一次段错误都会生成core文件
ulimit -a 查看文件的权限
core file size 如果为0该文件不会生成
ulimit -c unlimited :使用命令取消限制
如果使用 ulimit -c unlimited 后,还不能再当前目录生成core文件
在终端执行以下命令
sudo bash -c "echo core > /proc/sys/kernel/core_pattern"
需要同时gdb 可执行文件和core文件
gdb a.out core
程序会停在发生错误的一行
C高级部分
指针函数
本质上是函数,函数的返回值是地址
定义格式
存储类型 数据类型 *函数名(形参)
{
函数体;
return 地址;
}
函数指针
本质上是指针,指向函数的指针
函数名:函数的首地址
定义格式
存储类型 数据类型 (*函数指针名)(形参列表)
数据类型:指向函数的返回值类型一致
形参列表:指向函数的参数列表数据类型一致
通过函数指针调用函数
函数指针变量名(实参) ---> 指针代替函数名去调用函数
(*函数指针变量名)(实参)
什么情况下使用函数指针:
有机会看到 Linux 内核原码的时候,很多函数的参数用的是函数指针
这样写有什么意义?
方便、扩展性强,只需要一个 test 函数,可以实现不同的功能,这就是"多态"的特性,
面向对象三大特性:封装、继承、多态,我只需要一个接口,实现不同功能,就可以简化代码
函数指针可以将函数作为参数传递给其他函数调用
返回值和参数为空
函数指针指向指针函数
一下程序的执行结果是什么:
#include <stdio.h>void *test(void *arg)
{printf("333\n");printf("%s\n", (char *)arg);printf("444\n");return NULL;
}int fun(void *(*p)(void *), void *arg)
{printf("111\n");p(arg);printf("222\n");return 0;
}int main(int argc, char const *argv[])
{fun(test, "hello");printf("555\n");return 0;
}
1. char *fun(int a, int *b);
定义一个函数指针指向这个函数:char *(*p)(int ,int *) = fun;2. void fun1(char *spl, int **st, int n);
定义一个函数指针指向这个函数:void (*p)(char *, int **, int)= fun1;3. void *fun2(int (*sp)[3], int m);
定义一个函数指针指向这个函数:void *(*p)(int (*)[3], int) = fun2;
函数指针数组
本质是数组,元素同类型的函数指针
定义格式
存储类型 数据类型 (*函数指针数组名[元素个数]) (形参列表)