版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用它所指向的函数时,我们就说这个函数是回调函数。为什么要使用回调函数呢?我们先看一个小例子:Node*Search_List(Node*node,constintvalue)while(node!=NULL)if(node-value=value)break;node=node-next;returnnode;这个函数用于在一个单向链表中查找一个指定的值,返回保存这个值的节点。它的参数是指向这
2、个链表第一个节点的指针以及要查找的值。这个函数看上去很简单,但是我们考虑一个问题:它只能适用于值为整数的链表,如果查找一个字符串链表,我们不得不再写一个函数,其实大部分代码和现在这个函数相同,只是第二个参数的类型和比较的方法不同。其实我们更希望令查找函数与类型无关,这样它就能用于查找存放任何类型值的链表了,因此必须改变比较的方式,而借助回调函数就可以达到这个目的。我们编写一个函数(回调函数),用于比较两个同类型的值,然后把一个指向这个函数的指针作为参数传递给查找函数,查找函数调用这个比较函数来执行比较,采用这个方法,任何类型的值得都可以进行比较。我们还必须给查找函数传递一个指向待比较的值的指针
3、而不是值本身,也就是一个void*类型的形参,这个指针会传递给回调函数,进行最终的比较。这样的修改可以让我们传递指向任何类型的指针到查找函数,从而完成对任何类型的比较,这就是指针的好处,我们无法将字符串、数组或者结构体作为参数传递给函数,但是指向它们的指针却可以。现在,我们的查找函数就可以这样实现:NODE*Search_List(NODE*node,int(*compare)(voidconst*,voidconst*),voidconst*desired_value);while(node!=NULL)if(compare(node-value_address),desired_value
4、)=0)break;node=nodenext;returnnode;可以看到,用户将一个函数指针传递给查找函数,后者将回调这个函数。注意这里我们的链表节点是这样定义的:typedefstructlistvoid*value_address;structlist*next;N0DE;这样定义可以让NODE*类型的指针指向存储任何类型数据的链表节点。而value_address就是指向具体数据的指针,我们把它定义为void*,表示一个指向未知类型的指针,这样链表就可以存储任何类型的数据了,而我们传递给查找函数Search_List的第一个参数就可以统一表示为:NODE*,否则,还是要分别写查找函
5、数以适应存储不同数据类型的链表。现在,查找函数与类型无关,因为它不进行实际的比较,因此,我们必须编写针对不同类型的比较函数,这是很容易实现的,因为调用者知道链表中所包含的值的类型,如果创建几个分别包含不同类型值的链表,为每种类型编写一个比较函数就允许单个查找函数作用于所有类型的链表。下面是一个比较函数,用于在一个整型链表中查找:注意强制类型转换,比较函数的参数必须被声明为void*以匹配查找函数的原型,然后强制转换为(int*)类型用于比较整型。intint_compare(voidconst*a,voidconst*b)if(*(int*)a=*(int*)b)return0;elseret
6、urnT;这个函数可以这样被使用:desired_node=Search_List(root,int_compare,&desired_int_value);如果你希望在一个字符串链表中进行查找,下面的代码就可以完成任务:desired_node=Search_List(root,strcmp,“abcdefg);正好库函数strcmp所执行的比较和我们需要的一样,不过gcc会发出警告信息:因为strcmp的参数被声明为constchar*而不是voidconst*。上面的例子展示了回调函数的基本原理和用法,回调函数的应用是非常广泛的。通常,当我们想通过一个统一接口实现不同内容的时候,用回调函
7、数来实现就非常合适。任何时候,如果你所编写的函数必须能够在不同的时刻执行不同的类型的工作或者执行只能由函数调用者定义的工作,你都可以用回调函数来实现。许多窗口系统就是使用回调函数连接多个动作,如拖拽鼠标和点击按钮来指定调用用户程序中的某个特定函数。回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。函数指针有两具用途:调用函数和做函数的参数。请看下面的例子:voidf();函数原型上面的语句声明了一个函数,没有输入参数并返回vo
8、id。那么函数指针的声明方法如下:void(*)();让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:/获得函数指针的大小unsignedpsize=sizeof(void(*)();/为函数指针声明类型定义typedefvoid(*pfv)();pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法【函数指针】在程
9、序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。1函数指针定义函数类型(*指针变量名)(形参列表);“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如:int(*f)(intx);double(*ptr)(doublex);在定义函数指针时请注意:函数指针和它指向的函数的参数个数和类型都应该是一致的;函数指针的类型和函数的返回值类型也必须
10、是一致的。2函数指针的赋值函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。例如,intfunc(intx);/*声明一个函数*/int(*f)(intx);/*声明一个函数指针*/f=func;/*将func函数的首地址赋给指针f*/赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。3通过函数指针调用函数函数指针是通过函数名及有关参数进行调用的。与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*P等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则
11、*pf就等价于它所指的变量f。同样地,*f是指向函数func(x)的指针,则*f就代表它所指向的函数func。所以在执行了f=func;之后,(*f)和func代表同一函数。由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。现在我们就讨论如何用函数指针调用函数,它应执行下面三步:首先,要说明函数指针变量。例如:int(*f)(intx);其次,要对函数指针变量赋值。例如:f=func;(func(x)必须先要有定义)最后,要用(*指针变量)(参数表);调用函数例如:(*f)(x);(x必须先赋值)【例】任意输入n个数,找出其中最大数,并且输出最大数值。main()intf
12、();inti,a,b;int(*p)();/*定义函数指针*/scanf(%d,&a);p=f;/*给函数指针p赋值,使它指向函数f*/for(i=1;iy)?x:y;return;运行结果为:343-45438942351-534988555789/TheMaxNumberis:4389【例】(*(void(*)()0)()表示什么意思?void(*)()声明函数指针让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。(void(*)()0把0强制转换
13、成函数指针(*(void(*)()0)()调用所以(*(void(*)()0)()的意思是:调用地址为0处的程序。【指针函数】一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。返回指针的函数,一般定义格式为:类型标识符*函数名(参数表)int*f(x,y);其中x,y是形式参数,f是函数名,调用后返回一个指向整型数据的地址指针。f(x,y)是函数,其值是指针。如:char*ch();表示的就是一个返回字符型指针的函数,请看下面的例题:【例】将字符串1(str1)复制到字符串2(str2),并输出字符串2#includestdio.h
14、main()char*ch(char*,char*);charstr1=Iamgladtomeetyou!;charstr2=WelcomtostudyC!;printf(%s,ch(str1,str2);char*ch(char*str1,char*str2)inti;char*p;p=str2if(*str2=NULL)exit(-1);do*str2=*str1;str1+;str2+;while(*str1!=NULL);return(p);通过分析可得函数指针是一个指向函数的指针,而指针函数只是说明他是一个返回值为指针的函数,函数指针可以用来指向一个函数。函数名与函数指针一通常的函数
15、调用一个通常的函数调用的例子:自行包含头文件voidMyFun(intx);此处的申明也可写成:voidMyFun(int);intmain(intargc,char*argv)return0;MyFun(10);这里是调用MyFun(10);函数voidMyFun(intx)这里定义一个MyFun函数printf(“dn”,x);这个MyFun函数是一个无返回值的函数,它并不完成什么事情。这种调用函数的格式你应该是很熟悉的吧!看主函数中调用MyFun函数的书写格式:MyFun(10);我们一开始只是从功能上或者说从数学意义上理解MyFun这个函数,知道MyFun函数名代表的是一个功能(或是说
16、一段代码)。二函数指针变量的申明就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的首地址也以存储在某个函数指针变量里的。这样,我就可以通过这个函数指针变量来调用所指向的函数了。在C系列语言中,任何一个变量,总是要先申明,之后才能使用的。那么,函数指针变量也应该要先申明吧?那又是如何来申明呢?以上面的例子为例,我来申明一个可以指向MyFun函数的函数指针变量FunP。下面就是申明FunP变量的方法:void(*FunP)(int);也可写成void(*FunP)(intx);你看,整个函数指针变量的申明格式如同函数MyFun的申明处一样,只不过我们把MyFun改成(*FunP)而已
17、,这样就有了一个能指向MyFun函数的指针FunP了。(当然,这个FunP指针变量也可以指向所有其它具有相同参数及返回值的函数了。)三通过函数指针变量调用函数有了FunP指针变量后,我们就可以对它赋值指向MyFun,然后通过FunP来调用MyFun函数了。看我如何通过FunP指针变量来调用MyFun函数的:自行包含头文件voidMyFun(intx);这个申明也可写成:voidMyFun(int);void(*FunP)(int);也可申明成void(*FunP)(intx),但习惯上一般不这样。intmain(intargc,char*argv)MyFun(10);这是直接调用MyFun函数
18、FunP=&MyFun;将MyFun函数的地址赋给FunP变量(*FunP)(20);这是通过函数指针变量FunP来调用MyFun函数的。printf(“dn”,x);哦,我的感觉是:MyFun与FunP的类型关系类似于int与int*的关系。函数MyFUn好像是一个如int的变量(或常量),而FunP则像一个如int*一样的指针变量。inti,*pi;pi=&i;与FUnP=&MyFun比较。四调用函数的其它书写格式函数指针也可如下使用,来完成同样的事情:自行包含头文件voidMyFun(intx);void(*FunP)(int);申明一个用以指向同样参数,返回值函数的指针变量。intma
19、in(intargc,char*argv)MyFun(10);这里是调用MyFun(10);函数FunP=MyFun;将MyFun函数的地址赋给FunP变量FunP(20);这是通过函数指针变量来调用MyFun函数的。return0;voidMyFun(intx)这里定义一个MyFun函数printf(“dn”,x);FunP=MyFun;可以这样将MyFun值同赋值给FunP,难道MyFun与FunP是同一数据类型(即如同的int与int的关系),而不是如同int与int*的关系了?(有没有一点点的糊涂了?)看来与之前的代码有点矛盾了,是吧!所以我说嘛!请容许我暂不给你解释,继续看以下几种情
20、况(这些可都是可以正确运行的代码哟!):代码之三:intmain(intargc,char*argv)MyFun(10);这里是调用MyFun(10);函数FunP=&MyFun;将MyFun函数的地址赋给FunP变量FunP(20);这是通过函数指针变量来调用MyFun函数的。return0;代码之四:intmain(intargc,char*argv)MyFun(10);这里是调用MyFun(10);函数FunP=MyFun;将MyFun函数的地址赋给FunP变量(*FunP)(20);这是通过函数指针变量来调用MyFun函数的。return0;真的是可以这样的噢!还有呐!看intmain
21、(intargc,char*argv)(*MyFun)(10);看,函数名MyFun也可以有这样的调用格式return0;呵呵!假使我是“福尔摩斯”,依据以往的知识和经验来推理本篇的“新发现”,必定会由此分析并推断出以下的结论:1. 其实,MyFun的函数名与FunP函数指针都是一样的,即都是函数指针。MyFun函数名是一个函数指针常量,而FunP是一个函数数指针变量,这是它们的关系。2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许MyFun(10);这种形式地调用(这样方便多了并与数学中的函数形式一样,不是
22、吗?)。3. 为统一起见,FunP函数指针变量也可以FunP(10)的形式来调用。4. 赋值时,即可FunP=&MyFun形式,也可FunP=MyFun。上述代码的写法,随便你爱怎么着!补充说明一点:在函数的申明处:voidMyFun(int);不能写成void(*MyFun)(int)。void(*FunP)(int);不能写成voidFunP(int)。五定义某一函数的指针类型:就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数指针变量。我先给你一个自定义数据类型的例子。typedefint*PINT;为int*类型定义了一个PINT的别名intmain(
23、)intx;PINTpx=&x;与int*px=&x;是等价的。PINT类型其实就是int*类型*px=10;px就是int*类型的变量return0;下面我们来看一下函数指针类型的定义及使用:(请与上对照!)自行包含头文件voidMyFun(intx);此处的申明也可写成:voidMyFun(int);typedefvoid(*FunType)(int);这样只是定义一个函数指针类型FunTypeFUnP;然后用FunType类型来申明全局FunP变量intmain(intargc,char*argv)/FunTypeFUnP;函数指针变量当然也是可以是局部的,那就请在这里申明了。MyFun(10);FUnP=&MyFun;(*FUnP)(20);return0;voidMyFun(intx)printf(“dn”,x);看黑体部分:首先,在void(*FunType)(int);前加了一个typedef。这样只是定义一个名为FunType函数指针类型,而不是一个FunType变量。然后,FunTypeFunP;这句就如PINTpx;样地申明一个FunP变量。其它相同。整个程序完成了相同的事。这
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年液氨泄漏事故应急救援预案
- 绿色简约语文开学第一课
- 《网络安全》安全教育课件
- 合规转利润:降本增效全指南(2026)《GBT 39145-2020硅片表面金属元素含量的测定 电感耦合等离子体质谱法》
- 合规转利润:降本增效全指南(2026)《GBT 39069-2020商务楼宇等级划分要求》
- 2026年浙江省高考英语听力理解冲刺试卷
- 河北衡水市安平县实验初级中学2026-2027学年八年级上学期开学数学试题(含答案)
- 康复心理学基础
- 幼儿园洗手教育
- 肝脏外伤的外科治疗
- 2026广东湛江市遂溪发展集团有限公司招聘15人(第二批)考试备考题库及答案详解
- 2026年重庆市部编版高一语文一轮复习第五单元文言文阅读测试题库试卷
- 20S515 钢筋混凝土及砖砌排水检查井
- 土地租用协议
- 电子警察系统施工方案
- 动物防疫条件审查场所选址风险评估申请表、评估标准、评估报告
- 西方园林史智慧树知到答案章节测试2023年内蒙古农业大学
- 生活垃圾焚烧发电厂项目施工组织设计
- TEERT 018-2021 金属抛光打磨用湿式除尘一体机
- 太阳能科普知识-课件
- 管理者的自我管理(讲师手册)课件
评论
0/150
提交评论