回调函数

在C语言编程中,我们经常听到回调函数(callback function)一说。对于C语言新手而言,回调函数可能是一个比较难于理解的东西。今天我将通过一个实际例子,来讲解什么是回调函数,为什么要用回调函数。

 

程序任务:

写一段程序,实现对一个整型数据的排序功能,可以递增排序,也可以递减排序。

 

上述任务的编程并不难,对于排序而言,我们至少会使用冒泡法。于是,我相信很多同学会写出这样的代码:

 

#include <stdio.h>

 

//升序排序

int sort_asc(int nums[], int cnt)

{

         inti;

         intj;

        

         for(i = 0; i < cnt - 1; i++)

         {

                   intm = i;

                  

                   //查找最小的数

                   for(j = i + 1; j < cnt; j++)

                   {

                            if(nums[j] < nums[m])

                            {

                                     m= j;

                            }

                   }

                  

                   //交换

                   if(m != i)

                   {

                            intt = nums[m];

                            nums[m]= nums[i];

                            nums[i]= t;

                   }

         }

        

         return0;

}

//降序排序

int sort_dec(int nums[], int cnt)

{

         inti;

         intj;

        

         for(i = 0; i < cnt - 1; i++)

         {

                   intm = i;

                  

                   //查找最大的数

                   for(j = i + 1; j < cnt; j++)

                   {

                            if(nums[j] > nums[m])

                            {

                                     m= j;

                            }

                   }

                  

                   //交换

                   if(m != i)

                   {

                            intt = nums[m];

                            nums[m]= nums[i];

                            nums[i]= t;

                   }

         }

        

         return0;

}

 

void print_array(int num[], int cnt)

{

         inti;

 

         for(i = 0; i < cnt; i++)

         {

                   printf("%d", num[i]);

         }

         printf("\n");

                  

}

 

int main(int argc, char *argv[])

{

         inti;

         intnum[10] = {2, 3, 1, 3, 9, 12, 34, 8, 19, 0};

        

         sort_asc(num,10);

         print_array(num,10);

 

         sort_dec(num,10);

         print_array(num,10);

        

         return0;

}

 

在上面的例子中,我们实现了两个排序函数:sort_asc()与sort_dec(),分别用于升序与降序。不难发现,这两个函数非常相像,差别只在于一个判断符。这意味着我们写了冗余代码。十几年的编程生涯已经无数次非常清晰地告诉过我:冗余代码就负担,是垃圾。也许已经有人想到了消除冗余代码的方法:把两个函数合并成一个,函数后边加一个参数用于标识是升序还是降序,然后程序里有差异的地方用这个标识来走分支。老实说,这是一个非常不错的方法,但并不优雅,因为加上的分支条件会使们的程序变复杂。

 

那更好的方法是什么呢?答案是有很多,但是今天讲的是回调函数,那回调函数就要登场了。我们来看改进后的代码:

 

#include <stdio.h>

 

int sort(int nums[], int cnt, int(*cmp)(int, int))

{

         inti;

         intj;

        

         for(i = 0; i < cnt - 1; i++)

         {

                   intm = i;

                  

                   //查找最小的数

                   for(j = i + 1; j < cnt; j++)

                   {

                            if(cmp(nums[j], nums[m]) < 0)

                            {

                                     m= j;

                            }

                   }

                  

                   //交换

                   if(m != i)

                   {

                            intt = nums[m];

                            nums[m]= nums[i];

                            nums[i]= t;

                   }

         }

        

         return0;

}

 

 

 

void print_array(int num[], int cnt)

{

         inti;

 

         for(i = 0; i < cnt; i++)

         {

                   printf("%d", num[i]);

         }

         printf("\n");

                  

}

 

int num_cmp_asc(int a, int b)

{

         returna - b;

}

 

int num_cmp_dec(int a, int b)

{

         returnb - a;

}

 

int main(int argc, char *argv[])

{

         inti;

         intnum[10] = {2, 3, 1, 3, 9, 12, 34, 8, 19, 0};

        

         sort(num, 10, num_cmp_asc);

         print_array(num,10);

 

         sort(num, 10, num_cmp_dec);

         print_array(num,10);

        

         return0;

}

 

耐心的同志或者已经看来,改进后的代码只有一个sort函数了,但后边多了一个函数指针参数cmp,这正是所谓的回调函数。还sort函数的处理过程中,不再直接比较两个数值的大小,而是调用cmp来获取结果。在sort函数的外边,我们新增了两个整数比较函数:

