2022年2022年回调函数CallbackFunction

上传人:cl****1 文档编号:567479843 上传时间:2024-07-20 格式:PDF 页数:17 大小:140.38KB
返回 下载 相关 举报
2022年2022年回调函数CallbackFunction_第1页
第1页 / 共17页
2022年2022年回调函数CallbackFunction_第2页
第2页 / 共17页
2022年2022年回调函数CallbackFunction_第3页
第3页 / 共17页
2022年2022年回调函数CallbackFunction_第4页
第4页 / 共17页
2022年2022年回调函数CallbackFunction_第5页
第5页 / 共17页
点击查看更多>>
资源描述

《2022年2022年回调函数CallbackFunction》由会员分享,可在线阅读,更多相关《2022年2022年回调函数CallbackFunction(17页珍藏版)》请在金锄头文库上搜索。

1、1 回调函数Callback Function 编辑本段 简介对于很多初学者来说,往往觉得回调函数很神秘,很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、为什么要使用它们等等问题,在开始之前, 假设你已经熟知了函数指针。什么是回调函数?简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址 )作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。为什么要使用回调函数?因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。如果

2、想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell 排序、 shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。回调可用于通知机制,例如, 有时要在程序中设置一个计时器,每到一定时间, 程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时, 就需有一个特定原型的函数指针, 用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTi

3、mer() API 使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。另一个使用回调机制的API 函数是 EnumWindow() ,它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow() 并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。不管怎么说,回调函数是继续自C 语言的,因而,在C+中,应只在与C 代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况

4、, 在 C+中应使用虚拟方法或函数符 (functor) ,而不是回调函数。编辑本段 简单的回调函数实现代码实现下面创建了一个sort.dll的动态链接库,它导出了一个名为CompareFunction 的类型-typedef int (_stdcall *CompareFunction)(const byte*, const byte*),它就是回调函数的类型。另外,它也导出了两个方法:Bubblesort() 和 Quicksort() ,这两个方法原型相同,但实现了不同的排序算法。void DLLDIR _stdcall Bubblesort(byte* array,int size,i

5、nt elem_size,CompareFunction cmpFunc); void DLLDIR _stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc); 这两个函数接受以下参数:byte * array :指向元素数组的指针(任意类型 )。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 17 页 - - - - - - - - - 2 int size:数组中元素的

6、个数。int elem_size:数组中一个元素的大小,以字节为单位。CompareFunction cmpFunc :带有上述原型的指向回调函数的指针。这两个函数的会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。对编写者来说, 不必介意函数在何处实现, 或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值 (库的编写者和使用者都必须遵守这个约定):-1:如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。0:如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所

7、谓。1:如果第一个元素较大,那在已排序好的数组中,它应该排第二个元素后面。基于以上约定,函数Bubblesort() 的实现如下, Quicksort() 就稍微复杂一点:void DLLDIR _stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc) for(int i=0; i size; i+) for(int j=0; j size-1; j+) /回调比较函数if(1 = (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size) /两个相

8、比较的元素相交换byte* temp = new byteelem_size; memcpy(temp, array+j*elem_size, elem_size); memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size); memcpy(array+(j+1)*elem_size, temp, elem_size); delete temp; 注意:因为实现中使用了memcpy() ,所以函数在使用的数据类型方面,会有所局限。对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort() 函数。下面有二个简单的示例,一个比

9、较两个整数,而另一个比较两个字符串:int _stdcall CompareInts(const byte* velem1, const byte* velem2) int elem1 = *(int*)velem1; int elem2 = *(int*)velem2; if(elem1 elem2) return 1; return 0; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 17 页 - - - - - - - - - 3 int _stdcall Com

10、pareStrings(const byte* velem1, const byte* velem2) const char* elem1 = (char*)velem1; const char* elem2 = (char*)velem2; return strcmp(elem1, elem2); 下面另有一个程序,用于测试以上所有的代码,它传递了一个有5 个元素的数组给Bubblesort() 和 Quicksort() ,同时还传递了一个指向回调函数的指针。int main(int argc, char* argv) int i; int array = 5432, 4321, 3210

