几种函数的调用方式.docx_第1页
几种函数的调用方式.docx_第2页
几种函数的调用方式.docx_第3页
几种函数的调用方式.docx_第4页
几种函数的调用方式.docx_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

几种函数的调用方式_cdecl 是C DECLaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。 _stdcall 是StandardCall的缩写,是C+的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。 PASCAL 是Pascal语言的函数调用方式,也可以在C/C+中使用,参数压栈顺序与前两者相反。返回时的清栈方式忘记了 _fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。 _thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C+编译器使用eax。返回方式和_stdcall相当。 _fastcall 和 _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。 C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C+也一样,但是默认的调用方式可以在IDE环境中设置。 带有可变参数的函数必须且只能使用_cdecl方式,例如下面的函数: int printf(char * fmtStr, .); int scanf(char * fmtStr, .); */几种调用约定的区别 编辑本段几种调用约定的区别_cdecl _fastcall与 _stdcall,三者都是调用约定(Calling convention),它决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法。 1、_stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈, 2、_cdecl是C和C程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用_cdecl的转换方式。 3、_fastcall调用约定:它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。 4、thiscall仅仅应用于C+成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。 5、nakedcall采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。 编辑本段名字修饰约定1、修饰名(Decoration name):C或者C+函数在内部(编译和链接)通过修饰名识别 2、C编译时函数名修饰约定规则: _stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个符号和其参数的字节数,格式为_functionnamenumber,例如 :function(int a, int b),其修饰名为:_function8 _cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。 _fastcall调用约定在输出函数名前加上一个符号,后面也是一个符号和其参数的字节数,格式为functionnamenumber。 编辑本段设置方法1 可以直接在代码中写 _cdecl 等调用约定 2 在MS-VC+6.0中,调用约定也可以通过工程设置:Setting.C/C+ Code Generation项进行选择,缺省状态为_cdecl。名字修饰约定。静态函数函数调用的结果不会访问或者修改任何对象(非static)数据成员,这样的成员声明为静态成员函数比较好。且如果static int func(.)不是出现在类中,则它不是一个静态成员函数,只是一个普通的全局函数,只不过由于 static 的限制,它只能在文件所在的编译单位内使用,不能在其它编译单位内使用。静态成员函数的声明除了在类体的函数声明前加上关键字static,以及不能声明为const或者volatile之外,与非静态成员函数相同。出现在类体之外的函数定义不能制定关键字static。 静态成员函数没有this指针。 = Static关键字 在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。 用static声明的方法是静态方法,在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员 #静态方法不再是针对于某个对象调用,所以不能访问非静态成员 可以通过对象引用或类名(不需要实例化)访问静态成员 java: public class Cat private static int sid = 0; private String name; int id; Cat(String name) = name; id = sid+; public void info() System.out.println (My name is +name+ No.+id); public static void main(String arg) Cat.sid = 100; Cat mimi = new Cat(mimi); mimi.sid = 2000; Cat pipi = new Cat(pipi); = 如果某些成员函数只访问静态数据成员,那么最好把他们声明为静态的成员函数,因为这样不需要特定的对象就可以访问这些成员变量了。虚函数目录定义 作用 示例 1. 虚函数的实例 2. 条件其他信息 c+的虚函数 1. 一, 什么是虚函数 2. 二, 虚函数是如何做到的 3. 三, 以一段代码开始 4. CallVirtualFun方法最后的说明:定义 作用 示例 1. 虚函数的实例 2. 条件其他信息 c+的虚函数 1. 一, 什么是虚函数 2. 二, 虚函数是如何做到的 3. 三, 以一段代码开始 4. CallVirtualFun方法最后的说明:展开编辑本段定义虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,在基类的类定义中定义虚函数的一般形式: virtual 函数返回值类型 虚函数名(形参表) 函数体 编辑本段作用虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。 当程序发现虚函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。 (2010.10.28 注:下行语义容易使人产生理解上的偏差,实际效果应为: 如存在:Base - Derive1 - Derive2 及它们所拥有的虚函数func() 则在访问派生类Derive1的实例时,使用其基类Base及本身类型Derive1,或被静态转换的后续派生类Derive2的指针或引用,均可访问到Derive1所实现的func()。) 动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式: 指向基类的指针变量名-虚函数名(实参表) 或 基类对象的引用名. 虚函数名(实参表) 虚函数是C+多态的一种表现 例如:子类继承了父类的一个函数(方法),而我们把父类的指针指向子类,则必须把父类的该函数(方法)设为virtual(虚函数)。 使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。 如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 我们把这样的函数(方法)称为纯虚函数。 如果一个类包含了纯虚函数,称此类为抽象类 。 编辑本段示例虚函数的实例#include class Cshape public: void SetColor( int color) m_nColor=color; void virtual Display( void) coutCshapeendl; private: int m_nColor; ; class Crectangle: public Cshape public: void virtual Display( void) coutCrectangleendl; ; class Ctriangle: public Cshape void virtual Display( void) coutCtriangleendl; ; class Cellipse :public Cshape public: void virtual Display(void) coutCellipseendl; ; void main() Cshape obShape; Cellipse obEllipse; Ctriangle obTriangle; Crectangle obRectangle; Cshape * pShape4= &obShape, &obEllipse,&obTriangle, & obRectangle ; for( int I= 0; IDisplay( ); 本程序运行结果: Cshape Cellipse Ctriangle Crectangle 条件所以,从以上程序分析,实现动态联编需要三个条件: 1、 必须把动态联编的行为定义为类的虚函数。 2、 类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来。 3、 必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。 编辑本段其他信息定义虚函数的限制: (1)非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。实际上,优秀的程序员常常把基类的析构函数定义为虚函数。因为,将基类的析构函数定义为虚函数后,当利用delete删除一个指向派生类定义的对象指针时,系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时,只调用基类的析构函数。 (2)只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。 (3)当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。 (4)如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现这种同名函数。 虚函数联系到多态,多态联系到继承。所以本文中都是在继承层次上做文章。没了继承,什么都没得谈。 编辑本段c+的虚函数下面是对C+的虚函数这玩意儿的理解。 一, 什么是虚函数(如果不知道虚函数为何物,但又急切的想知道,那你就应该从这里开始) 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。下面来看一段简单的代码 class A public: void print() cout”This is A”endl; ; class B:public A public: void print() cout”This is B”print(); p2-print(); 运行一下看看结果,哟呵,蓦然回首,结果却是两个This is A。问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数 class A public: virtual void print() cout”This is A”endl; /现在成了虚函数了 ; class B:public A public: void print() cout”This is B”endl; /这里需要在前面加上关键字virtual吗? (可加可不加,参数和签名方式相同); 毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。 现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。 现在来消化一下,我作个简单的总结,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。 (多态的实现)二, 虚函数是如何做到的(如果你没有看过Inside The C+ Object Model这本书,但又急切想知道,那你就应该从这里开始) 虚函数是如何做到因对象的不同而调用其相应的函数的呢?现在我们就来剖析虚函数。我们先定义两个类 class A /虚函数示例代码 public: virtual void fun()cout1endl; virtual void fun2()cout2endl; ; class B:public A public: void fun()cout3endl; void fun2()cout4fun(); 毫无疑问,调用了A:fun(),但是A:fun()是如何被调用的呢?它像普通函数那样直接跳转到函数的代码处吗?No,其实是这样的,(虚函数的实现过程)首先是取出vptr的值,这个值就是vtbl的地址,再根据这个值来到vtbl这里,由于调用的函数A:fun()是第一个虚函数,所以取出vtbl第一个slot里的值,这个值就是A:fun()的地址了,最后调用这个函数。现在我们可以看出来了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里装着对应类的虚函数地址,所以这样虚函数就可以完成它的任务。 而对于class A和class B来说,他们的vptr指针存放在何处呢?其实这个指针就放在他们各自的实例对象里。由于class A和class B都没有数据成员,所以他们的实例对象里就只有一个vptr指针。通过上面的分析,现在我们来实作一段代码,来描述这个带有虚函数的类的简单模型。 #include using namespace std; /将上面“虚函数示例代码”添加在这里 int main() void (*fun)(A*); A *p=new B; long lVptrAddr; memcpy(&lVptrAddr,p,4); memcpy(&fun,reinterpret_cast(lVptrAddr),4); fun(p); delete p; system(pause); 用VC或Dev-C+编译运行一下,看看结果是不是输出3,如果不是,那么太阳明天肯定是从西边出来。现在一步一步开始分析 void (*fun)(A*); 这段定义了一个函数指针名字叫做fun,而且有一个A*类型的参数,这个函数指针待会儿用来保存从vtbl里取出的函数地址 A* p=new B; new B是向内存(内存分5个区:全局名字空间,自由存储区,寄存器,代码空间,栈)自由存储区申请一个内存单元的地址然后隐式地保存在一个指针中.然后把这个地址赋值给A类型的指针P. . long lVptrAddr; 这个long类型的变量待会儿用来保存vptr的值 memcpy(&lVptrAddr,p,4); 前面说了,他们的实例对象里只有vptr指针,所以我们就放心大胆地把p所指的4bytes内存里的东西复制到lVptrAddr中,所以复制出来的4bytes内容就是vptr的值,即vtbl的地址 现在有了vtbl的地址了,那么我们现在就取出vtbl第一个slot里的内容 memcpy(&fun,reinterpret_cast(lVptrAddr),4); 取出vtbl第一个slot里的内容,并存放在函数指针fun里。需要注意的是lVptrAddr里面是vtbl的地址,但lVptrAddr不是指针,所以我们要把它先转变成指针类型 fun(p); 这里就调用了刚才取出的函数地址里的函数,也就是调用了B:fun()这个函数,也许你发现了为什么会有参数p,其实类成员函数调用时,会有个this指针,这个p就是那个this指针,只是在一般的调用中编译器自动帮你处理了而已,而在这里则需要自己处理。 delete p; 释放由p指向的自由空间; system(pause); 屏幕暂停; 如果调用B:fun2()怎么办?那就取出vtbl的第二个slot里的值就行了 memcpy(&fun,reinterpret_cast(lVptrAddr+4),4); 为什么是加4呢?因为一个指针的长度是4bytes,所以加4。或者memcpy(&fun,reinterpret_cast(lVptrAddr)+1,4); 这更符合数组的用法,因为lVptrAddr被转成了long*型别,所以+1就是往后移sizeof(long)的长度 三, 以一段代码开始#include using namespace std; class A /虚函数示例代码2 public: virtual void fun() coutA:funendl; virtual void fun2()coutA:fun2endl; ; class B:public A public: void fun() coutB:funendl; void fun2() coutB:fun2*fun)(); fun = &A:fun2; (p-*fun)(); delete p; system(pause); 你能估算出输出结果吗?如果你估算出的结果是A:fun和A:fun2,呵呵,恭喜恭喜,你中圈套了。其实真正的结果是B:fun和B:fun2,如果你想不通就接着往下看。给个提示,&A:fun和&A:fun2是真正获得了虚函数的地址吗? 首先我们回到第二部分,通过段实作代码,得到一个“通用”的获得虚函数地址的方法 #include using namespace std; /将上面“虚函数示例代码2”添加在这里 void CallVirtualFun(void* pThis,int index=0) void (*funptr)(void*); long lVptrAddr; memcpy(&lVptrAddr,pThis,4); memcpy(&funptr,reinterpret_cast(lVptrAddr)+index,4); funptr(pThis); /调用 int main() A* p=new B; CallVirtualFun(p); /调用虚函数p-fun() CallVirtualFun(p,1);/调用虚函数p-fun2() system(pause); CallVirtualFun方法现在我们拥有一个“通用”的CallVirtualFun方法。 这个通用方法和第三部分开始处的代码有何联系呢?联系很大。由于A:fun()和A:fun2()是虚函数,所以&A:fun和&A:fun2获得的不是函数的地址,而是一段间接获得虚函数地址的一段代码的地址,我们形象地把这段代码看作那段CallVirtualFun。编译器在编译时,会提供类似于CallVirtualFun这样的代码,当你调用虚函数时,其实就是先调用的那段类似CallVirtualFun的代码,通过这段代码,获得虚函数地址后,最后调用虚函数,这样就真正保证了多态性。同时大家都说虚函数的效率低,其原因就是,在调用虚函数之前,还调用了获得虚函数地址的代码。回调函数百科名片 回调函数回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。(函数的参数一般为某种数据类型的变量,比如intcharfloat,但很少是函数指针的,但回调函数正是这个特点)更简单的阐述WINDOWS本身会调用它,比如WIN程序中的WndProc就是个CALLBACK函数(发送给窗口的消息就发送给它啦),你只需要在这个函数中写上你需要处理的东西,如果你自己想调用CALLBACK函数一般不要主动去调用它,比如wndproc你应该用sendmessage让WINDOWS去调用它。(也就是说这个函数由系统调用,而且不止调用一次,是系统不断地调用,有消息就调用。是一个循环不止的过程)也可以这么理解:(消息机制吗)“事件”发生时,响应事件发生的函数(消息处理函数),叫回调函数。发生一次自动调用一次。“事件”- 例如键盘,鼠标,定时器,窗体变化等。LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);参数传递事件普通函数,遇到 调用语句 才执行一次。回调函数的一般模式回调函数首先就得有一个函数指针.typedef void (*callback_fun)(char *ptr); /这个很重要其次,得有供函数指针指向的函数void output_int(char *x) cout output_int: x endl; void output_int(char *x) cout output_int: x endl; void output_double(char *y) cout output_double y endl; void output_double(char *y) cout output_double y endl; 这里其实最开始遇到了一点问题,呵呵,我最初定义的参数类型为void,因为我希望代码能够更具通用性,但是后来发现,函数指针指向的函数的参数类型必须匹配才行.函数指针与函数都有了,那么还得有将函数指针与函数联系起来的地方,如果直接在main函数里赋值,感觉太简单了,没有挑战性,于是我再写了一个函数template bool QueryCallback(callback_fun &cb,T temp)/这里得注意,回调函数的函数指针必须得传引用传地址会报错,而如果作为普通参数传进来,会因为作用域的问题,在函数结束后被销毁.其实这里存有疑惑,为什么不能传指针,难道说typedef的类型,在C+里面只能传引用? float result = temp - (int)temp; if(result 0.000001) cb = output_double; return true; else if( result 0.000001 ) cb = output_int; return true; else return false; templatebool QueryCallback(callback_fun &cb,T temp)/这里得注意,回调函数的函数指针必须得传引用传地址会报错,而如果作为普通参数传进来,会因为作用域的问题,在函数结束后被销毁.其实这里存有疑惑,为什么不能传指针,难道说typedef的类型,在C+里面只能传引用? float result = temp - (int)temp; if(result 0.000001) cb = output_double; return true; else if( result 0.000001 ) cb = output_int; return true; else return false; main函数就很简单了view plaincopy to clipboardprint?int main() char temp256; int a = 20; callback_fun cb; if(QueryCallback(cb,a)=false) return -1; sprintf(temp,%d,a); cb(temp); double b = 33.333333; if(QueryCallback(cb,b)=false) return -1; sprintf(temp,%f,b); cb(temp); return 0; 目录回调函数实现的机制是 为什么要使用回调函数 简单的回调函数实现 1. 代码实现 2. 调用约定回调函数实现的机制是 为什么要使用回调函数 简单的回调函数实现 1. 代码实现 2. 调用约定展开编辑本段回调函数实现的机制是(1)定义一个回调函数; (2)提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者; (3)当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。 编辑本段为什么要使用回调函数因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。 如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。 回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer() API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。 另一个使用回调机制的API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。 不管怎么说,回调函数是继续自C语言的,因而,在C+中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C+中应使用虚拟方法或函数符(functor),而不是回调函数。 编辑本段简单的回调函数实现代码实现下面创建了一个sort.dll的动态链接库,它导出了一个名为CompareFunction的类型-typedef int (_stdcall *CompareFunction)(const byte*, const byte*),它就是回调函数的类型。另外,它也导出了两个方法:Bubblesort()和Quicksort(),这两个方法原型相同,但实现了不同的排序算法。 void DLLDIR _stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc); void DLLDIR _stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc); 这两个函数接受以下参数: byte * array:指向元素数组的指针(任意类型)。 int size:数组中元素的个数。 int elem_size:数组中一个元素的大小,以字节为单位。 CompareFunction cmpFunc:带有上述原型的指向回调函数的指针。 这两个函数的会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。对编写者来说,不必介意函数在何处实现,或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值(库的编写者和使用者都必须遵守这个约定): (单个元素与数组的比较)-1:如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。 0:如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所谓。 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) /两个相比较的元素相交换 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()函数。下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串: int _stdcall CompareInts(const byte* velem1, const byte* velem2) int elem1 = *(int*)velem1; /注意这种好方法int elem2 = *(int*)velem2; if(elem1 elem2) return 1; return 0; int _stdcall CompareStrings(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, 2109, 1098; cout Before sorting ints with Bubblesortn; for(i=0; i 5; i+) cout array n; Bubblesort(byte*)array, 5, sizeof(array0), &CompareInts); cout After the sortingn; for(i=0; i 5; i+) cout array n; const char str510 = estella,danielle,crissy,bo,angie; cout Before sorting strings with Quicksortn; for(i=0; i 5; i+) cout str n; Quicksort(byte*)str, 5, 10, &CompareStrings); cout After the sortingn; for(i=0; i 5; i+) cout str n; return 0; 如果想进行降序排序(大元素在先),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。 调用约定上面的代码中,可在函数原型中找到_stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以_stdcall标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32 API(除了个别接受可变参数的除外)都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是Pascal的标准约定。但在C/C+中,调用约定是调用者负责清理堆栈,而不是被调用函数;为强制函数使用C/C+调用约定,可使用_cdecl。另外,可变参数函数也使用C/C+调用约定。 Windows操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。 如果你不喜欢_stdcall,还可以使用CALLBACK宏,它定义在windef.h中: #define CALLBACK _stdcallor #define CALLBACK PASCAL /而PASCAL在此被#defined成_stdcall 作为回调函数的C+方法 因为平时很可能会使用到C+编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码: class CCallbackTester public: int CALLBACK CompareInts(con

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论