最近在研究OBS源码在源码中作者大量应用了函数指针和回调函数的这种方式,这种方式代码的可读性和效率有了很大的提高。
一、函数指针的介绍
函数指针:指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。
再来看看函数指针的三种创建方式
第一种:
#include <stdio.h>
#include <stdlib.h>
void (*pfun)(int data);
void myfun(int data)
{
printf("get data:%d\n", data);
}
int main(int argc, char* argv[])
{
pfun = myfun;
(*pfun)(100);
return 0;
}
//从这个例子可以看到,我们首先定义了一个函数指针pfun,
//这个函数指针的返回值为void型, 然后我们给函数指针赋值,赋值为myfun,
//也就是myfun函数的首地址,在C99中myfun函数名就是myfun函数的首地址,
//此时pfun获得了myfun的地址,pfun的地址等于myfun的地址,所以最终调用pfun(); 也就相当于调用了myfun();
第二种:typedef 原变量类型 别名
也可以用typedef来定义一个指针函数这样使在大型代码中更加简洁
#include <stdio.h>
#include <stdlib.h>
typedef void (*pfun)(int data);
///typedef的功能是定义新的类型。第一句就是定义了一种pfun的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回void类型。*/
void myfun(int data)
{
printf("get data:%d\n", data);
}
int main(int argc, char* argv[])
{
pfun p = myfun; //函数指针指向执行函数的地址
p(100);
return 0;
}
这里面的pfun代表的是函数的类型,通过pfun来代表void (*)(int)函数类型即pfun是指针函数的别名,pfun p相当于定义了一个
void (*p)(int)函数指针。p = myfun可以理解为将函数指针p指向myfun函数的地址,p(100);相当于执行myfun(100);
第三种:
#include <stdio.h>
#include <stdlib.h>
typedef struct gfun {
void (*pfun)(int);
}gfun;
void myfun(int data)
{
printf("get data:%d\n", data);
}
int main(int argc, char* argv[])
{
gfun gcode = {
.pfun = myfun, //将函数指针指向要调用函数的地址
};
gcode.pfun(100);
return 0;
}
这三种的运行结果都是一样的:100
二、回调函数
回调函数:通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
下面是一个回调函数的例子:
#include <stdio.h>
#include <stdlib.h>
typedef struct gfun {
int (*pfun)(int);
}gfun;
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(100, gf.pfun);
printf("return data:%d\n", ret);
return 0;
}
通过上面的例子我们可以看到将结构体中的函数指针指向了myfun函数地址,在回调函数中我们将函数指针gf.pfun作为rt_data(int data,int (*tr_fun)())函数的参数即为int (*tr_fun)();回调函数中的return (*tr_fun)(data)相当于对指针进行了简引用,返回这个指针指向地址的内容值。
三、回调函数的意义
可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。