11、, 2109, 1098; cout Before sorting ints with Bubblesortn; for(i=0; i 5; i+) cout arrayi n; Bubblesort(byte*)array, 5, sizeof(array0), &CompareInts); cout After the sortingn; for(i=0; i 5; i+) cout arrayi n; const char str510 = estella,danielle,crissy,bo,angie; cout Before sorting strings with Quickso

12、rtn; for(i=0; i 5; i+) cout stri n; Quicksort(byte*)str, 5, 10, &CompareStrings); cout After the sortingn; for(i=0; i 5; i+) cout stri n; return 0; 如果想进行降序排序(大元素在先 ),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。调用约定上面的代码中,可在函数原型中找到_stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。任何支持开发基于Win32 的程序都必须支持这个扩展或

13、其等价物。以_stdcall 标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32 API(除了个别接受可变参数的除外)都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是 Pascal的标准约定。 但在 C/C+中,调用约定是调用者负责清理堆栈,而不是被调用函数;为强制函数使用C/C+ 调用约定,可使用 _cdecl。另外,可变参数函数也使用C/C+调用约定。Windows 操作系统采用了标准调用约定(Pascal约定 ),因为其可减小代码的体积。这点名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - -

14、- - - - - 名师精心整理 - - - - - - - 第 3 页,共 17 页 - - - - - - - - - 4 对早期的Windows 来说非常重要,因为那时它运行在只有640KB 内存的电脑上。如果你不喜欢_stdcall,还可以使用CALLBACK宏,它定义在windef.h 中:#define CALLBACK _stdcallor #define CALLBACK PASCAL /而 PASCAL 在此被 #defined 成_stdcall 作为回调函数的C+方法因为平时很可能会使用到C+编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码:clas

15、s CCallbackTester public: int CALLBACK CompareInts(const byte* velem1, const byte* velem2); ; Bubblesort(byte*)array, 5, sizeof(array0), &CCallbackTester:CompareInts); 如果使用微软的编译器,将会得到下面这个编译错误:error C2664: Bubblesort : cannot convert parameter 4 from int (_stdcall CCallbackTester:*)(const unsigned ch

16、ar *,const unsigned char *) to int (_stdcall *)(const unsigned char *,const unsigned char *) There is no context in which this conversion is possible 这是因为非静态成员函数有一个额外的参数:this 指针,这将迫使你在成员函数前面加上 static。函数指针目录 隐藏 基本概念指针函数和函数指针的区别关于函数指针数组的定义常用 C 变量的定义方式编辑本段 基本概念函数指针是指向函数的指针变量。因而“函数指针” 本身首先应是指针变量,只不过该指针变

17、量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C 在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后, 可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上一致的。函数指针有两个用途:调用函数和做函数的参数。函数指针的说明方法为: 数据类型标志符(指针变量名) (形参列表) ;注 1: “函数类型”说明函数的返回类型,由于“()”的优先级高于“*” ,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例int func(int x); /* 声

18、明一个函数*/ int (*f) (int x); /* 声明一个函数指针*/ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 17 页 - - - - - - - - - 5 f=func; /* 将 func 函数的首地址赋给指针f */ 赋值时函数func 不带括号,也不带参数,由于func 代表函数的首地址,因此经过赋值以后,指针f 就指向函数func(x) 的代码的首地址。注 2:函数括号中的形参可有可无,视情况而定。下面的程序说明了函数指针调用函数的方法:例一

19、、#include int max(int x,int y) return(xy?x:y); void main() int (*ptr)(int, int); int a,b,c; ptr=max; scanf(%d,%d,&a,&b); c=(*ptr)(a,b); printf(a=%d,b=%d,max=%d,a,b,c); ptr 是指向函数的指针变量,所以可把函数max()赋给 ptr 作为 ptr 的值, 即把 max()的入口地址赋给ptr,以后就可以用ptr 来调用该函数,实际上ptr 和 max 都指向同一个入口地址,不同就是ptr 是一个指针变量,不像函数名称那样是死的,

