《函数指针》
再讲回调函数之前,插播一下函数指针。
函数指针:其本质是一个指针变量,该指针指向这个函数。简单来说,函数指针就是指向函数的指针。
声明格式:类型说明符 (*函数名) (参数)
《回调函数》
回调函数:一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
--某度的解释
根据某度的解释有可能比较绕口;举一个简单的例子,pcb厂家提供打样pcb服务,pcb厂家都会要求客户决定如何制作自己的pcb板子。比如沉金,板子的厚度,SMT贴片等。其中pcb制作是psb厂家提供的服务,相当与库函数;pcb的制作方式要求是客户提出的,相当于回调函数,而客户告诉pcb厂家如何制作pcb的动作,相当于把回调函数传入到库函数,也称为回调函数。
从图片(来自于某网站)可以看到,回调函数通常和应用处于同一层(因为传入什么样的回调函数是在应用层决定的)。而回调就成了一个高层调用底层,底层再回过头来调用高层的过程。
《例子说明》
例子1:
代码语言:javascript复制#include <stdio.h>
typedef void (*p_fun)();
void call_fun()
{
printf("Hello workn");
}
int main()
{
p_fun fun = call_fun;
fun();
return 0;
}
输出结果:
代码语言:javascript复制Hello work
从这个例子可以看到,我们首先定义了一个函数指针fun,这个函数指针的返回值为void型,然后我们给函数指针赋值,赋值为call_fun,也就是call_fun函数的首地址,此时fun获得了call_fun的地址,fun的地址等于call_fun的地址,所以最终调用fun(),也就相当于调用了call_fun();
不过这个例子并没有实现回调函数本质(函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数),而是反应函数指针的的用法。
例子2:
代码语言:javascript复制#include <stdio.h>
int doubled(int k)
{
return 2*k;
}
int quadruple(int k)
{
return 4*k;
}
int get_odd_number(int k, int (*p_fun)(int))
{
return (*p_fun)(k) 1;
}
int main()
{
printf("%dn", get_odd_number(2, doubled));
printf("%dn", get_odd_number(2, quadruple));
return 0;
}
输出结果:
代码语言:javascript复制5
9
这个例子就符合了回调函数的本质了,函数指针作为
get_odd_number函数的型参(符合:函数的指针(地址)作为参数传递给另一个函数)。其中如第20行的代码,函数指针指向doubled函数(符合:这个指针被用来调用其所指向的函数),doubled函数就是回调函数。
不妨你看,这也体现了C语言抽象的设计,上面的例子中分别求(2*k 1)的奇数,(4*k 1)的奇数。他们的共性就是求奇数,所以get_odd_number函数可以看作把它们的共性抽象出来。
例子3:
我们把上面的例子进行拆分为三个文件,并使用Linux环境来解释,创建三个文件:main.c lib.c lib.h。
main.c:
代码语言:javascript复制#include <stdio.h>
#include "lib.h"
int doubled(int k)
{
return 2*k;
}
int quadruple(int k)
{
return 4*k;
}
int main()
{
printf("%dn", get_odd_number(2, doubled));
printf("%dn", get_odd_number(2, quadruple));
return 0;
}
lib.c:
代码语言:javascript复制#include "lib.h"
int get_odd_number(int k, int (*p_fun)(int))
{
return (*p_fun)(k) 1;
}
lib.h:
代码语言:javascript复制#ifndef __LIB_H
#define __LIB_H
int get_odd_number(int k, int (*p_fun)(int));
#endif
制作动态库:
代码语言:javascript复制rice@rice:~/rice_file$ gcc -shared -fPIC lib.c -o libtest.so
rice@rice:~/rice_file$ sudo cp libtest.so /usr/lib/
rice@rice:~/rice_file$ gcc main.c -L. -ltest -o output
rice@rice:~/rice_file$ ./output
5
9
生成动态库,参数说明: gcc -shared -fPIC lib.c -o libtest.so
-shared : 生成动态库; -fPIC : 生成与位置无关代码; -o :指定生成的目标文件; 使用动态库,参数说明: gcc main.c -L . –ltest -o output -L : 指定库的路径(编译时); 不指定就使用默认路径(/usr/lib/) -lvendor : 指定需要动态链接的库是谁; 这个例子简单的实现了C的封装封装的方式,对外只提供了API。在linux中很多这种链接库的做事,比如第三方的算法等,只提供API,你看不到算法的实现。回调函数在linux里面用的提多的。动态链接库和静态链接库详细内容请看另外两篇