c语言回调函数:C++回调函数使用方法

回调

我们经常在C设计时通过使用回调可以使有些应用(如定时器事件回调处理、用回调记录某操作进度等)变得非常方便和符合逻辑那么它内在机制如何呢如何定义呢?它和其它(比如钩子)有何区别呢?

使用回调实际上就是在某个(通常是API)时将自己(这个为回调)地址作为参数传递给那个

而 那个在需要时候利用传递地址回调这时你可以利用这个机会在回调中处理消息或完成操作至于如何定义回调跟具体使用 API有关般在帮助中有介绍说明回调参数和返回值等C般要求在回调前加CALLBACK(相当于FAR PASCAL)这主要是介绍说明该方式

至于钩子只是回调个特例习惯上把和SetWindowsHookEx起使用回调称为钩子也有人把利用VirtualQueryEx安装称为钩子不过这种叫法不太流行

也可以这样更容易理解:回调就好像是个中断处理系统在符合你设定条件时自动为此你需要做 3件事:

1. 声明;

2. 定义;

3. 设置触发条件就是在你中把你回调名称转化为地址作为个参数以便于系统

声明和定义时应注意:回调由系统所以可以认为它属于WINDOWS系统不要把它当作你某个类成员

2回调、消息和事件例程


(calling)机制从汇编时代起已经大量使用:准备段现成代码者可以随时跳转至此段代码起始地址执行完后再返回跳转时后续地址 CPU为此准备了现成指令时可以压栈保护现场结束后从堆栈中弹出现场地址以便自动返回借堆栈保护现场真是项绝妙发明它使 者和被调者可以互不相识于是才有了后来和构件

机制并非完美回调就是的类本是为者准备美餐其烹制者应对食客了如指掌但实情并非如此例如个快速排序供他人调 用其中必包含比较大小麻烦来了:此时并不知要比较是何类数据--整数、浮点数、串?于是只好为每类数据制作个区别排序更通行办法是 在参数中列个回调地址并通知者:君需自己准备个比较其中包含两个指针类参数要比较此 2指针所指数据的大小并由返回值 介绍说明比较结果排序借此者提供来比较大小借指针传递参数可以全然不管所比较数据类型者回头(够咬嘴)故 称其为回调(callback)

回调使结构乱了许多Windows API 集中有不少回调尽管有详尽介绍说明仍使初学者头雾水恐怕这也是无奈的举

无论何种事物能以树形结构单向描述毕竟让人舒服些如果某家族中孙辈又是某祖辈祖辈恐怕无人能理清其中头绪但数据处理的复杂往往需要构成网状结构非简单客户/服务器关系能穷尽

Windows 系统还包含着另种更为广泛回调机制即消息机制消息本是 Windows 基本控制手段乍看和无关其实是种变相发送消息是通知收方运行段预先准备好代码相当于消息所附带 WParam 和 LParam 相当于参数只不过比普通参数更通用应用可以主动发送消息更多情况下是坐等 Windows 发送消息旦消息进入所属消息队列便检感兴趣那些跳转去执行相应消息处理代码操作系统本是为应用服务由应用而应用旦 启动却要反过来等待操作系统这分明也是种回调或者说是种广义回调其实应用的间也可以形成这种回调假如进程 B 收到进程 A 发来消息启动了段代码其中又向进程 A 发送消息这就形成了回调这种回调比较隐蔽弄不好会搞成递归若缺少终止条件将会循环不已直至把搞垮若是故意编写成此递归并设好 终止条件倒是很有意思但这种结构太隐蔽除非十分必要还是不用为好

利用消息也可以构成狭义回调上面所举排序可以把回调地址换成窗口 handle如此当需要比较数据大小时不是去回调而是借 API SendMessage 向指定窗口发送消息收到消息方负责比较数据大小把比较结果通过消息本身返回值传给消息发送方所实现功能和回调并无区别当然此例中改为消 息纯属画蛇添脚反倒把搞得很慢但其他情况下并非总是如此特别是需要异步发送消息是种不错选择假如回调中包含文件处理的类低 速处理方等不得需要把同步改为异步去启动个单独线程然后马上执行后续代码其余事让线程慢慢去做个替代办法是借 API PostMessage 发送个异步消息然后立即执行后续代码这要比自己搞个线程省事许多而且更安全

如今我们是活在个 object 时代只要和编程有关无论何事都离不开 object但 object 并未消除回调反而把它发扬光大弄得到处都是只不过大都以事件(event)身份出现镶嵌在某个结构的中显得更正统更容易被人接受应用 要使用某个构件总要先弄清构件属性、思路方法和事件然后给构件属性赋值在适当时候适当构件思路方法还要给事件编写处理例程以备构件代码来调 用何谓事件?它不过是个指向事件例程地址和回调地址没什么区别

不过此种回调方式比传统回调要高明许多首先它把让人不太舒服回调变成种自然而然处理例程使编程者顿觉气顺再者地址是个危险 东西用好了可使加速用不好处处是陷阱随时都会崩溃现代编程方式总是想法把地址隐藏起来(隐藏比较彻底如 VB 和 Java)其代价是降低了效率事件例程(?)使编程者无需直接操作地址但并不会使减速
(例程似乎是进程台湾翻译)

3精妙比喻:回调还真有点像您随身带BP机:告诉别人号码在它有事情时Call您

