标题:什么是回调函数那
只看楼主
luckbird
Rank: 1
等 级:新手上路
帖 子:30
专家分:0
注 册:2005-3-21
 问题点数:0 回复次数:9 
什么是回调函数那
<BR>vc中遇到 int WINAPI WndProc();<BR>  LRESULT CALLBACK WndProc();<BR>就感到迷茫,在c/c++中没见过 中间的 WINAPI 和CALLBACK <BR>这2个分别是做什么的啊?听说过CALLBACK 是回调函数究竟是如何回调,如何执行的那/<BR>期待详细讲解
搜索更多相关主题的帖子: 回调函数 CALLBACK WINAPI WndProc int 
2006-03-22 21:39
txw8623595
Rank: 1
等 级:新手上路
帖 子:13
专家分:0
注 册:2006-3-2
得分:0 
回调函数就是由程序员设计但是由系统调用的函数
2006-03-22 22:13
okey
Rank: 1
等 级:新手上路
帖 子:27
专家分:0
注 册:2006-2-12
得分:0 

这个概念是知道的,但是不知道关于细节它是怎么实现的,哪位英雄儿女能指点指点一下吗?


when i first saw you , i saw love . and the first time you touch me , i feel love.〆〆
2006-03-23 02:30
makebest
Rank: 8Rank: 8
等 级:蝙蝠侠
威 望:3
帖 子:658
专家分:962
注 册:2005-3-17
得分:0 

调用函数 A 时,它需要一个回调函数指针 B,而且规定了 B 的参数和返回值,
B 一般都需要手工写,A 在必要的时候自动调用 B 函数,并根据 B 的返回值确定如何操作。

2006-03-23 12:37
luckbird
Rank: 1
等 级:新手上路
帖 子:30
专家分:0
注 册:2005-3-21
得分:0 
还是不太理解 呼唤达人给详细点的解释

2006-03-25 19:38
幽园香客
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:231
专家分:142
注 册:2006-2-27
得分:0 
转贴:

先泛后精,先浅后深”的道理可是知易行难,所以常常会给技术上的小细节纠缠着,搞得晕晕乎,其中“回调函数”就是其中之一了,虽然回用,但是老是不明白其中的含义,众多的书本也说得隐晦不清。直到不久前看到一篇文章才总算比较清晰了解。

概括起来,回调机制包括两部分:服务执行者和服务方式制定者。
1. 服务执行者先制定服务规范;
2.服务方式制定者然后按照规范制定服务方式;
3.然后执行者按照这个方式提供服务。
回调函数的方式是把函数指针的作为参数传递进去,所以规范就是约定函数的参数类型,个数。

