博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言 回调函数
阅读量:4050 次
发布时间:2019-05-25

本文共 3959 字,大约阅读时间需要 13 分钟。

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简言之,回调函数就是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用指向的函数时,我们就说这个函数是回调函数。

在高级语言中,回调函数也就是观察者模式的一种应用。本质上都是“你想让别人的代码执行你的代码,而别人的代码你又不能动”这种需求下产生的。

很文艺的解释:

1.告诉我一些关于你的事情,在需要的时候我能够找到你。

2.把电话号码留下,我Call(回调)你
3.诸葛亮给赵子龙一个锦囊,吩咐他危急时打开按锦囊指示办,锦囊里的命令就是回调函数,危急时刻就是回调的时机,不同的锦囊里可以有不同的命令。

通俗一些的解释:

我们对回调函数的使用无非是对函数指针的应用,函数指针的概念本身很简单,但是把函数指针应用于回调函数就体现了一种解决问题的策略,一种设计系统的思想。

那么什么是回调函数呢,那是不得以而为之的设计策略,想象一种系统实现:在一个下载系统中有一个文件下载模块和一个下载文件当前进度显示模块,系统要求实时的显示文件的下载进度,想想很简单在面向对象的世界里无非是实现两个类而已。但是问题恰恰出在这里,显示模块如何驱动下载进度条?显示模块不知道也不应 该知道下载模块所知道的文件下载进度(面向对象设计的封装性,模块间要解耦,模块内要内聚),文件下载进度是只有下载模块才知道的事情,解决方案很简单给下载模块传递一个函数指针作为回调函数驱动显示模块的显示进度。

比如打印一堆数据,我只管要打印的数据,至于怎么打印,我不管,回调打印函数就可以了。

在面向对象的世界中这样的例子还真不少,造成这样的问题的根源,相信大家已经从上面的叙述中体会到了,就是面向对象的程序设计思想,就是设计模式中要求的模块独立性,高内聚低耦合等特性。

封装变化的编程策略给编程人员第一位的指导思想就是面向接口编程,即设计模式中提到的面向虚拟编程而不是面向实现。这样的编程思想极大地革新了编程世界,可以说没有这一原则就没有面向对象的程序设计,这一原则给程序设计一种指导思想: 即如何更高的将现实模型映射成程序模型。这样的设计思想在极大地催生高 度独立性模块的同时削弱了模块间的协作性,也就是耦合性,它使得模块间更多的从事着单向的调用工作,一个模块需要某种服务就去找另一个模块,这使得程序呈 现出层次性,高层通过接口调用底层,底层提供服务。但是现实世界中严格遵循现层次特性的系统是很少见的,绝对的MVC是不存在的,因为更多的模块要求通并 协作,可见没有耦合就没有协作没有好的调用关系,耦合真的不是错。

既然我们需要模块间的协作,同时我们又厌恶的摒弃模块间你中有我我中有你的暧昧关系那如何生成系统呢,答案是函数指针(不一定一定是函数指针)也就是使用 回调的方式。如果一个对象关心另一个对象的状态变化那么给状态的变化注册回调函数让它通知你这类状态的改变,这样在封装了模块变化的同时实现了模块间的协作关系另辟独径的给对象解耦。

例子1:

#include 
void PrintNum(int n);void ShowNum(int n,void (* ptr)());void PrintMessage1();void PrintMessage2();void PrintMessage3();void ShowMessage(void (* ptr)());int main(){ ShowNum(11111,PrintNum); ShowNum(22222,PrintNum); ShowMessage(PrintMessage1); ShowMessage(PrintMessage2); ShowMessage(PrintMessage3);}void PrintNum(int n){ printf("Test1 is called,the number is %d\n",n);}void ShowNum(int n,void (* ptr)()){ (* ptr)(n);}void PrintMessage1(){ printf("This is the message 1!\n");}void PrintMessage2(){ printf("This is the message 2!\n");}void PrintMessage3(){ printf("This is the message 3!\n");}void ShowMessage(void (* ptr)()){ (* ptr)();}

输出为:

Test1 is called,the number is 11111

Test1 is called,the number is 22222
This is the message 1!
This is the message 2!
This is the message 3!

例子2:

#include
// 方法指针的格式为:int (*ptr)(char *p) 即:返回值(指针名)(参数列表) typedef int (*CallBackFun)(char *p); // 为回调函数命名,类型命名为 CallBackFun,参数为char *p // 方法 Afun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun int Afun(char *p) { printf("Afun 回调打印出字符%s!\n", p); return 0; } // 方法 Bfun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun int Cfun(char *p) { printf("Cfun 回调打印:%s, Nice to meet you!\n", p); return 0; } // 执行回调函数,方式一:通过命名方式 int call(CallBackFun pCallBack, char *p) { printf("call 直接打印出字符%s!\n", p); pCallBack(p); return 0; } // int call2(char *p, int (*ptr)(char *p)) // 执行回调函数,方式二:直接通过方法指针 int call2(char *p, int (*ptr)()) { printf("=======%s=======\n",p); (*ptr)(p); } // 执行回调函数,方式三:通过命名方式 int call3(char *p, CallBackFun pCallBack) { printf("------%s--------\n", p); pCallBack(p); } int main() { char *p = "hello"; call(Afun, p); call(Cfun, p); call2(p, Afun); call2(p, Cfun); call3(p, Afun); call3(p, Cfun); return 0; }

输出为:

call 直接打印出字符hello!

Afun 回调打印出字符hello!
call 直接打印出字符hello!
Cfun 回调打印:hello, Nice to meet you!
=======hello=======
Afun 回调打印出字符hello!
=======hello=======
Cfun 回调打印:hello, Nice to meet you!
——hello——–
Afun 回调打印出字符hello!
——hello——–
Cfun 回调打印:hello, Nice to meet you!

例子3:

#include 
#include
int Test1(){ int i; for(i=0; i<30; i++) { printf("The %d th charactor is: %c\n", i, (char)('a' + i%26)); } return 0;}int Test2(int num){ int i; for (i=0; i

例子参考文章:

1. C语言回调函数一个简单的例子
2. 一个c回调函数的例子
3.c语言之回调函数

当我们想通过一个统一的接口实现不同的内容时,用回调函数就比较合适。

任何时候,如果所编写的函数必须在不同的时刻执行不同类型的工作或者执行由函数调用者定义的工作,我们就可以用回调函数实现。

许多窗口系统就是使用多个回调函数连接动作,如拖拽鼠标和点击按钮来指定调用特定的函数完成工作。

转载地址:http://vsnci.baihongyu.com/

你可能感兴趣的文章
linux 驱动开发 头文件
查看>>
container_of()传入结构体中的成员,返回该结构体的首地址
查看>>
ipconfig,ifconfig,iwconfig
查看>>
opensuse12.2 PL2303 minicom
查看>>
网络视频服务器移植
查看>>
Encoding Schemes
查看>>
移植QT
查看>>
如此调用
查看>>
计算机的发展史
查看>>
带WiringPi库的交叉编译如何处理一
查看>>
带WiringPi库的交叉笔译如何处理二之软链接概念
查看>>
Spring事务的七种传播行为
查看>>
ES写入找不到主节点问题排查
查看>>
Java8 HashMap集合解析
查看>>
欢迎使用CSDN-markdown编辑器
查看>>
Android计算器实现源码分析
查看>>
Android系统构架
查看>>
Android 跨应用程序访问窗口知识点总结
查看>>
各种排序算法的分析及java实现
查看>>
SSH框架总结(框架分析+环境搭建+实例源码下载)
查看>>