12. 回调函数

回调函数(Callback Function)是指通过函数指针保存函数的地址,在某个条件满足的时候再由其它函数调用的函数。

回调函数经常用于操作系统驱动,信号中断处理等调用函数者和函数实现者不同的场合。使用回调函数,可以在运行时改变处理的方式(动态调用不同的回调函数)。

示例:

比如有如下的函数 process_number_with,此函数传入一个数字和一个函数。我们要求此函数调用传入的函数 pfun 来处理此数字。

void process_number_with(int number, void(*pfun)(int)) {
    pfun(number);  // 调用回调函数来处理此数字。
}

下面我们给出三种处理数字的方法。

方法1:

使用英文提示打印此数字到控制台终端。

void print_number_with_english_hint(int n){
    printf("this number is %d.\n", n);
}

方法2:

使用中文提示打印此数字到控制台终端。

void print_number_with_chinese_hint(int n){
    printf("这个数是 %d。\n", n);
}

方法3:

将此数字保存通过网络发送给远程服务器

void send_number_to_server(int n){
    printf("正在发送数字 %d。\n", n);
    // ... 此处发送数据的代码省略。
}

我们在调用 process_number_with 函数时,函数的第二个参数传入上述三个函数之一,process_number_with 就会根据传入的函数调用相应的函数。我们称上述三个函数为 process_number_with 的回调函数。

如:

process_number_with(100, print_number_with_english_hint);
process_number_with(200, print_number_with_chinese_hint);
process_number_with(300, send_number_to_server);

示例代码:

// filename: callback_func.c
#include <stdio.h>

// 使用**英文**提示打印此数字到控制台终端。
void print_number_with_english_hint(int n){
    printf("this number is %d.\n", n);
}

// 使用**中文**提示打印此数字到控制台终端。

void print_number_with_chinese_hint(int n){
    printf("这个数是 %d。\n", n);
}

// 将此数字保存通过网络发送给远程服务器
void send_number_to_server(int n){
    printf("正在发送数字 %d。\n", n);
    // ... 此处发送数据的代码省略。
}

void process_number_with(int number, void(*pfun)(int)) {
    pfun(number);  // 调用回调函数来处理此数字。
}

int main(int argc, char * argv[]) {
    process_number_with(100, print_number_with_english_hint);
    process_number_with(200, print_number_with_chinese_hint);
    process_number_with(300, send_number_to_server);

    return 0;
}

编译和运行结果如下:

weimingze@mzstudio:~$ gcc -o callback_func callback_func.c
weimingze@mzstudio:~$ ./callback_func
this number is 100.
这个数是 200。
正在发送数字 300

使用回调函数的优点:

  1. 可以先定义函数的主要功能和框架,后使用回调函数来动态的完善或修改数据的处理行为。
  2. 可以实现调用者和功能实现者(回调函数部分)的解耦。
  3. 可以添加新的回调函数来扩展软件新的功能,实现可程序的可扩展性。

实验:

  1. 完成上述示例,为 process_number_with 再多添加几个回调函数然后来尝试调用。
  2. 思考 process_number_with 的回调函数的参数个数和类型是否可以不同?