回调函数

  回调函数:顾名思意,就是使用者自己定义一个函数,使用者自己定义这个函数的功能,然后把 这个函数作为参数 传入到 其他函数 中,由  其他函数 在运行时来调用的函数。该“其他函数”就是回调函数(特点:它 主动调用 别的函数)

  什么是回调函数呢?回调函数其实就是一个通过函数指针调用的函 数!假如你把A函数的指针当作参数传给B函数,然后在B函数中通过A函数传进来的这个指针调用A函数,那么这就是回调机制。B函数就是回调函数,而通常情 况下,A函数是系统在符合你设定条件的情况下会自动执行,比如Windows下的消息触发等等。那么调用者和被调用者的关系就被拉开了,就像是中断处理函 数那样。

   简单的说:由 其他的函数 运行期间来回调 你定义的函数;(这两个函数 被捆绑在一起 运行)

 1 //不带参数版
 2 #include <stdio.h>
 3 
 4 void func()    //定义回调函数的调用函数;
 5 {
 6     printf("hello world !! \n");
 7 }
 8 
 9 void mycall_back(void(*call_back_test)()) //实现回调函数
10 {
11     call_back_test();
12 }
13 
14 int main()
15 {
16     mycall_back(func);
17     return 0;
18 }
//回调函数带参数版
1
#include <stdio.h> 2 3 void func(int t) 4 { 5 printf("hello world !! %d\n", ++t); 6 } 7 //void mycall_back((*call_back_test) (int), int k) //error; 8 void mycall_back(void (*call_back_test) (int), int k)   //注意参数写法 9 { 10 call_back_test(k); 11 } 12 13 int main() 14 { 15 mycall_back(func, 8);  //注意参数写法; 16 return 0; 17 }

下面给出回调函数的标准定义:

  简而言之,回调函数就是一个 通过函数指针调用的 函数。如果把 函数的指针(地址)作为参数传递给另一个函数, 当这个指针 被作 调用它指向的函数时,我们就说 这个函数是回调函数;

  为什么要使用回调函数?

  因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

   如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排 序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、 float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。

  回调可用于通知机制,例如,有时要在程序中设置一个 计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调, 来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。

  另一个使用回调机制的 API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个 值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为 基于返回值,它将继续执行或退出。

示例一个:

 1 #include <stdio.h>
 2 
 3 typedef void (*callback_t)(void *);
 4 
 5 void repeat_three_times(callback_t f, void *para)
 6 {
 7     f(para);
 8 }
 9 
10 void say_hello(void *str)
11 {
12     printf("Hello %s\n", (const char *)str);
13 } 
14 
15 void count_numbers(void *num)
16 {
17     int i;
18     for(i=1;i<=(int)num;i++)
19         printf("%d ", i);
20     putchar('\n');
21 }
22 
23 int main()
24 {
25      repeat_three_times(say_hello, "Guys");
26      repeat_three_times(count_numbers,  (void *)4);
27      return 0;
28 }

 示例二

 1 #include <stdio.h>
 2 typedef int (cmp_t)(void *a, void *b);
 3 
 4 typedef struct
 5 {
 6     const char *name;
 7     int score;
 8 }student_t;
 9 
10 int cmp_student(void *a, void *b) 
11 {
12     if(((student_t *)a)->score > ((student_t *)b)->score)
13         return 1;
14     if(((student_t*)a)->score == ((student_t *)b)->score)
15         return 0;
16     else
17         return -1; 
18 
19     return 0;
20 }
21 
22 void *max(void *data[], int num, cmp_t cmp)
23 {
24     int i;
25     void *tmp = data[0];
26     for(i=1; i<num; i++)
27     {   
28         if(cmp(tmp, data[i]) < 0)
29             tmp = data[i];
30     }   
31     return tmp;
32 }
33 
34 int main()
35 {
36     student_t list[4] = {{"Tom", 68}, {"Jerry", 72}, {"John", "87"}, {"Chris", 99}};
37     student_t *plist[4] = {&list[0], &list[1], &list[2], &list[3]};
38     student_t *pmax = max((void**)plist, 4, cmp_student);
39     printf("%s gets the highest score %d\n", pmax->name, pmax->score);
40 
41     return 0;
42 }

  max函数之所以能对一组任意类型的对象进行操作,关键在于传给max的是指向对象的指针所构成的数组,而不是对象本身所构成的数组,这样max不必关心对象到底是什么类型,只需要转给比较函数cmp,然后根据比较结果做对应操作即可,cmp是调用者提供的回调函数,调用者就知道对象是什么类型以及如何比较;

以上举例的回调函数是被同步调用的,调用者调用max函数,max函数则调用cmp函数,相当于调用者间接调了自己提供的回调函数。在实际系统中,异步调用也是回调函数的一种典型用法,调用者首先将回调函数传给实现者,实现者记住这个函数,这称为注册一个回调函数,然后当某个事件发生时实现者再调用先前注册的函数,比如sigaction(2)注册一个信号处理函数,当信号产生时由系统调用该函数进行处理,再比如pthread_create(3)注册一个线程函数,当发生调度时系统切换到新注册的线程函数中运行,在GUI编程中异步回调函数更是有普遍的应用,例如为某个按钮注册一个回调函数,当用户点击按钮时调用它。

上一篇:Linux系统源码安装过程中的prefix选项


下一篇:linux线程之pthread_join和pthread_detach