函数指针
函数指针:指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。
首先来看一个函数指针的例子:
实例
#include <stdio.h>
#include <stdlib.h>
void (*pfun)(int data);
typedef void (*tpfun)(int data);
typedef struct {
void (*spfun)(int data);
} SFUN;
void myfun(int data)
{
printf("get data: %d\n", data);
}
int main(int argc, char* argv[])
{
pfun = myfun;
(*pfun)(10);
tpfun tfun = myfun;
tfun(20);
SFUN sfun;
sfun.spfun = myfun;
sfun.spfun(30);
return 0;
}
输出:
./test
get data: 10
get data: 20
get data: 30
A
上述使用了3种方法;
第一种用法:直接使用函数指针
第二种用法:typedef 变量类型 别名
第三种用结构体函数指针的方法
回调函数
通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
实例:
#include <stdio.h>
#include <stdlib.h>
typedef struct gfun{
int (*pfun)(int);
}gfun;
int (*ppfun)(int);
typedef int (*ptfun)(int);
int myfun(int data)
{
printf("get data:%d\n", data);
return (data*2);
}
int rt_data(int data, int (*tr_fun)())
{
return ((*tr_fun)(data));
}
int main(int argc, char* argv[])
{
int ret;
gfun gf;
gf.pfun = myfun;
ret = rt_data(10, gf.pfun);
printf("ret:%d\n", ret);
ppfun = myfun;
ret = rt_data(20, (*ppfun));
printf("ret:%d\n", ret);
ptfun pttfun = myfun;
ret = rt_data(30, pttfun);
printf("ret:%d\n", ret);
return 0;
}
以结构体为例,在回调函数中我们将函数指针gf.pfun作为rt_data(int data,int (*tr_fun)())函数的参数即为int (*tr_fun)();回调函数中的return (*tr_fun)(data)相当于对指针进行了简引用,返回这个指针指向地址的内容值。
回调函数的意义
我们简单的讨论一下使用回调函数的优势所在。
首先,可以使上层的应用更完整,但又不需要考虑底层的实现细节。比如我们设计了一个通讯应用,但在设计时我并不能确定底层接口,或者说不想局限于某一接口。那么我们可以将接口部分的实现留在具体使用中,所以采用回调函数的方式就非常方便。
其次,可以使应用更加灵活,这是显而易见的。比如我们设计一个通讯协议栈,这个协议栈在什么平台使用并不局限,我们使用回调的方式具体实现平台相关部分,而协议栈的内核这可以使用于多种平台。
再者,可以把调用者与被调用者分开,这样调用者不关心谁是被调用者,也不关心他的具体实现。使得软件的设计更加独立,方便与协作或者移植。其实细说起来还有很多,在此仅列举上述几点。
谈完回调函数的意义,我们就有了用户和开发者之间的概念,举个例子,用户是实现myfun这个函数,开发者是实现rt_data函数,根据需求用户将myfun函数以参数的形式传入开发者的rt_data函数中,rt_data函数就能返回给相应的数据给用户,开发者不用告诉用户它实现了什么,用户也并不知道开发者怎么实现,用户只用传入自己的函数,便可以得到开发者实现的函数返回值,开发者可以将内容封装起来,将头文件以及库文件提供给用户。
实例:
fun.h
#ifndef _FUN_H
#define _FUN_H
int tr_data(int data, int (*pfun)(int));
#endif
fun.c
#include "fun.h"
int tr_data(int data, int (*pfun)(int))
{
return ((*pfun)(data));
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "fun.h"
typedef struct {
int (*ptfun)(int);
} tspfun;
int testfun(int data)
{
printf("get data:%d\n", data);
return (data*2);
}
int main()
{
int ret;
tspfun spfun;
spfun.ptfun = testfun;
ret = tr_data(10, spfun.ptfun);
printf("data:%d\n", ret);
return 0;
}
生成动态库:
gcc -shared -fPIC fun.c -o libfun.so
-shared : 生成动态库;
-fPIC : 生成与位置无关代码;
-o :指定生成的目标文件;
使用动态库:
gcc -o main main.c -L. -lfun
-L : 指定库的路径(编译时); 不指定就使用默认路径(/usr/lib/lib)
-lfun : 指定需要动态链接的库是谁;
代码运行时需要加载动态库:
./main 加载动态库 (默认加载路径:/usr/lib /lib ./ …)
使用回调函数
在大多数情况下,我们可能都是将函数指针作为参数传递给调用者来实现回调。比如我们声明如下函数:
void function1(int var1,int var2)
void function2(void (*fc)(int,int),float a,int b)
回调函数的参数为被调用函数的参数以及以被调用函数形式的函数指针。
调用时使用function2(function1,a,b)就可以了。或者使用function1初始化函数指针,然后使用函数指针作为参数传递。
例如:
typedef struct gfun{
int (*pfun)(int, int);
}gfun;
int (*ppfun)(int, int);
typedef int (*ptfun)(int, int);
gfun gf;
gf.pfun = myfun;
ppfun = myfun;
ptfun pttfun = myfun;
function2(gf.pfun,a,b)
function2((*ppfun),a,b)
function2(pttfun,a,b)
实例
#include <stdio.h>
int fun1(int a, int b)
{
int ret;
ret = (a < b) ? a : b;
return ret;
}
int fun2(int a, int b, int (*pfun)(int, int))
{
return (*pfun)(a, b);
}
int main()
{
int ret;
ret = fun2(2, 3, fun1);
printf("less:%d\n", ret);
return 0;
}
输出:
./test
less:2
参考:
C语言中函数指针和回调函数的详解_weixin_39939425的博客-CSDN博客_函数指针
C语言 回调函数详解_老五的作坊-CSDN博客_c语言回调函数详解