很久没有写 blog 了,这次写一点与回调函数相关的内容...🧐
此文算是之前学习过程中,记录的笔记吧。
概念
首先了解一下回调的概念:
回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。
以上解释来自百度百科 - 回调函数。仅从简单理解的角度来讲,个人认为以这样的方式说明回调函数是什么,很直观明了。所以,这篇文章中所提到的回调函数就是指被作为参数传递的函数。
下面,再研究一下 C 中回调函数的使用。
应用
如上所述,C 中的回调函数只能用函数指针实现,具体如何,直接看下面的示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef void (*pfunc)();
void ppfunc() {
printf("Here is ppfunc!\n");
}
void func(pfunc p) {
p();
printf("Here is func!\n");
}
int main() {
func(&ppfunc);
return 0;
}
在上面的代码中,利用typedef
关键字定义了一个叫做pfunc
的函数指针,这个函数指针指向的函数有两个“特点”:
- 没有参数
- 返回值为
void
另外,上述代码还定义了一个叫做func
的函数,这个函数以pfunc
类型的函数指针p
作为参数,返回值为void
,且在其函数体内还调用了p
指向的函数,并打印输出了Here is func!
。
最终,这个程序的输出结果是:1
2Here is ppfunc!
Here is func!
可以发现,在main
函数中通过传递函数指针ppfunc
(实际上,这里把函数的入口地址叫做函数指针不是很严谨的说法,仅仅是为了方便说明>),在func
函数中成功调用了ppfunc
函数。此时,ppfunc
就叫做回调函数,func
就叫做主调函数。
整个执行过程,也可以简单的概括为:在调用func
函数的过程中,通过传递过来的函数指针p
来调用ppfunc
函数。
现在可以发现,要想掌握 C 中回调函数的用法,必须要具备:
- 理解函数指针概念
- 理解
typedef
关键字定义函数指针用法 - 理解函数的本质:内存中的代码段
同时,也会产生这样的疑问:如果只是为了调用ppfunc
函数,为什么不直接在func
函数里调用ppfunc
函数呢?用回调函数的方式来调用,不是多此一举吗?
其实上面的代码仅仅是为了用作说明而写出来的,实际情况当然不会这么多此一举。
在 C 语言中比较常见使用回调函数的例子,就是标准库的qsort
函数,比如下面的示例代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int cmp_int(const void *p, const void *q) {
return *(int *)p > *(int *)q;
}
int cmp_double(const void *p, const void *q) {
return *(double *)p < *(double *)q;
}
int main() {
int arr1[5] = {3, 2, 5, 1, 4};
qsort(arr1, 5, sizeof(*arr1), cmp_int);
puts("arr1:");
for(int i = 0; i < sizeof(arr1) / sizeof(*arr1); i++)
printf("%d ", arr1[i]);
putchar('\n');
double arr2[5] = {1.2, 2.3, 3.4, 4.5, 5.6};
qsort(arr2, 5, sizeof(*arr2), cmp_double);
puts("arr2:");
for(int i = 0; i < sizeof(arr2) / sizeof(*arr2); i++)
printf("%.1lf ", arr2[i]);
putchar('\n');
return 0;
}
/*
out:
arr1:
1 2 3 4 5
arr2:
5.6 4.5 3.4 2.3 1.2
*/
在上面的代码中,使用qsort
函数对arr1
和arr2
分别进行升序排序和降序排序,而arr1
和arr2
分别是两个不同类型的数组。
此时,可以发现,如果不使用回调函数,就需要写出两个参数类型和排序方式都不同的排序函数,尽管这两个排序函数的算法是一样。所以,为了避免写重复的代码(如果从工程的角度来讲,原因更多),就可以借助回调函数的方式。
结语
在 C 语言中,回调函数的应用场景还是比较多的,但想要用好这个工具,必须要明白指针的概念。归根结底,回调函数还是在指针上玩花样。
另外,C++ 中的回调函数也很常见,比如 C++11 的 lambda 提供的匿名函数或者仿函数。
总而言之,回调函数是一种编程思想,是工程中的一种实践方法。