函数指针:即指向函数的指针变量
因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数
正如用指针变量可指向整型变量、字符型、数组一样,这里的指针变量是指向函数
C语言在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址
有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的
函数指针有两个用途:调用函数和做函数的参数
我们首先定义了一个函数指针pfun ,这个函数指针的返回值为void
void (*pfun)(int data);
然后我们给函数指针赋值,赋值为myfun,也就是myfun函数的首地址,在C99中myfun函数名就是myfun函数的首地址
pfun = myfun;
此时pfun获得了myfun的地址,pfun的地址等于myfun的地址,所以最终调用pfun(); 也就相当于调用了myfun();
(*pfun)(100);
typedef
也可以用typedef来定义一个指针函数
定义了一种pfun的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回void类型
typedef void (*pfun)(int data);
pfun p= myfun; //函数指针指向执行函数的地址
这里面的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;
}
回调函数
即通过函数指针调用的函数
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
通过下面的例子我们可以看到将结构体中的函数指针pfun指向了myfun函数地址,在回调函数中我们将函数指针gf.pfun作为rt_data(int data,int (*tr_fun)())函数的参数,即为int (*tr_fun)();
回调函数中的return (*tr_fun)(data)相当于对指针进行了简引用,返回这个指针指向地址的内容值。
#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;
}
回调函数可以把调用者与被调用者分开,所以调用者不关心谁是被调用者
它只需知道存在一个具有特定原型和限制条件的被调用函数
简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
STM32Cube中的回调函数
看一下DMA的中断服务函数
接着往里面跳
这个XferHalfCpltCallback就是函数指针
看一下其定义
这里就跳不下去了,我们看看它是在哪赋值的
我们找找这个函数指针被赋值的地方
这里才是函数指针被赋值的地方
这里才是函数指针实际指向的函数
内部是执行到这个地方