20、它可以指向任何函数,就看你像怎么做了。 在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数,不过注意,指向函数的指针变量没有+和- 运算,用时要小心。不过,在某些编译器中这是不能通过的。这个例子的补充如下。应该是这样的:1.定义函数指针类型:typedef int (*fun_ptr)(int,int); 2.申明变量,赋值: fun_ptr max_func=max; 也就是说,赋给函数指针的函数应该和函数指针所指的函数原型是一致的。例二、#include void FileFunc() printf(FileFuncn); void Edit

21、Func() printf(EditFuncn); void main() void (*funcp)(); funcp=FileFunc; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 17 页 - - - - - - - - - 6 (*funcp)(); funcp=EditFunc; (*funcp)(); 编辑本段 指针函数和函数指针的区别1,这两个概念都是简称,指针函数是指带指针的函数,即本质是一个函数。我们知道函数都又有返回类型(如果不返回值,则为无值型)

22、,只不过指针函数返回类型是某一类型的指针。其定义格式如下所示:返回类型标识符* 返回名称(形式参数表) 函数体 返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上,每一个函数, 即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。 比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。例如下面一个返回指针函数的例子:#include using namespace std; void main() float *find(float(*pionter)4,int n); stat

23、ic float score4=60,70,80,90,56,89,34,45,34,23,56,45; float *p; int i,m; coutm; p=find(score,m); for(i=0;i4;i+) cout y?x:y); void main() int (*ptr)(); int a,b,c; ptr=max; scanf(%d,%d,&a,&b); c=(*ptr)(a,b); printf(a=%d,b=%d,max=%d,a,b,c); ptr 是指向函数的指针变量,所以可把函数max()赋给 ptr 作为 ptr 的值, 即把 max()的入口地址赋给ptr,

24、以后就可以用ptr 来调用该函数,实际上ptr 和 max 都指向同一个入口地址,不同就是ptr 是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你像怎么做了。在程序中把哪个函数的地址赋给它,它就指向哪个函数。而后用指针变量调用它,因此可以先后指向不同的函数,不过注意,指向函数的指针变量没有+和-运算,用时要小心。编辑本段 关于函数指针数组的定义关于函数指针数组的定义方法,有两种:一种是标准的方法;一种是蒙骗法。第一种,标准方法: 分析: 函数指针数组是一个其元素是函数指针的数组。那么也就是说,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。根据分析:首先说明是一

25、个数组:数组名 其次,要说明其元素的数据类型指针:*数组名 . 再次,要明确这每一个数组元素是指向函数入口地址的指针:函数返回值类型(* 数组名)(). 请注意,这里为什么要把“*数组名 ”用括号扩起来呢?因为圆括号和数组说明符的优先级是等同的,如果不用圆括号把指针数组说明表达式扩起来,根据圆括号和方括号的结合方向, 那么*数组名 () 说明的是什么呢?是元素返回值类型为指针的函数数组。有这样的函数数祖吗?不知道。所以必须括起来,以保证数组的每一个元素是指针。 第二种,蒙骗法:尽管函数不是变量,但它在内存中仍有其物理地址,该地址能够赋给指针变量。获取函数方法是:用不带有括号和参数的函数名得到。

26、函数名相当于一个指向其函数入口指针常量。那么既然函数名是一个指针常量,那么就可以对其进行一些相应的处理,如强制类型转换。那么我们就可以把这个地址放在一个整形指针数组中,然后作为函数指针调用即可。完整例子:#include stdio.h 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 17 页 - - - - - - - - - 8 int add1(int a1,int b1); int add2(int a2,int b2); void main() int numa

27、1=1,numb1=2; int numa2=2,numb2=3; int (*op2)(int a,int b); op0=add1; op1=add2; printf(%d %dn,op0(numa1,numb1),op1(numa2,numb2); int add1(int a1,int b1) return a1+b1; int add2(int a2,int b2) return a2+b2; 编辑本段 常用 C 变量的定义方式a) 一个整型数( An integer)b) 一个指向整型数的指针(A pointer to an integer )c) 一个指向指针的的指针,它指向的指

28、针是指向一个整型数(A pointer to a pointer to an integer)d) 一个有 10 个整型数的数组(An array of 10 integers )e) 一个有10 个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)f) 一个指向有10 个整型数数组的指针(A pointer to an array of 10 integers )g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an ar

29、gument and returns an integer)h) 一个有 10 个指针的数组, 该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer )答案是:a) int a; / An integer b) int *a; / A pointer to an integer c) int *a; / A pointer to a pointer to an integer d) int a10; / An a

30、rray of 10 integers e) int *a10; / An array of 10 pointers to integers f) int (*a)10; / A pointer to an array of 10 integers g) int (*a)(int); / A pointer to a function a that takes an integer argument and returns an integer h) int (*a10)(int); / An array of 10 pointers to functions that take an int

31、eger argument and return an integer 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 17 页 - - - - - - - - - 9 彻底了解指针数组,数组指针,以及函数指针,以及堆中的分配规则2010-03-27 18:25一 :关于指针和堆的内存分配先来介绍一下指针:指针一种类型,理论上来说它包含其他变量的地址,因此有的书上也叫它: 地址变量。 既然指针是一个类型,是类型就有大小,在达内的服务器上或者普通的机上,都是个字节大小,里边

32、只是存储了一个变量的地址而已。不管什么类型的指针, char * ,int * ,int (*) ,string * ,float * ,都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上所有的问题都好象都变的合理了。在 C+中,申请和释放堆中分配的存贮空间,分别使用new 和 delete 的两个运算符来完成:指针类型指针变量名 =new 指针类型(初始化 );delete 指针名 ; 例如: 1、 int *p=new int(0); 它与下列代码序列大体等价:2、int tmp=0, *p=&tmp; 区别: p 所指向的变量是由库操作符new() 分配的,位于内存的堆区中

33、,并且该对象未命名。下面是关于new 操作的说明:部分引自 1、new 运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的临时对象的区别,两者完全不同: 生命期不同, 操作方法不同,临时变量对程序员是透明的 )。3、堆区是不会在分配时做自动初始化的(包括清零),所以必须用初始化式(initializer)来显式初始化。 new 表达式的操作序列如下:从堆区分配对象,然后用括号中的值初始化该对象。下面是从堆中申请数组1、申请数组

34、空间:指针变量名 =new 类型名 下标表达式 ; 注意: “下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在运行时确定。这就是堆的一个非常显著的特点,有的时候程序员本身都不知道要申请能够多少内存的时候,堆就变的格外有用。2、释放数组空间:delete 指向该数组的指针变量名; 注 意:方括号非常重要的,如果delete 语句中少了方括号,因编译器认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占空间) ,我们通常叫它“内存泄露” ,加了方括号后就转化为指向数组的指针,回收整个数组。delete 的方括号中不需要填数组元素数,系统自知。即使写了,编译器

35、也忽略。 上说过以前的delete 方括号中是必须添加个数的,后来由于很容易出错,所以后来的版本就改进了这个缺陷。下面是个例子,VC 上编译通过#include 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 17 页 - - - - - - - - - 10 using namespace std; /#include /for VC #include void main() int n; char *p; cout 请输入动态数组的元素个数n; /n 在运行时确定,可

36、输入17 p=new charn; / 申请 17 个字符(可装 8 个汉字和一个结束符)的内存空间strcpy(pc, “ 堆内存的动态分配”);/ coutpendl; delete p;/ 释放 pc 所指向的 n 个字符的内存空间return ; 通过指针使堆空间,编程中的几个可能问题动态分配失败。返回一个空指针(NULL ) ,表示发生了异常,堆资源不足,分配失败。data = new double m; / 申请空间if (data ) = 0) ,/或者 =NULL 指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p 所指的目标(变量或对象等) ,释放了

37、它所占的堆空间,而不是删除本身,释放堆空间后,成了空悬指针,不能再通过p 使用该空间,在重新给p 赋值前,也不能再直接使用p。内存泄漏( memory leak)和重复释放。new 与 delete 是配对使用的,delete只能释放堆空间。如果new 返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,而这个时候又去释放的话,会导致一个很难查出来的运行时错误。所以必须妥善保存new 返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。动态分配的变量或对象的生命期。无名变量的生命期并不依赖于建立它的作用域,比如在函数中建立

