C语言的深度挖掘(三)函数指针与动态链接库西安电子科技大学计算机学院李龙海指向函数的指针在C++中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针”。函数指针的定义格式:返回类型(*指针变量)(形参表)。/*fp可以指向返回值类型为double,有一个int型参数的任何函数。*/double(*fp1)(int);int*(*fp2)(char[],int);doublef(intx){……}int*g(char*s,intlen){……}intmain(){fp1=f;//或为:fp=&fdoubled=fp1(5);fp2=g;fp1=g;//Error!fp2=f;//Error!……}指向函数的指针也可以用typedef为函数指针类型取一个名字,然后再用该函数指针类型来定义指针变量:typedef返回类型(*函数指针类型名)(形参表)。typedefdouble(*FP)(int);FPfp;可以通过一个函数指针来调用它所指向的函数,调用格式为:(*函数指针变量)(实参表);或者函数指针变量(实参表);注意不要将函数指针与返回指针的函数搞混了。int*f(int,char*);int(*f)(int,char*);向函数传递函数C++中允许在调用一个函数时把一个函数作为参数传给被调用函数,这时,被调用函数的形参定义为一个函数指针类型,调用时的实参为一个函数的地址。doubleintegrate(double(*f)(double),doublea,doubleb){…计算函数f在区间[a,b]上的定积分}integrate(sin,0,1);Integrate(cos,1,2);C++虚拟函数与虚拟函数表消息映射表的实现AddressMessageMemoryFunction1Function2‘a'‘b'‘w'NULLFunction3获得输入消息扫描消息映射表跳转到相应函数消息映射表的实现消息映射表的实现回调函数回调函数是由程序员自己定义的但不是由自己显式调用的函数,其调用者往往是框架、容器、服务器、操作系统等,当然也可以是自己的程序。程序员往往将回调函数的地址传递给调用者从而实现调用。例一:DOS中设置中断处理函数例二:在Windows中创建线程例三:在Windows中设置消息钩子WindowsAPI中的其它例子分别编译与链接(Linking)大多数高级语言都支持分别编译,程序员可以显式地把程序划分为独立的模块或文件,然后每个独立部分分别编译。在编译之后,由链接器把这些独立的片段(称为编译单元)“粘接到一起”。(想想这样做有什么好处?)在C/C++中,这些独立的编译单元包括obj文件(一般的源程序编译而成)、lib文件(静态链接的函数库)、dll文件(动态链接的函数库)等。分别编译与链接编译器a.cppb.c编译器a.objb.obj静态链接库(libc.lib…)动态链接库(user32.dll…)链接器xx.exeproject链接器的主要工作1.将分散的数据和机器代码收集并合成一个单一的可加载并可执行的文件;2.符号解析:由多个程序模块(源程序)构建一个可执行程序时,模块之间的相互引用通过符号进行。程序也可以通过符号来引用代码库(lib库)中的功能。符号解析就是将符号引用和符号定义关联起来。3.地址重定位:编译器产生的各个目标文件(obj文件)中数据和代码的地址一般都是从0开始。因此如果一个程序包含多个目标文件时就会产生地址重叠。重定位就是为每个目标文件重新定义加载地址,并修改相应的代码和数据以反映这种变化。静态链接与动态链接静态链接方式:在程序执行之前完成所有的组装工作,生成一个可执行的目标文件(EXE文件)。动态链接方式:在程序已经为了执行被装入内存之后完成链接工作,并且在内存中一般只保留该编译单元的一份拷贝。静态链接库与动态链接库可以将静态链接库或动态链接库看成是一种仓库,它提供给你一些已经编译成机器代码的可以直接拿来用的数据、函数或类,它们是实现代码共享的一种方式。静态链接库中的机器代码和数据都被直接包含在最终生成的EXE文件中动态链接库的内容不必被包含在最终的EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。VC6.0中使用lib库的三种方法1.利用编译器指令#pragmacomment(lib,…)2.将lib库文件所在目录设置在VC环境中3.将lib库文件设置在工程中。动态链接库1.动态链接库(DynamicLinkLibrary)是一个可以被其它程序共享的程序模块,其中封装了一些可以被共享的数据、函数和资源。2.如果一个可执行程序使用了一个DLL,当可执行程序运行时,操作系统会把DLL加载到内存,并解析可执行程序对该DLL的符号引用,使得可执行程序能够调用DLL中的函数功能。3.扩展名一般是dll,也有可能是fon、ocx、drv、sys等,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由其他应用程序直接或间接调用。使用动态链接库的优点1.DLL文件与EXE文件独立,只要输出接口不变,更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性。2.被多个应用程序共享时,在内存中只有一份拷贝,因而更加节省内存3.可以在多种编程语言之间共享代码动态链接库的创建1.第一步:通过编译开关(编译参数)将编译器和链接器设置为输出DLL状态;或者在VC6中用向导创建一个“Win32DynamicLinkLibrary”工程2.第二步:将程序中的一些函数设置为导出函数,设置导出函数的方法有两种:利用.DEF文件利用VC扩展关键字__declspec(dllexport)3.VC在生成DLL的同时还会产生一个导入库(importlib),该静态库中列出了DLL输出的所有函数和变量的名称,但不包含任何实现代码。4.在生成的DLL文件头部,还包含一个输出符号表,其中记录了该DLL输出的所有函数和变量的名称及相对偏移。利用__declspec(dllexport)导出函数1.在函数定义或函数声明的最前面加上关键字__declspec(dllexport)即可将该函数设置为导出函数2.为了键入方便,一般定义一个宏:#defineEXPORTextern”C”__declspec(dllexport)EXPORTintadd(intx,inty){returnx+y;}利用.DEF文件导出函数1.在“Win32DynamicLinkLibrary”工程中添加一个后缀名为.def的文本文件,该文件一般采用如下格式:LIBRARYDLL文件名DESCRIPTION“用途描述”EXPORTSfuncionA@1funcionB@2funcionC@32.利用DEF文件导出函数的一个优点是将来可以按序号查找该函数,比一般的按名字查找更高效。动态链接库的两种链接方法1.装载时动态链接(Load-timeDynamicLinking)2.运行时动态链接(Run-timeDynamicLinking)运行时动态加载和链接DLL文件1.利用WindowsAPI函数LoadLibrary,GetProcAddress和FreeLibrary实现运行动态加载、链接和释放DLLLoadLibrary查找DLL的路径1.应用程序被加载的目录2.当前子目录(默认子目录)(GetCurrentDirectory)3.Winodws\System32子目录4.Windows子目录5.环境变量PATH中标识的子目录静态加载和链接DLL中的函数1.静态方式的特点是由编译器利用导入库(.lib)将DLL的加载、链接和卸载代码直接添加到.exe文件中。2.采用静态加载方式的优点:调用程序更简单,易读运行效率高3.采用静态加载方式的缺点:不够灵活,无法选择加载时机,无法更换DLL文件动态链接库的应用举例1.所有的WindowsAPI函数都是以动态链接库导出函数形式提供的。大部分API函数都存放在kernel32.dll、user32.dll和gdi32.dll三个动态库中,相应的导入库为kernel32.lib、user32.lib和gdi32.lib2.应用软件的插件技术3.每个Windows驱动程序本质上都是动态链接库操作系统的作用与功能操作系统裸机应用程序编程接口终端用户操作接口WindowsAPI1.WindowsAPI(ApplicationProgrammingInterface)是Windows操作系统为应用程序设计提供的一组函数(过程)调用接口。应用程序通过调用这些函数就可以获得操作系统的底层服务,访问系统管理的各种软、硬件资源。2.所有的WindowsAPI函数都是以动态链接库导出函数形式提供的。大部分API函数都存放在kernel32.dll、user32.dll和gdi32.dll三个动态库中,相应的导入库为kernel32.lib、user32.lib和gdi32.lib3.WindowsAPI函数的使用说明在MSDN中可以找到()PlatformSDK1.PlatformSoftwareDevelopmentKit(SDK)是微软免费提供的开发Windows应用程序的开发包,它包含了如下内容:与WindowsAPI相关的C头文件(windows.h等)与WindowsAPI相关的导入库(kernel32.lib等)WindowsAPI的帮助文档C++编译器和链接器调试工具、分析工具2.VC集成开发环境中已经集成了SDK(一般版本较老)。SDK也可以从微软网站上免费下载。MFC与WindowsAPI操作系统WindowsAPIMicrosoftFoundationClasses(MFC)面向过程的接口面向对象的接口MFC以C++源码的形式提供给程序员.NET与WindowsAPI基于DLL的消息映射获得输入消息以输入消息为参数调用当前消息处理函数消息处理函数返回1?移向下一个消息处理函数YN可扩展Web服务器的实现客户机……../htmlxxx.htmlyyy.htmlzzz.html可扩展Web服务器的实现HTTP协议模块多线程管理缓存管理安全管理……MoudleURL1.dll2.dlla.sab.sac.sa...3.dllOnGet()1.dll可扩展Web服务器容器与组件容器组件1组件2组件3组件4容器与组件1.容器实现了所有组件所需的通用服务,并且负责管理组件的生命周期。2.组件对容器的功能进行扩展,它是一种没有生命力的被动软件模块。3.组件是一组函数(子程序)的集合体,这些函数由容器在适当的时机调用,我们称这些函数为回调函数4.容器提供的通用服务也以函数调用的形式给出5.组件提供给容器的调用接口,以及容器提供给组件的调用接口必须事先约定好“好莱坞模式”回调函数(callbackfunction)容器/组件模型好莱坞模式摒弃以自我为中心编程理念