于层间协作上层将本层安装在下层这个就是回调而下层在定条件下触发回调例如作为个驱动个底层他在收到个数据时除了 完成本层处理工作外还将进行回调将这个数据交给上层应用层来做进步处理这在分层数据通信中很普遍其实回调和API非常接近他们共性都是 跨层但区别是API是低层提供给高层般这个对高层都是已知;而回调正好相反他是高层提供给底层对于低层他是未知 必须由高层进行安装这个安装其实就是个低层提供API安装后低层不知道这个回调名字但它通过指针来保存这个回调在需要只需引用这个指针和相关参数指针 其实:回调就是该写在高层低层通过指针保存这个在某个事件触发下低层通过该指针高层那个

4方式
软件Software模块的间总是存在着接口方式上可以把他们分为 3类:同步、回调和异步同步种阻塞式方要等待对方执行完毕 才返回它是种单向;回调是种双向模式也就是说方在接口被时也会对方接口;异步种类似消息或事件机制不过它 方向刚好相反接口服务在收到某种讯息或发生某种事件时会主动通知客户方(即客户方接口)回调和异步关系非常紧密通常我们使用 回调来实现异步消息注册通过异步来实现消息通知同步是 3者当中最简单而回调又常常是异步基础

对于区别类型语言(如结构化语言和对象语言)、平台(Win32、JDK)或构架(CORBA、DCOM、WebService)客户和服务交互除 了同步方式以外都需要具备异步通知机制让服务方(或接口提供方)在某些情况下能够主动通知客户而回调是实现异步个最简捷途径

对于结构化语言可以通过回调来实现回调回调也是或过程不过它是个由方自己实现供被方使用特殊

在面向对象语言中回调则是通过接口或抽象类来实现我们把实现这种接口类成为回调类回调类对象成为回调对象对于象C或Object Pascal这些兼容了过程特性对象语言不仅提供了回调对象、回调思路方法等特性也能兼容过程语言回调机制

Windows平台消息机制也可以看作是回调种应用我们通过系统提供接口注册消息处理(即回调)从而实现接收、处理消息由于Windows平台API是用C语言来构建我们可以认为它也是回调个特例

对于分布式组件代理体系CORBA异步处理有多种方式如回调、事件服务、通知服务等事件服务和通知服务是CORBA用来处理异步消息标准服务他们主要负责消息处理、派发、维护等工作些简单异步处理过程我们可以通过回调机制来实现

下面我们集中比较具有代表性语言(C、Object Pascal)和架构(CORBA)来分析回调实现方式、具体作用等

过程语言中回调(C)
(1 )指针
回调在C语言中是通过指针来实现,通过将回调地址传给被调从而实现回调因此要实现回调必须首先定义指针请看下面例子:

void Func(char *s);// 原型
void (*pFunc) (char *);//指针

可以看出定义和指针定义非常类似

为了简化指针类型变量定义提高可读性我们需要把指针类型自定义

typedef void(*pcb)(char *);

回调可以象普通样被但是只兴 坏弊鞑问 莞 坏骱 辈拍艹谱骰氐骱 ?

被调例子:

void GetCallBack(pcb callback)
{
/*do something*/
}
用户在上面需要自己实现个pcb类型回调:
void fCallback(char *s)
{
/* do something */
}
然后就可以直接把fCallback当作个变量传递给GetCallBack,
GetCallBack(fCallback);

如果赋了区别值给该参数那么者将区别地址赋值可以发生在运行时这样使你能实现动态绑定

(2 )参数传递规则
到目前为止我们只讨论了指针及回调而没有去注意ANSI C/C编译器规范标准许多编译器有几种规范标准如在Visual C可以在类型前加_cdecl_stdcall或者_pascal来表示其规范标准(默认为_cdecl)C Builder也支持_fastcall规范标准规范标准影响编译器产生给定参数传递顺序(从右到左或从左到右)堆栈清理责任(者或 者被者)以及参数传递机制(堆栈CPU寄存器等)

规范标准看成是类型部分是很重要;不能用不兼容规范标准将地址赋值给指针例如:

// 被是以为参数为返回值
__stdcall callee();

// 指针为参数
void caller( __cdecl (*ptr)());

// 在p中企图存储被地址非法操作
__cdecl (*p)() = callee; // 出错

指针p和callee类型不兼容它们有区别规范标准因此不能将被地址赋值给指针p尽管两者有相同返回值和参数列

(3 )应用举例
C语言标准库中很多地方就采用了回调来让用户定制处理过程如常用快速排序、 2分搜索

快速排序原型:

void qsort(void *base, size_t nelem, size_t width, (_USERENTRY *fcmp)(const void *, const void *));
2分搜索原型:
void *bsearch(const void *key, const void *base, size_t nelem,
size_t width, (_USERENTRY *fcmp)(const void *, const void *));

其中fcmp就是个回调变量

下面给出个具体例子:

# <stdio.h>
# <stdlib.h>

sort_function( const void *a, const void *b);
list[5] = { 54, 21, 11, 67, 22 };

(void)
{
x;

qsort((void *)list, 5, (list[0]), sort_function);
for (x = 0; x < 5; x)
prf("%i\n", list[x]);
0;
}

sort_function( const void *a, const void *b)
{
*(*)a-*(*)b;
}
Tags:  if函数的用法 vlookup函数的用法 回调函数 c语言回调函数

延伸阅读

最新评论

发表评论