您当前的位置:首页 >> 资讯 >> 详情
世界快看:C语言进阶之回调函数详解
来源: 面包芯语      时间:2023-05-06 12:38:13
点击左上方蓝色“一口Linux”,选择“设为星标”
int*p1;char*p2;STRUCT*p3;// STRUCT为我们定义的结构体

下面我们来了解一下函数指针的概念和使用方法。

1. 概念


(相关资料图)

一般为了方便使用,我们会选择:

比如:

typedefint(*Fun1)(int);//声明也可写成int (*Fun1)(int x),但习惯上一般不这样。typedefint(*Fun2)(int,int);//参数为两个整型,返回值为整型typedefvoid(*Fun3)(void);//无参数和返回值typedefvoid*(*Fun4)(void*);//参数和返回值都为void*指针

2. 如何用函数指针调用函数

intFunc(intx);/*声明一个函数*/int(*p)(intx);/*定义一个函数指针*/p=Func;/*将Func函数的首地址赋给指针变量p*/p=&Func;/*将Func函数的首地址赋给指针变量p*/

赋值时函数 Func 不带括号,也不带参数。由于函数名 Func 代表函数的首地址,因此经过赋值以后,指针变量 p 就指向函数 Func() 代码的首地址了。

#includeintMax(int,int);//函数声明intmain(void){int(*p)(int,int);//定义一个函数指针inta,b,c;p=Max;//把函数Max赋给指针变量p,使p指向Max函数printf("pleaseenteraandb:");scanf("%d%d",&a,&b);c=(*p)(a,b);//通过函数指针调用Max函数printf("a=%d\nb=%d\nmax=%d\n",a,b,c);return0;}intMax(intx,inty)//定义Max函数{intz;if(x>y){z=x;}else{z=y;}returnz;}

特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。

p=Max 可以改成 p=&Maxc=(*p)(a,b) 可以改成 c=p(a,b)

3. 函数指针作为某个函数的参数

#include#include//前加一个typedef关键字,这样就定义一个名为FunType函数指针类型,而不是一个FunType变量。

//形式同typedefint*PINT;

typedefvoid(*FunType)(int);

voidmyFun(intx);voidhisFun(intx);voidherFun(intx);voidcallFun(FunTypefp,intx);intmain(){callFun(myFun,100);//传入函数指针常量,作为回调函数callFun(hisFun,200);callFun(herFun,300);return0;}voidcallFun(FunTypefp,intx){fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数}voidmyFun(intx){printf("myFun:%d\n",x);}voidhisFun(intx){printf("hisFun:%d\n",x);}voidherFun(intx){printf("herFun:%d\n",x);}

4. 函数指针作为函数返回类型

有了上面的基础,要写出返回类型为函数指针的函数应该不难了,下面这个例子就是返回类型为函数指针的函数:

void(*func5(int,int,float))(int,int){...}

在开始讲解回调函数前,最后介绍一下函数指针数组。既然函数指针也是指针,那我们就可以用数组来存放函数指针。下面我们看一个函数指针数组的例子:

/*方法 1*/void(*func_array_1[5])(int,int,float);/*方法 2*/typedefvoid(*p_func_array)(int,int,float);p_func_arrayfunc_array_2[5];

我们先来看看百度百科是如何定义回调函数的:

结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2. 为什么要用回调函数?

因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。

intCallback()// /<回调函数{//TODOreturn0;}intmain()// /<主函数{//TODOLibrary(Callback);// /<库函数通过函数指针进行回调//TODOreturn0;}

回调似乎只是函数间的调用,和普通函数调用没啥区别。

3. 怎么使用回调函数?

intCallback_1(inta)// /<回调函数1{printf("Hello,thisisCallback_1:a=%d",a);return0;}intCallback_2(intb)// /<回调函数2{printf("Hello,thisisCallback_2:b=%d",b);return0;}intCallback_3(intc)// /<回调函数3{printf("Hello,thisisCallback_3:c=%d",c);return0;}intHandle(intx,int(*Callback)(int))// /<注意这里用到的函数指针定义{Callback(x);}intmain(){Handle(4,Callback_1);Handle(5,Callback_2);Handle(6,Callback_3);return0;}
#include#include/*****************************************函数指针结构体***************************************/

typedefstruct_OP

{

float(*p_add)(float,float);float(*p_sub)(float,float);float(*p_mul)(float,float);float(*p_div)(float,float);}OP;/*****************************************加减乘除函数***************************************/floatADD(floata,floatb){returna+b;}floatSUB(floata,floatb){returna-b;}floatMUL(floata,floatb){returna*b;}floatDIV(floata,floatb){returna/b;}/*****************************************初始化函数指针***************************************/voidinit_op(OP*op){op->p_add=ADD;op->p_sub=SUB;op->p_mul=&MUL;op->p_div=&DIV;}/*****************************************库函数***************************************/floatadd_sub_mul_div(floata,floatb,float(*op_func)(float,float)){return(*op_func)(a,b);}intmain(intargc,char*argv[]){OP*op=(OP*)malloc(sizeof(OP));init_op(op);/*直接使用函数指针调用函数*/printf("ADD=%f,SUB=%f,MUL=%f,DIV=%f\n",(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\n",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));return0;}
/*********工作状态处理*********/typedefstruct{uint8_tmStatus;uint8_t(*Funtion)(void);//函数指针的形式}M26_WorkStatus_TypeDef;//M26的工作状态集合调用函数/************************************************>M26工作状态集合函数***********************************************/M26_WorkStatus_TypeDefM26_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_tM26_WorkStatus_Call(uint8_tStart){uint8_ti=0;for(i=0;i<12;i++){if(Start==M26_WorkStatus_Tab[i].mStatus){returnM26_WorkStatus_Tab[i].Funtion();}}return0;}

原文:https://blog.csdn.net/qq_41854911/article/details/121058935

精彩文章合集

文章推荐

标签: