回调函数的定义
对指针的应用是C语言的精髓之处,而回调函数就是C语言里对函数指针的高级应用,简而言之,回调函数是一个通过函数指针调用的函数。如果把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用它所指向的函数时,我们就说这个函数是回调函数。
既然聊到了回调函数,那么我们先来谈一谈函数指针和指针函数的概念吧。
函数指针和指针函数
(1)函数指针:顾名思义,函数指针就是指向函数的指针,定义为 数据类型(*funcPtr)(参数列表);例如,void(*funcPtr)(int*,int*)。
(2)指针函数:即返回值是指针的函数,定义为 数据类型* funcPtr(参数列表),例如:char* fucnPtr(int*,int*);即返回值为char*型指针。
在C语言中,变量有它的地址,同理函数也是有地址的,那么把函数的地址赋给函数指针,再通过函数指针调用这个函数就可以了。具体的操作步骤如下所示:
① 先定义一个函数指针,如:int(*pfuncPtr)(int*,int*);
② 再定义该函数指针指向的函数,如:“int funcPtr(int*,int*);”
③ 把函数的地址赋给函数指针,即“pfuncPtr = funcPtr“。
④ 通过函数指针去调用函数”(*pfuncPtr)(p,q);“,pfuncPtr 是函数的地址,那么*pfuncPtr当然就是函数本身了。
回调的用法
既然已经介绍过了回调函数和函数指针的定义,那么我们正式来学习一下回调函数的用法。
举个例子:你在整理你电脑里的文件,此时你先按照文件名来进行排序你的文件,只需要点击一下”按文件名排序“,系统就会给你一个按照文件名排序的文件夹,接着你又想另一个文件是按照文件大小来排序的,于是你又点击了一下”按照文件大小排序“,系统自然而然的又给你一个按照文件大小排序好的文件夹。那我们不禁思考:为什么计算机能够定制这么多相同类型的不同方式排序的功能呢?难道是系统内部本身就存在这么多函数?其实并不然,这些排序其实都是由一个函数功能实现的。那为什么一个函数能比较不同类型的数据类型呢?其实此处系统调用的这个函数就是回调函数。
回调在多文件编程,或者在调用我们自己写的库时,会极为便利,省去了许多编写重复代码的时间,同时也会减少内存的使用,会大大的提高我们的效率。
下面请看一段关于双向链表中快排的代码实例:
可能会有朋友看不太清此处的代码,因为在编辑时有水印遮挡了,此处的代码为定义一个数据类型为void*型,为什么要把数据类型定义为void*型呢?注意此处的void*不是空指针的有意思,而是万能指针的意思。万能指针在数据类型里,能够接收任何数据类型,但是void*型的指针转化为其他类型的指针时则需要进行强制类型转换,下面接着看代码:
上述的代码,如果读者还没有学到数据结构可能理解起来会比较困难,但是没关系,笔者只是举一个比较典型的回调函数运用实例,下面接着看:在快排函数FastSort函数中的最后一个参数为一个bool型的函数指针,在快排函数内部的循环中若funcPtr函数返回值若为true则继续执行括号内的语句,咋一听一看是不是比较懵逼,这个函数功能在哪实现的?我怎么知道它的返回值是否为true?
其实啊,回调的功能不在这里,笔者将它藏进了主函数中:
观察上述的三个函数再对比一下上面出现过的FastSort函数你会发现,在快排中调用的函数正是这些,或者说只要是bool类型参数为void*型的函数他都能调用,但是它调用这些函数做了些什么呢?仔细观察不难发现,在这三个函数中函数的功能都均为比较大小,不过他们之间比较的类型不同,此时若要对年龄进行比较则在main函数将排序函数中的回调函数参数改为根据年龄排序的函数: FastSort(list.head->next,NULL,SortByAge);,同理若要对学号进行排序调用学号排序函数即可,除此之外快排中数据比较大小的步骤也被我们的回调函数干了!
简而言之回调函数就是封装好一个函数,它的参数中包含一个函数指针指向一个函数,该函数的功能是什么样的完全由你来决定,同时也能为你省去不少麻烦事!
笔者可能对于回调的理解还不够深刻,这里还希望各位狠狠的补充一下意见!还望轻喷!还有示例代码中不少是自己封装的库,图方便就直接拿来用了,不懂需要了解的也请狠狠评论!狠狠批评指正!