一、什么是回调函数?
什么是回调函数?先看官方解释:在计算机程序设计中,回调函数(或简称回调,Callback=call then back,被主函数调用运算后会返回主函数),是指通过参数将函数传递到其它代码的,某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。
这段解释太过于官方了,我们抓住几个关键点:1.它是个函数;2.它被当做参数传递到其他代码,并且会被调用。总结一下:回调函数就是我们以参数的形式传给某个参数,然后这个回调函数会在未来某个时机被调用。
举个例子:我给了你一个手机,并且跟你说,你明天早上10点拿这个手机给我打电话,那么在这个场景里面,这个手机的功能就类似于回调函数,使用(调用)它的时机就是明天早上10点。
在我们写程序的时候,回调函数都有哪些应用场景呢?最经典的就是我们做一些异步操作的时候,比如在JavaScript里面发送网络请求的时候,伪代码如下:
在上面这段代码里面,我们给ajax函数传递了两个回调函数,一个是负责请求成功的时候调用,一个是负责请求失败的时候调用,这个例子再加上我上面的解释,应该就可以理解什么是回调函数了。
二、回调函数是这样的
在说回调函数之前,很有必要先说明一下 “函数指针” 这个概念,它是回调函数能够实现的重要基础。
1、函数指针
C语言中的灵魂:指针。指针的使用和变幻方式,真正的使用起来能让你眼花缭乱。下面是常见的指针的定义:
int *ptr1;
char *ptr2;
struct std *ptr3; // 结构体指针
函数指针:函数指针是指向函数的指针变量。简单理解是指向函数名的指针变量。函数指针既然是指向函数的,那么它就可以像函数一样,用于调用函数、传递参数等操作。函数指针的定义方式如下:
函数返回值类型 (* 指针变量名) (函数参数列表);
“函数返回值类型”:表示该指针变量可以指向具有什么返回值类型的函数;
“函数参数列表”:表示该指针变量可以指向具有什么参数列表的函数。
举例如下:
int (*func1)(void)
int (*func2)(int,char,...)
char (*func3)(int,char,...)
......
从上面的演示可以看到,函数指针的定义就是将一个函数中的 “函数名” 改成“(* 指针变量名)”的方式,从而实现了一个函数指针的定义。
但是这里需要注意的是:“(* 指针变量名)”两端的括号是必须要有的,如果缺少了这对括号,那么这个定义的方式就会变为指针函数。如下:
int *func1(void)
int *func2(int,char,...)
char *func3(int,char,...)
......
这种就不是函数指针了,而是指针函数。两者差别是很大的。
特别需要需要注意的一点是:指向函数的指针变量没有 ++ 和 -- 运算。
对于函数指针,一般为了方便使用,我们会选择另外的一种定义方式:
typedef 函数返回值类型 (* 指针变量名) (函数参数列表);
比如:
typedef int (*Fun1)(int,...);
typedef int (*Fun2)(int, int,...);
typedef void (*Fun3)(void);
typedef void* (*Fun4)(void*);
......
2.函数指针的使用方式
函数指针要怎么使用呢?例子:
因为函数名 Func 代表函数的首地址,所以经过赋值以后,指针变量 p 保存的就是Func的函数入口地址,即 p 就指向函数 Func() 代码的首地址。
为了加深函数指针的使用方式,看下面的一段代码:
#include <stdio.h>
int Max(int, int); //函数声明
int main(void)
{
int a, b, c;
int(*p)(int, int); //定义一个函数指针
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\nb = %d\nmax = %d\n", a, b, c);
return 0;
}
int Max(int x, int y) //定义Max函数
{
int z;
if (x > y) z = x;
else z = y;
return z;
}
特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。
3. 函数指针可以作为函数的参数来使用
函数指针变量本身也是一个变量,也可以作为某个函数的参数进行使用的。如下:
4.回调函数
回调函数:如果一个函数的指针(函数名或地址)作为参数传递给另外一个函数,当这个指针被用来调用其所指向的函数时,就说这个指针所指向的函数是一个回调函数。简明点说:回调函数不是直接调用该函数进行使用的,而是要通过另外的特定事件或者其他函数进行调用的。定义一个函数然后直接调用,都不能称为回调函数。
例程1:
例程2:
可以看出,MyHandle() 函数的参数有一个指针,在 main() 函数里调用MyHandle() 函数的时候,给它传入了函数Callback_1()、Callback_2()、Callback_3() 的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。