这篇文章如下:
调用(calling)机制从汇编时代起已经大量使用:准备一段现成的代码,调用者可以随时跳转至此段代码的起始地址,执行完后再返回跳转时的后续地址。 CPU为此准备了现成的调用指令,调用时可以压栈保护现场,调用结束后从堆栈中弹出现场地址,以便自动返回。借堆栈保护现场真是一项绝妙的发明,它使调用 者和被调者可以互不相识,于是才有了后来的函数和构件,使吾辈编程者如此轻松愉快。若评选对人类影响最大之发明,在火与车轮之后,笔者当推压栈调用。
话虽这样说,此调用机制并非完美。回调函数就是一例。函数之类本是为调用者准备的美餐,其烹制者应对食客了如指掌,但实情并非如此。例如,写一个快速排序 函数供他人调用,其中必包含比较大小。麻烦来了:此时并不知要比较的是何类数据--整 数、浮点数、字符串?于是只好为每类数据制作一个不同的排序函数。更 通行的办法是在函数参数中列一个回调函数地址,并通知调用者:君需自己准备一个比较函数,其中包含两个指针类参数,函数要比较此二指针所指数据之大小,并 由函数返回值说明比较结果。排序函数借此调用者提供的函数来比较大小,借指针传递参数,可以全然不管所比较的数据类型。被调用者回头调用调用者的函数(够 咬嘴的),故称其为回调(callback)。
回调函数使程序结构乱了许多。Windows API 函数集中有不少回调函数,尽管有详尽说明,仍使初学者一头雾水。恐怕这也是无奈之举。无论何种事物,能以树形结构单向描述毕竟让人舒服些。如果某家族中孙 辈又是某祖辈的祖辈,恐怕无人能理清其中的头绪。但数据处理之复杂往往需要构成网状结构,非简单的客户/服务器关系能穷尽。
Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,乍看与函数调用无关,其实是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,只不过比普通参数更通用一些。应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦 启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。若是故意编写成此递归调用,并设好 终止条件,倒是很有意思。但这种程序结构太隐蔽,除非十分必要,还是不用为好。
利用消息也可以构成狭义回调。上面所举排序函数一例,可以把回调函数地址换成窗口handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向 指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然,此例中改为消 息纯属画蛇添脚,反倒把程序搞得很慢。但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。假如回调函数中包含文件处理之类的低 速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。
如今我们是活在一个 object 时代。只要与编程有关,无论何事都离不开 object。但 object 并未消除回调,反而把它发扬光大,弄得到处都是,只不过大都以事件(event)的身份出现,镶嵌在某个结构之中,显得更正统,更容易被人接受。应用程序 要使用某个构件,总要先弄清构件的属性、方法和事件,然后给构件属性赋值,在适当的时候调用适当的构件方法,还要给事件编写处理例程,以备构件代码来调 用。何谓事件?它不过是一个指向事件例程的地址,与回调函数地址没什么区别。
不过,此种回调方式比传统回调函数要高明许多。首先,它把让人不太舒服的回调函数变成一种自然而然的处理例程,使编程者顿觉气顺。再者,地址是一个危险的 东西,用好了可使程序加速,用不好处处是陷阱,程序随时都会崩溃。现代编程方式总是想法把地址隐藏起来(隐藏比较彻底的如 VB 和 Java),其代价是降低了程序效率。事件例程使编程者无需直接操作地址,但并不会使程序减速。更妙的是,此一改变,本是有损程序结构之奇技怪巧变成一种 崭新设计理念,不仅免去被人抨击,而且逼得吾等凡人净手更衣,细细研读,仰慕至今。只是偶然静心思虑,发觉不过一瓶旧酒而已,故引得此番议论,让君见笑 了。 事件驱动程序设计是围绕着消息基础形成的,发生一个事件,伴随着一大堆的消息。
我理解“回调机制”是window 在执行某个API函数的过程中,调用指定的一个函数。我们可以模拟一下:
假设 ms 提供一个函数叫做 EnumFont ,该函数是得到所有的字体,假设它的实现是
EnumFont()
{
while ( (f =FindNextFont()) !=NULL)
{
printf("fontname: " + f.name);
}
}
这样就循环显示出所有的字体名称。但是,开发者可能对字体信息另有用处,那么如何才能让开发者能使用这些信息呢,于是做改进:
EnumFont( void* userFunc )
{
while ( (f =FindNextFont()) !=NULL)
{
printf("fontname: " + f.name);
if ( userFunc!=NULL) userFunc( f) ;
}
}
假设userFunc 是一个函数 void f( FontObject font).这样使用者只需要定义一个函数:
void myfunc( FontObject font)
{
listCtrl.Addstring ( font.name);
}
通过使用 EnumFont ( myfunc) 就可以将所有额字体信息添加到一个列表框中。那么我们称 myfunc是一个回调函数,即让某个系统函数调用的函数。因此可以得出结论:
1 回调函数是由开发者按照一定的原型进行定义的函数
2 回调函数并不由发者直接调用执行
3 回调函数通常作为参数传递给系统API,由该API来调用。
4 回调函数可能被系统API调用一次,也可能被循环调用多次。
比如 函数int EnumFontFamilies(
HDC hdc, // handle to device control
LPCTSTR lpszFamily, // pointer to family-name string
FONTENUMPROC lpEnumFontFamProc,
// pointer to callback function
LPARAM lParam // pointer to application-supplied data
);
其中的 FONTENUMPROC lpEnumFontFamProc就是一个回调函数,该函数遵照格式
int CALLBACK EnumFontFamProc( ENUMLOGFONT FAR *lpelf, NEWTEXTMETRIC FAR *lpntm, int FontType, LPARAM lParam )进行定义。
如 同mutant所说,回调函数主要用于一些比较费时的操作,或响应不知道何时将会发生的事件,回调函数提供了一种异步的机制,相对于同步执行,提高了效 率.前者的例子如WriteFileEx,ReadFileEx等,函数的最后一个参数是一个回调函数的指针,程序中调用WriteFileEx以后,就 直接返回了,可以继续进行其他工作,系统在读写操作完成后通知程序作善后处理.后者的例子就是windows的事件机制回调函数的另一个用途,是用于一些 枚举函数,如EnumDisplayModes等,每找到一种支持的显示模式,就通知回调函数,由回调函数具体处理,这是因为 EnumDisplayModes本身并不知道用户要如何处理.能,用户提供回调函数,定制系统的功能,这样,不同的用户提供不同的回调函数,可以使系统 具有不同的功能.这就是所谓的plugin.使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地 址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。至于如 何定义回调函数,跟具体使用的API函数有关,一般在帮助中有说明回调函数的参数和返回值等。



做个有用的人!
2006-03-26 21:04
wonglaye
Rank: 1
等 级:新手上路
帖 子:27
专家分:0
注 册:2006-3-26
得分:0 

楼上这篇写得不错哦
初始化这篇帖子的luckbird所说的WinAPI是调用约定,同样也是为了解决函数栈问题,比如参数入栈出栈的顺序,怎样清理推栈,调用约定分成好几个,有cdecl,stdcall,fastcall等等,MFC默认的是cdecl,而你写的Win32默认的是stdcall,在Win3.0时用PASCAL宏来声明这个约定,而在Win32以后就用WinAPI或者APIENTRY来声明stdcall了,这些调用约束在调试时经常会在调试窗口看到的。
楼上的这篇真的不错,还有那么一点文学味道,喜欢啊


freedom is not free to gain
2006-03-26 21:53
cowboywd
Rank: 1
等 级:新手上路
帖 子:4
专家分:0
注 册:2006-3-26
得分:0 
good
2006-03-27 01:11
luckbird
Rank: 1
等 级:新手上路
帖 子:30
专家分:0
注 册:2005-3-21
得分:0 

解释的实在是太漂亮了,真高兴又长知识了,真心感谢楼上的几位,特别是幽园香客,真是使我茅塞顿开啊,不过还是小弟接触的太少,大体上明白了 可是 对于 枚举函数 是头一次听说  这个又是个什么东东那


2006-03-27 22:49
teawon
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2011-12-2
得分:0 
不错,解释的太好了,学习了
2012-02-21 17:08



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-52123-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.060688 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved