一、函数指针
在讲回调函数之前,我们需要了解函数指针。
我们都知道,C语言的灵魂是指针,我们经常使用整型指针,字符串指针,结构体指针等。
int *p1; char *p2; STRUCT *p3; // STRUCT为我们定义的结构体
typedef int (*Fun1)(int); //声明也可写成int (*Fun1)(int x),但习惯上一般不这样。 typedef int (*Fun2)(int, int); //参数为两个整型,返回值为整型 typedef void (*Fun3)(void); //无参数和返回值 typedef void* (*Fun4)(void*); //参数和返回值都为void*指针2. 如何用函数指针调用函数
int Func(int x); /*声明一个函数*/ int (*p) (int x); /*定义一个函数指针*/ p = Func; /*将Func函数的首地址赋给指针变量p*/ p = &Func; /*将Func函数的首地址赋给指针变量p*/赋值时函数 Func 不带括号,也不带参数。由于函数名 Func 代表函数的首地址,因此经过赋值以后,指针变量 p 就指向函数 Func() 代码的首地址了。
#include特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。int Max(int, int); //函数声明 int main(void) { int(*p)(int, int); //定义一个函数指针 int a, b, c; p = Max; //把函数Max赋给指针变量p, 使p指向Max函数 printf("please enter a and b:"); scanf("%d%d", &a, &b); c = (*p)(a, b); //通过函数指针调用Max函数 printf("a = %d b = %d max = %d ", a, b, c); return 0; } int Max(int x, int y) //定义Max函数 { int z; if (x > y) { z = x; } else { z = y; } return z; }
p = Max可以改成 p = &Max c = (*p)(a, b) 可以改成 c = p(a, b)
#include#include typedef void(*FunType)(int); //前加一个typedef关键字,这样就定义一个名为FunType函数指针类型,而不是一个FunType变量。 //形式同 typedef int* PINT; void myFun(int x); void hisFun(int x); void herFun(int x); void callFun(FunType fp,int x); int main() { callFun(myFun,100);//传入函数指针常量,作为回调函数 callFun(hisFun,200); callFun(herFun,300); return 0; } void callFun(FunType fp,int x) { fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数 } void myFun(int x) { printf("myFun: %d ",x); } void hisFun(int x) { printf("hisFun: %d ",x); } void herFun(int x) { printf("herFun: %d ",x); }
void (* func5(int, int, float ))(int, int) { ... }
/* 方法1 */ void (*func_array_1[5])(int, int, float); /* 方法2 */ typedef void (*p_func_array)(int, int, float); p_func_array func_array_2[5];
函数指针常量 :Max;函数指针变量:p;
数名调用如果都得如(*myFun)(10)这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许myFun(10)这种形式地调用(这样方便多了,并与数学中的函数形式一样)。
在函数指针变量也可以存入一个数组内。数组的声明方法:int (*fArray[10]) ( int );
二、回调函数
1. 什么是回调函数
我们先来看看百度百科是如何定义回调函数的:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
这段话比较长,也比较绕口。下面我通过一幅图来说明什么是回调:
假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。
结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。
我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。
如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2. 为什么要用回调函数?
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
int Callback() // /< 回调函数 { // TODO return 0; } int main() // /< 主函数 { // TODO Library(Callback); // /< 库函数通过函数指针进行回调 // TODO return 0; }回调似乎只是函数间的调用,和普通函数调用没啥区别。
int Callback_1(int a) // /< 回调函数1 { printf("Hello, this is Callback_1: a = %d ", a); return 0; } int Callback_2(int b) // /< 回调函数2 { printf("Hello, this is Callback_2: b = %d ", b); return 0; } int Callback_3(int c) // /< 回调函数3 { printf("Hello, this is Callback_3: c = %d ", c); return 0; } int Handle(int x, int (*Callback)(int)) // /< 注意这里用到的函数指针定义 { Callback(x); } int main() { Handle(4, Callback_1); Handle(5, Callback_2); Handle(6, Callback_3); return 0; }
#include#include /**************************************** * 函数指针结构体 ***************************************/ typedef struct _OP { float (*p_add)(float, float); float (*p_sub)(float, float); float (*p_mul)(float, float); float (*p_div)(float, float); } OP; /**************************************** * 加减乘除函数 ***************************************/ float ADD(float a, float b) { return a + b; } float SUB(float a, float b) { return a - b; } float MUL(float a, float b) { return a * b; } float DIV(float a, float b) { return a / b; } /**************************************** * 初始化函数指针 ***************************************/ void init_op(OP *op) { op->p_add = ADD; op->p_sub = SUB; op->p_mul = &MUL; op->p_div = &DIV; } /**************************************** * 库函数 ***************************************/ float add_sub_mul_div(float a, float b, float (*op_func)(float, float)) { return (*op_func)(a, b); } int main(int argc, char *argv[]) { OP *op = (OP *)malloc(sizeof(OP)); init_op(op); /* 直接使用函数指针调用函数 */ printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f ", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2), (op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2)); /* 调用回调函数 */ printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f ", add_sub_mul_div(1.3, 2.2, ADD), add_sub_mul_div(1.3, 2.2, SUB), add_sub_mul_div(1.3, 2.2, MUL), add_sub_mul_div(1.3, 2.2, DIV)); return 0; }
/********* 工作状态处理 *********/ typedef struct { uint8_t mStatus; uint8_t (* Funtion)(void); //函数指针的形式 } M26_WorkStatus_TypeDef; //M26的工作状态集合调用函数 /********************************************** ** >M26工作状态集合函数 ***********************************************/ M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] = { {GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, //模块关机 {GPRS_NETWORK_OPEN, M26_PWRKEY_On }, //模块开机 {GPRS_NETWORK_Start, M26_Work_Init }, //管脚初始化 {GPRS_NETWORK_CONF, M26_NET_Config }, //AT指令配置 {GPRS_NETWORK_LINK_CTC, M26_LINK_CTC }, //连接调度中心 {GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC }, //等待调度中心回复 {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM }, //连接前置机 {GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM }, //等待前置机回复 {GPRS_NETWORK_COMM, M26_COMM }, //正常工作 {GPRS_NETWORK_WAIT_Sig, M26_WAIT_Sig }, //等待信号回复 {GPRS_NETWORK_GetSignal, M26_GetSignal }, //获取信号值 {GPRS_NETWORK_RESTART, M26_RESET }, //模块重启 } /********************************************** ** >M26模块工作状态机,依次调用里面的12个函数 ***********************************************/ uint8_t M26_WorkStatus_Call(uint8_t Start) { uint8_t i = 0; for(i = 0; i < 12; i++) { if(Start == M26_WorkStatus_Tab[i].mStatus) { return M26_WorkStatus_Tab[i].Funtion(); } } return 0; }
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !