转自:http://www.cnblogs.com/chenyuming507950417/archive/2012/01/02/2310114.html

今天讨论下C/C++中的回调函数。

在理解“回调函数”之前,首先讨论下函数指针的概念。

函数指针

(1)概念:指针是一个变量,是用来指向内存地址的。一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件都可以用指针来指向它。函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用指针来存取函数,把这种指向函数入口地址的指针称为函数指针。

(2)先来看一个Hello World程序:

int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}

然后,采用函数调用的形式来实现:

void Invoke(char* s);

int main(int argc,char* argv[])
{
Invoke("Hello World!\n");
return 0;
} void Invoke(char* s)
{
printf(s);
}

用函数指针的方式来实现:

void Invoke(char* s);

int main()
{
void (*fp)(char* s); //声明一个函数指针(fp)
fp=Invoke; //将Invoke函数的入口地址赋值给fp
fp("Hello World!\n"); //函数指针fp实现函数调用
return 0;
} void Invoke(char* s)
{
printf(s);
}

由上知道:函数指针函数的声明之间唯一区别就是,用指针名(*fp)代替了函数名Invoke,这样这声明了一个函数指针,然后进行赋值fp=Invoke就可以进行函数指针的调用了。声明函数指针时,只要函数返回值类型、参数个数、参数类型等保持一致,就可以声明一个函数指针了。注意,函数指针必须用括号括起来 void (*fp)(char* s)。

实际中,为了方便,通常用宏定义的方式来声明函数指针,实现程序如下:

typedef void (*FP)(char* s);
void Invoke(char* s); int main(int argc,char* argv[])
{
FP fp; //通常是用宏FP来声明一个函数指针fp
fp=Invoke;
fp("Hello World!\n");
return 0;
} void Invoke(char* s)
{
printf(s);
}

函数指针数组

下面用程序对函数指针数组来个大致了解:

#include <iostream>
#include <string>
using namespace std; typedef void (*FP)(char* s);
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;} int main(int argc,char* argv[])
{
void* a[]={f1,f2,f3}; //定义了指针数组,这里a是一个普通指针
a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数 FP f[]={f1,f2,f3}; //定义一个函数指针的数组,这里的f是一个函数指针
f[0]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用 return 0;
}

回调函数

(1)概念:回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。

(2)标准Hello World程序:

int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}

将它修改成函数回调样式:

//定义回调函数
void PrintfText()
{
printf("Hello World!\n");
} //定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)())
{
callfuct();
} //在main函数中实现函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText);
return 0;
}

修改成带参的回调样式:

//定义带参回调函数
void PrintfText(char* s)
{
printf(s);
} //定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
callfuct(s);
} //在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText,"Hello World!\n");
return 0;
}

至此,对回调函数应该有了一个大致的了解。

C/C++之回调函数的更多相关文章

  1. 小兔JS教程(三)-- 彻底攻略JS回调函数

    这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...

  2. 嵌入式&iOS:回调函数(C)与block(OC)传 参/函数 对比

    C的回调函数: callBack.h 1).声明一个doSomeThingCount函数,参数为一个(无返回值,1个int参数的)函数. void DSTCount(void(*CallBack)(i ...

  3. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  4. 理解 JavaScript 回调函数并使用

    JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...

  5. 关于js的回调函数的一点看法

    算了一下又有好几个月没写博客了,最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做nativ ...

  6. JS学习:第二周——NO.1回调函数

    [回调函数] 定义:把一个函数的定义阶段,作为参数,传给另一个函数: 回调函数调用次数,取决于条件: 回调函数可以传参: 回调函数可以给变this指向,默认是window: 回调函数没有返回值,for ...

  7. 【java回调】java两个类之间的回调函数传递

    背景交代:熟悉用js开发的cordovaAPP:对java一窍不通的我,老师让做一个监测用户拍照事件的功能,无奈没有找到现成的库,无奈自己动手开发java插件~~0基础java GreenHand,祝 ...

  8. Java|今天起,别再扯订阅和回调函数

    编程史上有两个令人匪夷所思的说辞,一个是订阅,一个是回调函数. 我想应该还有很多同学为“事件的订阅”和“回调函数”所困扰,因为事情本来就不应该按这个套路来解释. 多直白,所谓的“回调函数”你完全可以线 ...

  9. C++ 回调函数的定义与用法

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

  10. 通过修改i8042prt端口驱动中类驱动Kbdclass的回调函数地址,达到过滤键盘操作的例子

    同样也是寒江独钓的例子,但只给了思路,现贴出实现代码 原理是通过改变端口驱动中本该调用类驱动回调函数的地方下手 //替换分发函数 来实现过滤 #include <wdm.h> #inclu ...

随机推荐

  1. Delphi 绘图对象

    来自:http://blog.csdn.net/lailai186/article/details/8755430 ========================================== ...

  2. HDU 3085 Nightmare Ⅱ(双向BFS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3085 题目大意:给你一张n*m地图上,上面有有 ‘. ’:路 ‘X':墙 ’Z':鬼,每秒移动2步,可 ...

  3. jmeter------线程组(默认)

    Jmeter中的采样器必须要基于线程组. 一.添加线程组 在测试计划上右键,然后选择,如下图: 二.线程组界面 三.线程组界面配置说明 1.名称:线程组自定义名称: 2.注释:添加的一些备注说明信息, ...

  4. bzoj1941 hdu5992

    看了青岛赛区的题简单学了一下kd,感觉这东西还是挺厉害的 一般kd树找最近点对最坏是O(n),但是随机情况下跑得还是很快的 kd树是一棵BST,但是每一层的关键字不同 一般写法是按照每一维轮流来,这一 ...

  5. markdown 测试代码

    这是 H1 这是 H2 这是 H3 这是 H4 这是 H5 这是 H6 A First Level Header A Second Level Header Now is the time for a ...

  6. Asp.net MVC CSS/Javascript Bundle 配置文件

    Asp.net mvc 中使用 Web Optimization 可以合并.压缩JS和CSS文件,但是每次修改都要改代码 ~/App_Start/BundleConfig.cs ,遂有了将它挪到配置文 ...

  7. LoadRunner配置方案

    1.配置方案运行时设置 选择“Tools”>“Options”.在“Options”对话框有“Run-Time Settings”(运行时设置).“Timeout”(超时).“Run-Time  ...

  8. 洛谷P0248 [NOI2010] 超级钢琴 [RMQ,贪心]

    题目传送门 超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符 ...

  9. python并发编程-线程池

    from concurrent.futures import ThreadPoolExecutor import time def func(n): time.sleep(2) print(n) re ...

  10. 【3.16高一(第二学期)模拟测试】 T3,T4题解

    看到这个标题我想你一定会想为什么小编只发T3,T4的题解,原因有很多:1)小编也不怎么会讲:2)小编搜遍各大OJ,都没有找到可以提交的地方:3)虽然给了测试数据,小编懒得一个一个试.如果你找到了测评网 ...