钩子函数
钩子函数也叫回调函数,是通过函数指针来实现的,那我们来看看什么是函数指针。
首先看看以下例子:
int *p;
int a,b;
我们可以让指针p先后指向a, b,这样,p就先后代表了不同变量的地址
p = &a;
p = &b;
同样地,函数的指针可以指向不同的函数,从而完成不同的功能。话说, 函数指针是怎么定义呢? 函数指针和普通的指针没什么区别,只是函数指针是指向函数的指针。函数指针的定义方式起始和函数声明方式类似, 说白了就是函数声明,唯一不同的是定义的函数指针是可以指向任何具有相同传入参数和相同返回类型的函数,说这么多还不如举个例子来的实际点。
int (*g_pFun) (int x, int y);
这里定义了一个返回值为int类型,有两个int类型传入参数的函数,假如我在写代码的时候,写了一个这样的函数:
int Max(int x, int y)
{
}
Max函数是返回int类型,传入两个int类型参数的函数,这个函数类型是不是和我们定义的g_pFun的函数类型一致, 那么g_pFun可以指向Max,g_pFun = Max; 这样我们就可以使用g_pFun 来调用Max函数了, 比如,g_pFun(10, 12)和Max(10, 12)是一样的效果。说到这里,大家已经明白函数指针的用法了吧?
接下来我们看一个函数指针的实例代码:
#include "stdio.h"
void fun1(void)
{
printf("i am fun1\r\n");
}
void fun2(void)
{
printf("i am fun2\r\n");
}
int main(int argc, char const *argv[])
{
void (* fun)(void); //定义一个函数指针
// 让fun指向fun1
fun = fun1;
// 执行fun
fun();
// 让fun指向fun2
fun = fun2;
// 执行fun
fun();
return 0;
}
这段代码中,首先写了两个无传入参数,无返回值的函数fun1 和 fun2, 然后定义了一个无返回值无传入参数的函数指针;main函数通过将fun指向fun1和fun2 然后再执行fun,达到调用fun1和fun2的作用。
运行结果:
其实说到这,我们已经会用钩子函数了,只是还没引入专业术语而已,在这里,fun 1和fun 2就是钩子函数,把函数指针fun指向fun 1和fun 2的过程称为“挂钩子”, 这个很形象吧。其实在这有人会问,为什么我不直接调用fun 1和fun 2函数,偏偏要通过定义一个函数指针来实现,这不是变得更麻烦,说到这,我只想说有些实物是适合在不同的场合,不是一定得这样用的, 我先说说一个场景。这是因为,我们在写main函数的时候,可能还不知道它会完成什么功能,这时候留下函数指针作为接口,可以挂上不同的函数完成不同的功能,究竟执行什么功能由钩子函数的编写者完成。
那我们平时怎么用的呢?
在我们的代码中,常常把挂钩子的过程叫做注册,会提供一个注册函数,让使用者把自己编写的钩子函数挂在已经声明的函数指针上,这个注册函数的参数就是我们的函数指针了,比如,我们可以给刚才的函数指针提供一个注册函数:
int RegFun(int(*pFun)(int x, int y)) \*****注册函数的参数是函数指针***\
{
g_pFun = pFun;
return 0;
}
那么上述代码改为以下代码:
#include "stdio.h"
void (* fun)(void); //定义一个函数指针
void fun1(void)
{
printf("i am fun1\r\n");
}
void fun2(void)
{
printf("i am fun2\r\n");
}
int RegFun(void (*pFun)(void))//形参为函数相应类型的函数指针
{
fun = pFun; //将pFun挂钩子
return 0;
}
int main(int argc, char const *argv[])
{
// 挂fun1钩子函数
RegFun(fun1);
// 执行fun
fun();
// 挂fun2钩子函数
RegFun(fun2);
// 执行fun
fun();
return 0;
}
注意:为了便于使用,函数指针往往被声明为全局变量,这也是刚才把函数指针的名字命名为g_pFun的原因。
下面我们来进行一下实战演习,比如,平台部分要执行某一个操作,但是具体的操作还不确定,我们完成这样的代码:
#include "stdio.h"
int (* g_pFun) (int x, int y); /*函数指针*/
int Plat(void)
{
int r;
int a = 10;
int b = 15;
r = g_pFun(a, b);
printf("%d\n", r);
return 0;
}
//另外,平台部分再提供一个注册函数:
int RegFun(int (*pFun)(int x, int y))
{
g_pFun = pFun;
return 0;
}
//应用模块完成具体的函数的功能:
int Max(int x, int y)
{
if(x > y)
{
return x;
}
else
{
return y;
}
}
int Min(int x, int y)
{
if (x < y)
return x;
else
return y;
}
int main(int argc, char const *argv[])
{
/* code */
RegFun(Min);
Plat();
RegFun(Max);
Plat();
return 0;
}
在嵌入式系统中,底层不知道应用层需要完成什么功能, 往往会提供像这样子的函数回调方式供应用层使用。