123.你知道回调函数吗?它的作用?
123.你知道回调函数吗?它的作用?
1.当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数;
2.回调函数就相当于一个中断处理函数,由系统在符合你设定的条件时自动调用。为此,你需要做三件事:1,声明;2,定义;3,设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用;
3.回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数;
4.因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。
回调函数是一个通过函数指针调用的函数。当你把函数的指针(地址)作为参数传递给另一个函数时,当这个指针被用来调用它所指向的函数时,我们就说这是回调函数。
回调函数在编程中非常有用,因为它们可以将调用者和被调用者分离。通过使用回调函数,调用者可以通知被调用者在特定情况下执行某些操作。以下是一些回调函数的使用场景:
1.事件处理:在许多应用程序中,当某个事件发生时,比如按键按下或窗口关闭,需要通知应用程序执行特定的操作。通过使用回调函数,应用程序可以将事件处理程序传递给事件处理函数,以便在事件发生时调用。
2.异步编程:在某些情况下,当一个操作需要花费较长时间才能完成时,可以使用回调函数来通知操作已经完成。例如,当一个函数需要执行一个长时间运行的任务时,可以将一个回调函数传递给它,以便在任务完成后调用。
3.排序和过滤:在某些算法中,需要对一组数据进行排序或过滤。通过使用回调函数,可以在每次比较或检查数据时调用,以便根据特定的条件进行排序或过滤。
在使用回调函数时,需要注意以下几点:
- 确保回调函数的参数和返回值与调用方所期望的相匹配。回调函数的参数和返回值应该与调用方所期望的相同,否则可能会导致不正确的行为。
- 确保回调函数在需要时可以被正确调用。在调用方中,必须确保回调函数的指针是有效的,并且可以在需要时被正确调用。
- 避免悬挂指针和空指针异常。当使用回调函数时,需要注意避免悬挂指针和空指针异常的情况发生。为了确保回调函数的正确性,应该在调用方中检查指针是否为空或悬挂指针。
总之,回调函数是一种强大的编程技术,可以将调用者和被调用者分离,并在特定情况下执行特定的操作。
1.函数指针
[82.函数指针? - CodeMagicianT - 博客园 (cnblogs.com)]
2.回调函数
2.1定义
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。
结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。
我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调
。
如果代码立即被执行就称为同步回调
,如果过后再执行,则称之为异步回调
。
回调函数
就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2.2为什么要用回调函数?
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活地使用不同的方法。
int Callback() ///< 回调函数
{
// TODO
return 0;
}
int main() ///< 主函数
{
// TODO
Library(Callback); ///< 库函数通过函数指针进行回调
// TODO
return 0;
}
回调似乎只是函数间的调用,和普通函数调用没啥区别。
但仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。
这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀。
2.3怎么使用回调函数?
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;
}
如上述代码:可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。
2.4例子
1.是一个四则运算的简单回调函数例子:
#include <stdio.h>
#include <stdlib.h>
/****************************************
* 函数指针结构体
***************************************/
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\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));
return 0;
}
2.回调函数实例(很有用)
一个GPRS模块联网的小项目,使用过的同学大概知道2G、4G、NB等模块要想实现无线联网功能都需要经历模块上电初始化、注册网络、查询网络信息质量、连接服务器等步骤,这里的的例子就是,利用一个状态机函数(根据不同状态依次调用不同实现方法的函数),通过回调函数的方式依次调用不同的函数,实现模块联网功能,如下:
/********* 工作状态处理 *********/
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;
}
所以,如果有人想做个NB
模块联网项目,可以copy
上面的框架,只需要修改回调函数内部的具体实现,或者增加、减少回调函数,就可以很简洁快速的实现模块联网。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)