`
qimo601
  • 浏览: 3415864 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

回调函数测试例子

阅读更多

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。 

 

回调函数实现的机制是

   ⑴定义一个回调函数

  ⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;

  ⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。 
实例代码:

#include <iostream>
using namespace std;

typedef void (*CALLBACK)(int a,int b);

class base
{
private:
	int m;
	int n;
	static CALLBACK func;
	public:
	void registercallback(CALLBACK fun,int k,int j);
	void callcallback();

};

CALLBACK base::func=NULL;

void base::registercallback(CALLBACK fun,int k,int j)
{
	func=fun;
	m=k;
	n=j;
}

void base::callcallback()
{
	base::func(m,n);
}

void seiya(int a,int b)
{
	cout<<a<<endl<<b<<endl;
	cout<<"this is seiya callback function"<<endl;
}

void zilong(int a,int b)
{
	cout<<a<<endl<<b<<endl;
	cout<<"this is zilong callback function"<<endl;
}

void main(void)
{
  base ba;
  ba.registercallback(seiya,2,3);
  ba.callcallback();
   
  ba.registercallback(zilong,5,6);   
  ba.callcallback();
}

 

 


2 过程语言中的回调(C)

2.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.2 参数传递规则

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

将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:

 


// 被调用函数是以int为参数,以int为返回值
__stdcall int callee(int); 

// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int)); 

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

 

 

指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列

2.3 应用举例

C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。

快速排序函数原型:

 


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

 

 

其中fcmp就是一个回调函数的变量。

下面给出一个具体的例子:

 


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

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

int main(void)
{
   int  x;

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

int sort_function( const void *a, const void *b)
{
   return *(int*)a-*(int*)b;
}

测试代码2:

 

#include "stdafx.h"
#include "windows.h"
#include <iostream>
using namespace std;

//回调函数指针
typedef int(WINAPI* WNDCALLBACK)(int*,int*);
//回调函数
int __stdcall Call(int *a,int *b);
//定义类
class A
{
public:
	//比较两个数字大小
	int Compare(int m,int n,WNDCALLBACK function)
	{
		int *a=&m;
		int *b=&n;
		return (*function)(a,b);
	}
};

int main(int argc, char* argv[])
{
	int a=10;
	int b=6;
	A m_a;
	cout<<m_a.Compare(a,b,Call)<<endl;
	return 0;
}

//回调函数
int CALLBACK Call(int *a,int *b)
{	
	return *a>(*b)?(*a):(*b);
}

 

 

回调函数例子2:

 

 

回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。

CODE:
typedef void (*f1) ();// 为函数指针声明类型定义
void (*p) (); //p是指向某函数的指针
void func1() 
{
printf("From func1(), Hello World!\n");    /* do something */
} 

void caller(void(*ptrfunc1)())
{
ptrfunc1(); /* 调用ptr指向的函数 */ 
}

//typedef bool (*f2) (int *);// 为函数指针声明类型定义
//bool (*q) (int *); //p是指向某函数的指针

bool func2(int* t_i) 
{
/* do something */
printf("From func2() = %d, Hello World!\n", (*t_i)++);
return true;
} 

void caller2(bool (*ptrfunc2)(int *), int * i)
{
ptrfunc2(i); /* 调用ptr指向的函数 */ 
}


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

//无参数调用
p = func1; /* 传递函数地址地址 */
caller(p); /* 传递函数地址到调用者 */

//有参数调用
int i = 0; 
for (int j = 0; j < 10; j++)
{
caller2(func2, &i); //* 传递函数地址到调用者 */
}

//有参数调用第二次
i = 0;
//q = func2; /* 传递函数地址地址 */
//caller2(q, &i); /* 传递函数地址到调用者 */
printf("\n");
printf("From main(), Hello World!\n");
getchar();
return 0;
}

 

 

回调函数例子3  

下面是自己写的一个简单的回调函数,相比其他的那些复杂的代码,这个更容易理解:

#include<stdio.h>
#include<stdlib.h>
void perfect(int n)
{
 int i=1;
    int count=0;
 for(i=1;i<n;i++)
 {
     
  if(0==n%i)
  {
   count+=i;
  }
 }
 if(count==n)
  printf("%d是完数\n",n);
 else printf("%d不是完数\n",n);
}
void myCallback(void (*perfect)(int ),int n)
{
 perfect(n);
}

int main()
{
 int n;
 printf("请输入一个正整数\n");
 scanf("%d",&n);

 myCallback(perfect,n);
 return 0;
 
}
 

 

 

分享到:
评论

相关推荐

    C++回调函数测试例子

    C++回调函数测试例子。

    keras自定义回调函数查看训练的loss和accuracy方式

    第二种方式就是通过自定义一个回调函数Call backs,来实现这一功能,本文主要讲解第二种方式。 一、如何构建回调函数Callbacks 本文所针对的例子是卷积神经网络Lenet-5,数据集是mnist数据集。 1.1 什么是回调函数 ...

    VC++ DLL回调使用例程(测试Demo+DLL库)

    基于VC2019的DLL回调例子 包括测试Demo和DLL库 x86+x64完整项目 开箱即用

    js 编程笔记 无名函数

    明显的例子就是setInterval,我想这是很多人头疼的一个函数,尤其是你想在回调函数中添加参数的时候。 而且最叫人头疼的是,DHTML不是w3c规定的标准,于是不同的浏览器给出的setInterval参数表还不一样。。。 就我...

    易语言 HOOK CreateWindowEx 例子

    部分代码 .版本 2 ........ ...._临时子程序 () ' 在初始化代码执行完毕后调用测试代码 .......' 本名称子程序用作测试程序用,仅在开发及调试环境中有效,编译发布程序前将...安装 (“user32.dll”, “CreateWindowExA”, &回调函数)

    inspect-loader:Webpack加载器,用于加载器测试和调试。 用接收到的输入调用函数

    将inspect-loader放在要测试的加载器前面,并传递一个回调函数。 将使用有关给定输入( arguments )的有用信息来调用回调函数。 它还公开了内部加载程序上下文以供进一步检查: webpack ( { ... module : { ...

    VTK DICOM 图像显示以及距离角度测量

    基于VTK类库的距离角度测量例子,可以用滚轮切换图像,包含距离角度WIDGET的回调函数的写法。

    Windows内核安全与驱动开发光盘源码

    10.3.7 boot驱动完成回调函数和稀疏文件 187 10.3.8 读/写请求的处理 190 第11章 文件系统的过滤与监控 199 11.1 文件系统的设备对象 200 11.1.1 控制设备与卷设备 200 11.1.2 生成自己的一个控制设备 201 ...

    Windows内核安全驱动开发(随书光盘)

    10.3.7 boot驱动完成回调函数和稀疏文件 187 10.3.8 读/写请求的处理 190 第11章 文件系统的过滤与监控 199 11.1 文件系统的设备对象 200 11.1.1 控制设备与卷设备 200 11.1.2 生成自己的一个控制设备 201 ...

    lua-mtstates:多线程Lua状态(请参阅

    一般原则是通过在此状态下运行设置函数来准备状态,该设置函数返回回调函数,此后可以从不同线程中调用该回调函数。 当状态数超过可用线程数时,这在线程池方案中很有用。 该软件包也可以通过LuaRocks获得,请参阅...

    基于SIP开发软件电话的一些资源(转自YOUTOO)

    该目录下的若干文件基本上是测试了Osip的一些基本功能函数,例如URI解析之类,可以大概了解一下oSIP的语法规范和调用方法,同时也能校验一下之前编译的OSIP开发库能否正常使用,成功完成本项工作后,可以进入下一步...

    php中使用array_filter()函数过滤空数组的实现代码

    如果没有给出回调函数,所有的等于 FALSE 的元素将会被移除掉,这不正好可以用来过滤一些有空元素的数组吗?连忙写个例子测试一下想法: 复制代码 代码如下: $entry = array(   0 =&gt; ‘蓝色夏威夷的博客’,   1...

    artDialog_Demo

    4、 如果有取消按钮回调函数,那关闭按钮的回调函数也将与其相同 5、 增加当出现多个对话框时让顶层的与众不同的特性 6、 让Esc键关闭最高层对话框 7、 锁屏的时候改用js屏蔽页面滚动功能,取消原来CSS隐藏滚动...

    qunit-data-providers:向 QUnit 测试添加数据提供者

    data :传递给回调的参数列表数组,或生成数组的函数 expected :测试中的断言数 callback : 用于运行测试和任何断言的函数 async : 设置测试是否应该异步运行的标志 例子 // Uses an array of argument lists ...

    node.js中的fs.exists方法使用说明

    回调函数包含一个参数exists,true则文件存在,否则是false。 语法: 代码如下: fs.exists(path, callback) 由于该方法属于fs模块,使用前需要引入fs模块(var fs= require(“fs”) ) 接收参数: path 欲检测的...

    stepperbox:JavaScript测试库,用于创建具有顺序行为的存根函数

    StepperBox是一个测试库,用于创建在不同步骤调用不同回调的桩函数。 它的工作方式与Sinon间谍类似,但是其实现和使用要简单得多。 这使您可以创建更明确的模拟行为,而无需了解任何隐秘的API。 例子 var ...

Global site tag (gtag.js) - Google Analytics