38、的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。 但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错, 所以永远不要在函数体内申请空间,让调用者释放,这是一个很差的做法。你再怎么小心翼翼也可能会带来错误。类在堆中申请内存:通过 new 建立的对象要调用构造函数,通过deletee删除对象要调用析构函数。CGoods *pc; pc=new CGoods; /分配堆空间,并构造一个无名对象/的 CGoods 对象;,. delete pc; /先析构,然后将内存空间返回给堆;堆对象的生命期并不

39、依赖于建立它的作用域,所以除非程序结束,堆对象(无名对象)的生命期不会到期,并且需要显式地用delete 语句析构堆对象,上面的堆对象在执行delete 语句时, C+自动调用其析构函数。正因为构造函数可以有参数,所以new 后面类( class)类型也可以有参数。这些参数即构造函数的参数。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 17 页 - - - - - - - - - 11 但对创建数组,则无参数,并只调用缺省的构造函数。见下例类说明:class CGoo

40、ds char Name21; int Amount; float Price; float Total_value; public: CGoods(); / 缺省构造函数。 因已有其他构造函数,系统不会再自动生成缺省构造,必须显式声明。CGoods(char* name,int amount ,float price) strcpy(Name,name); Amount=amount; Price=price; Total_value=price*amount; ,;/类声明结束下面是调用机制:void main() int n; CGoods *pc,*pc1,*pc2; pc=new C

41、Goods( “hello” ,10,118000); /调用三参数构造函数pc1=new CGoods(); /调用缺省构造函数cout ”输入商品类数组元素数”n; pc2=new CGoodsn; /动态建立数组,不能初始化,调用n 次缺省构造函数,delete pc; delete pc1; delete pc2; 申请堆空间之后构造函数运行;释放堆空间之前析构函数运行;再次强调: 由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。-下面我们再来看一下指针数组和数组指针如果你想了解指针最好理解以下的公式:(1)int*pt

42、r;/ 指针所指向的类型是int (2)char*ptr;/ 指针所指向的的类型是char 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 17 页 - - - - - - - - - 12 (3)int*ptr;/指针所指向的的类型是int* (也就是一个int * 型指针)(4)int(*ptr)3;/指针所指向的的类型是int()3 / 二维指针的声明() 指针数组: 一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。比 如 int * a10; 它

43、里边放了个int * 型变量, 由于它是一个数组,已经在栈区分配了个 (int * ) 的空间,也就是位机上是个byte,每个空间都可以存放一个int 型变量的地址,这个时候你可以为这个数组的每一个元素初始化,在,或者单独做个循环去初始化它。例子:int * a2= new int(3), new int(4) ; /在栈区里声明一个int * 数组,它的每一个元素都在堆区里申请了一个无名变量,并初始化他们为和,注意此种声明方式具有缺陷,VC 下会报错例如:int * a2= new int3, new int3 ; delete a0; delet a10; 但是我不建议这么写,可能会造成歧

44、义,不是好的风格,并且在VC 中会报错,应该写成如下:int* a2 ;a0= new int3; a1= new int3; delete a0; delete a10; 这样申请内存的风格感觉比较符合大家的习惯;由于是数组,所以就不可以delete a;编译会出警告 .delete a1; 注意这里是一个数组,不能delete ; ( ) 数组指针: 一个指向一维或者多维数组的指针;int * b=new int10;指向一维数组的指针b ; 注意, 这个时候释放空间一定要delete , 否则会造成内存泄露,b 就成为了空悬指针. int (*b2)10=new int1010; 注意,

45、这里的b2 指向了一个二维int 型数组的首地址. 注意:在这里,b2 等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。int(*b3) 30 20; /三级指针 指向三维数组的指针;名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 17 页 - - - - - - - - - 13 int (*b2) 20; /二级指针

46、;b3=new int 1 20 30; b2=new int 30 20; 两个数组都是由600 个整数组成,前者是只有一个元素的三维数组,每个元素为30 行20 列的二维数组,而另一个是有30 个元素的二维数组,每个元素为20 个元素的一维数组。删除这两个动态数组可用下式:delete b3; /删除(释放)三维数组;delete b2; /删除(释放)二维数组;再次重申:这里的b2 的类型是int (*) ,这样表示一个指向二维数组的指针。b3 表示一个指向(指向二维数组的指针)的指针,也就是三级指针. ( 3 ) 二级指针的指针看下例: int (*p)2=new (int(*)3)2

47、; p0=new int22; p1=new int22; p2=new int22; delete p0; delete p1; delete p2; delete p; 注意此地方的指针类型为int (*), 碰到这种问题就把外边的2 先去掉,然后回头先把int * p new int(*)n 申请出来,然后再把外边的附加上去;p 代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是 int (*) 代表二级指针,而int (*) 顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间,那首先要定义它的范围:( int(*)n )2 ,n 个这样的二级指针,其

48、中的每一个二级指针的最低维是个元素.(因为要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到) 。然后我们又分别为 p0,p1,p2 , 在堆里分配了空间,尤其要注意的是:在释放内存的时候一定要为p0,p1,p2, 单独delete ,否则又会造成内存泄露,在deletep 的时候一定先delete p0; delete p1 ,然后再把给p 申请的空间释放掉delete p , 这样会防止内存泄露。()指针的指针;int * cc=new (int*)10; 声明一个个元素的数组,数组每个元素都是一个int * 指针,每个元素还可以单独申请空间,因为cc 的类型是int*型的指针

49、,所以你要在堆里申请的话就要用 int * 来申请;看下边的例子(& GNU 编译器都已经通过);int * a= new int * 2;/申请两个int * 型的空间a1=new int3;/为 a的第二个元素又申请了个int 型空间 ,a1指向了此空间首地址处a0=new int4;/为 a 的第一个元素又申请了个int 型空间, a0 指向了此空间的首地址处int * b; a00=0; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 17 页 - - - -

50、- - - - - 14 a01=1; b=a0; delete a0/一定要先释放a0,a1的空间,否则会造成内存泄露.; delete a1; delete a; b+; cout*bendl; /随机数注意:因为 a 是在堆里申请的无名变量数组,所以在delete 的时候要用delete 来释放内存,但是a 的每一个元素又单独申请了空间,所以在delete a 之前要先 delete 掉a0,a1, 否则又会造成内存泄露. ()指针数组:我们再来看看第二种:二维指针数组int *(*c)3=new int *32;如果你对上边的介绍的个种指针类型很熟悉的话,你一眼就能看出来c 是个二级指

51、针 ,只不过指向了一个二维int * 型的数组而已,也就是二维指针数组。例子:int *(*b)10=new int*210;/ b00=new int100; b01=new int100; *b00=1; cout *b00endl; /打印结果为delete b00; delete b01; delete b; cout*b00endl; /打印随机数这里只为大家还是要注意内存泄露的问题,在这里就不再多说了。如果看了上边的文章,大家估计就会很熟悉,这个 b 是一个二维指针,它指向了一个指针数组第二种:int *d2; 表示一个拥有两个元素数组,每一个元素都是int * 型,这个指向指针的

52、指针: )d 不管怎样变终究也是个数组,呵呵,如果你读懂了上边的,那下边的声明就很简单了:d0=new int *10; d1=new int * 10; delete d0; delete d1; 具体的就不再多说了: )二:函数指针关于函数指针, 我想在我们可能需要写个函数,这个函数体内要调用另一个函数,可是由于项目的进度有限, 我们不知道要调用什么样的函数,这个时候可能就需要一个函数指针;名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 14 页,共 17 页 - - - - -