num_cmp_asc()与num_cmp_dec(),在主函数调用排序函数sort的时候,它们当作回调函数传给了sort()。这样sort就无需了解整数大小比较这个细节,回调函数告诉它结果是大就是大,是小就是小,它只需安安心心按照cmp告诉的比较结果来做自认为的升序排序,虽然num_cmp_dec()回调欺骗了它。

 

正是因为这样小小的欺骗,我们非常巧妙地实现了用单一个的sort逻辑,实现了升序与降序两种排序功能:sort按升序排列,main函数告诉你一个相反的整数大小判读逻辑num_cmp_dec(),结果就发生了逆转。

 

改进后的代码,没有了冗余代码,代码数量少了一些,逻辑也清晰不少。用尽可能少的代码、尽可能清晰的逻辑,写出预定功能的代码,这是编程的最高境界

 

通过上面的例子,我们对回调函数作出如下总结:

 

1 C语言中,回调函数说白了就是以函数指针的方式来调用函数。

 

2我们将函数a以指针f的形式传给函数b,函数b中调用f(),就是b回调了a。

 

3回调函数屏蔽了一些底层细节(如示例中整数的大小比较),让回调函数调用者(如示例中的sort)只需关注自己的业务,而不用操心无关的内容,使自身逻辑变得简单、独立。

 

4 回调函数的存在,实现了函数的“多态”,相同的逻辑(本列中的冒泡排序算法)可以解决同一类的问题(本例中的升序与降序)。

 

在实际编程中,我们使用回调,更多是想达成上述第3条功能:屏蔽某些底层细节,实现通用模块的独立。某些时候,我们的通用模块(如排序功能)要调用外部的接口(如整数比较)实现某些运算,而这些外接接口尚未知,就算已知,为保证模块的独立性,也不能引用其头文件更不能链接其共享库,这时候回调函数是个最好的方案:我给你定义一个接口形式,你接照要求的形式实现,传给我,我再调你。

 

扩展任务:

写一个函数,实现对字符串、整数、浮点等数组的排序功能。

参考:C库的sort函数实现。

 

通过本文,相信大家对回调函数是什么,有什么用,如何使用有了进一步的理解。





另一转载++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

callback.c

/*
 * @file c语言实现回调函数
 * @detial 在java等更高级的语言中往往已经给我们封装好了回调函数的调用方式,直接用就可以了。
 * 而C语言中并没有这种直接可以操作的回调方式,我们用函数指针来实现回调原理。
 */
#include<stdio.h>

// 将函数名作为指针的格式为:int (*ptr)(char *p) 即:返回值(指针名)(参数列表)
typedef int (*callback)(char *str); // 回调函数的名称为 callback,参数是char *p

// functionA的格式符合 callback 的格式,因此可以看作是一个 callback类型
int functionA(char *str)
{
    printf("回调 functionA(char *str) 函数:%s!\n", str);
    return 0;
}

// functionB的格式符合 callback 的格式,因此也可以看作是一个 callback类型
int functionB(char *str)
{
    printf("回调 functionB(char *str) 函数:%s!\n", str);
    return 0;
}



// 调用回调函数,方式一:通过命名方式
int test1(callback p_callback, char *str)
{
    printf("test1:\n不调用回调函数打印:%s!\n", str);
    p_callback(str);
    return 0;
}

// 调用回调函数,方式二:直接通过函数指针
int test2(int (*ptr)(), char *str)
{
    printf("test2:\n不调用回调函数打印:%s!\n", str);
    (*ptr)(str);
}

int main()
{
    char *str = "hello world!";

    test1(functionA, str);
    test1(functionB, str);
    test2(functionA, str);
    test2(functionB, str);

    printf("test3:\n");
    callback test3 = functionB;
    test3(str);

    return 0;
}


 
 
  • 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
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 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
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

运行结果 
[root@centos6 data]# gcc callback.c 
[root@centos6 data]# ./a.out 
test1: 
不调用回调函数打印:hello world!! 
回调 functionA(char *str) 函数:hello world!! 
test1: 
不调用回调函数打印:hello world!! 
回调 functionB(char *str) 函数:hello world!! 
test2: 
不调用回调函数打印:hello world!! 
回调 functionA(char *str) 函数:hello world!! 
test2: 
不调用回调函数打印:hello world!! 
回调 functionB(char *str) 函数:hello world!! 
test3: 
回调 functionB(char *str) 函数:hello world!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值