函数指针的重要用途——回调函数
什么是回调函数?
粗暴的说,如果一个函数作为另一个函数的参数传入,这种函数就可以称为回调函数(这句话并不严谨,但为了说明问题可以这么理解)。C语言里面,一般就是一个函数的参数列表中有函数指针,函数指针指向的函数就是一个回调函数。
为什么要有回调函数?
那为什么不直接在函数体内调用,而非要把函数指针作为参数呢?
举一个例子:系统提供一个排序函数sort(int a[ ]),排序函数默认升序,但如果我们想要降序排列呢?那系统还要提供一个降序版本?
显然,系统可以提供这样一个接口sort(int a[ ], void(*p)(int*) ),后者是一个指向排序方法的函数指针。用户可以自行定义排序方法。即,我们可以认为,回调函数有以下的功能:
- 一个接口,可以实现不同的功能。这种思想不就是多态吗?本质上还是为了实现地址的晚绑定。C++中的仿函数,其实是对函数指针做了某种程度的简化,使用户使用更简单罢了,使用到的思想是一样的。
- 通过回调函数可以修改一个黑盒子内部默认的功能,这也是业务上经常用的。
Linux操作系统用到了大量回调的思想,比如对于不同厂家提供的驱动,厂家按照Linux提供的接口标准编写自己的方法,而Linux接口的参数列表中就有指向对应方法的函数指针,这样尽管厂家不同,Linux也可以使用。
再举一个例子,默认情况下,当我们按下CTRL + C时,进程会终止。那我们想改变这样的默认行为怎么办,linux提供了signal这样一个接口。
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
第一个参数是信号的编号,此处的ctrl + C对应的是2号信号,第二个参数就是一个函数指针,指向用户自己提供的方法。
如下面的这段代码:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void mysignal(int signo)
{
printf("catch a signal! %d\n", signo);
}
int main()
{
signal(2, mysignal);
while(1)
{
printf("hello world!\n");
sleep(1);
}
return 0;
}
这段代码中,使用signal函数注册了一个新的方法,当按下ctrl + C时,不再默认终止进程,而是输出catch a signal! 2。效果如下:
也就是说,系统提供接口用到了回调函数,用户定义该函数,实现了用户所需要的功能。这也是回调函数的主要应用方式。