53、 - - - - 15 int a(); 这个一个函数的声明;ing (*b)(); 这是一个函数指针的声明;让 我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量 -只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:unsigned psize = sizeof (int (*) (); 获得函数指针的大小/ 为函数指针声明类型定义typedef int (*PFUNC) (); PFUNC 是一个函数指针,它指向的函数没有输

54、入参数,返回int。使用这个类型定义名可以隐藏复杂的函数指针语法,就我本人强烈建议我们大内弟子使用这种方式来定义;下面是一个例子,一个简单函数指针的回调(在 GNU 编译器上通过, 在 VC 上需要改变一个头文件就OK 了)#include /GNU 编译器g+ 实现using namespace std; /* /vc 的实现#include stdafx.h #include */ #define DF(F) int F() coutthis is in function #Fendl; return 0; /声明定义DF(F)替代int F() ;函数;DF(a); DF(b); DF(

55、c); DF(d); DF(e); DF(f); DF(g); DF(h); DF(i); /声明定义函数a b c d e f g h i / int (*pfunc)(); /一个简单函数指针的声明typedef int(*FUNC)(); /一个函数指针类型的声明FUNC ff = a,b,c,d,e,f,g,h,i; /声明一个函数指针数组,并初始化为以上声明的a,b,c,d,e,f,g,h,i 函数FUNC func3(FUNC vv) /定义函数func3,传入一个函数指针,并且返回一个同样类型的函数指针vv(); return vv; /*FUNC func4(int (*vv)

56、() /func3 的另一种实现名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 15 页,共 17 页 - - - - - - - - - 16 vv(); return vv; */ int main() for(int i=0;isizeof(ff)/sizeof (FUNC);i+) /循环调用函数指针FUNC r=func3(ff i ); coutr()endl; /输出返回值,只是返回了0 return 0; 到 目前为止,我们只讨论了函数指针及回调而没有去注意ANSI

57、C/C+ 的编译器规范。许多编译器有几种调用规范。如在Visual C+中,可以在函数类型前加_cdecl,_stdcall 或者_pascal 来表示其调用规范(默认为_cdecl) 。 C+ Builder 也支持 _fastcall 调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU 寄存器等)。好了,先到此为止吧,写这篇文章耗费了基本上快半天的时间了,很多事情还没有做,等 改天有时间再回来整理,所有的源程序都放在openlab3 服务器上我的目录下lib/cpp 下,大家可以去拿。不知道的

58、登陆openlab3 然后 cd chengx/lib/cpp 就可以看到了。还有很复杂的声明可能也是一种挑战比如 里的int (*(*f4()10();的声明 ,f4 是一个返回指针的函数,该指针指向了含有10 个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是Bruce Eckel 说的“从右到左的辨认规则”是一种很好的方法,值得我们去学习,感谢他:) 最 后我想应该跟大家说一下,写程序应该就象JERRY 所说的: 简单就是美; 我们应该遵循一个原则: KISS (Keep It Simple,Stupid ,尽量保持程序简单出自: Practical C programm

59、ing ) ,把自己的程序尽量的简单明了,这是个非常非常好的习惯。#include main() void search_score(); /* 定义自定义涵数类型为不返回型*/ void count_avg(); /* 定义自定义涵数类型为不返回型*/ static float a34=97,45.5,66,77,88,92.5,78.5,66,83,74.5,92,100; /* 输入 3 个学生的各自 4 门课的成绩*/ search_score(a,0); /* 调换自定义涵数显示其中一个同学的各门课成绩*/ count_avg(*a,12); /* 调换自定义涵数显示3 个同学各门课

60、的平均成绩成绩*/ /* 注重上面的 *a 其实也就是a0或者是 &a00将这行改写成count_avg(a0,12); 或者count_avg(&a00,12)也都是对的*/ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 16 页,共 17 页 - - - - - - - - - 17 void search_score(p,n) float (*p)4; /* 定义一个指针变量p,说明它是一个指向一个包含4个整型变量一维数组的指针*/ int n; /* 定义形式参数n 为整形

61、*/ int i; /* 定义用于循环的变量i */ for (i=0;i4;i+ ) /* 这里循环4 次用于打印一个同学的4 门课成绩*/ printf(%7.2f,*(*(p+n)+i); printf(n); void count_avg(p,n) float *p; int n; float *p_end; float sum = 0; float avg; p_end = p+n-1; /* 计算出最后一个数组元素的地址*/ for (;p=p_end;p+) /* 循环到最后一个元素地址就停止*/ sum += *p; avg = sum/n; printf(avg=%7.2fn,avg); /* 注重此题的意思在于输入3 个同 4 门课的成绩, 计算出平均值和显示其中一个同学的4门课成绩,此例是对多维数组指针和多维数组的指针作为涵数参数传递的总结,认真联系和体会可以很好的了解多维数组指针的概念到底是什么!名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 17 页,共 17 页 - - - - - - - - -

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号