[C++] 函数指针的学习与运用
什么是函数指针
函数指针:如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。
函数指针变量定义形式
函数返回值类型 (*函数指针变量名) (函数参数列表);
如何用函数指针调用函数
int max(int x, int y); /*声明一个函数*/ int (*pMax) (int x, int y); /*定义一个函数指针*/ pMax = max; /*将max函数的首地址赋给指针变量p*/
使用实例
#include <iostream> using namespace std; int max(int x, int y); int main(int argc, char const *argv[]) { int (*pMax)(int, int); pMax = max; int max_val = pMax(10, 100); cout << max_val << endl; return 0; } int max(int x, int y) { return x>y ? x : y; }
为某一函数型定义其函数指针类型
定义语句
typedef (*函数指针类型名)(函数参数列表); /*e.g*/ typedef int(*max_func_type)(int, int);
这个就可以用像定义普通变量一样,定义一个函数指针变量了,前面的例子可以改成如下:
#include <iostream> using namespace std; int max(int x, int y); typedef int(*max_func_type)(int, int); int main(int argc, char const *argv[]) { max_func_type pMax = max; /*这就很符合平常定义变量的语法*/ int max_val = pMax(10, 100); cout << max_val << endl; return 0; } int max(int x, int y) { return x>y ? x : y; }
函数指针的高级使用
开发过程中,需要用到某个dll库文件时,但只有说明文档,而开发用的lib文件和头文件都丢失了,这种情况下怎么办?方法就是:
- 使用HMODULE hModule = LoadLibrary(LPCTSTR lpFileName)加载这个dll文件,
- 再用GetProcAddress(hModule, "函数名")获取需要调用的函数的内存地址(函数指针)。
还是前面的例子,把max()函数封装到一个dll里,代码如下:
/*CMakeLists.txt*/ cmake_minimum_required(VERSION 3.0) project(max) FILE(GLOB SC "*.cpp" "*.h") add_library(${PROJECT_NAME} SHARED ${SC})
/*max.h*/ #ifndef __MAX_H__ #define __MAX_H__ extern "C" __declspec(dllexport) int __cdecl max(int x, int y); /*注意这里*/ #endif // !__MAX_H__
上面需要注意的那一行,为了让max函数在dll文件的导出表里的名称就是“max”,其实我有点偷懒了,正确的声明应该是把__cdecl 换成 __stdcall,这样才符合GetProcAddress()返回的函数指针对应的函数调用规范,但用了__stdcall后,dll中的导出函数名就不是简单的“max”了。至于函数调用规范的内容,就可能开另外一个单章进行总结了。有兴趣的可以网上搜,自个进行了解。
/*max.cpp*/ #include "max.h" int __cdecl max(int x, int y) { return x > y ? x : y; }
在另外一个项目中使用上面封装的dll,注意只要dll文件,lib文件与头件别也复制过来了:
/*CMakeLists.txt*/ cmake_minimum_required(VERSION 3.0) project(LoadDll) FILE(GLOB SC "*.cpp" "*.h") add_executable(${PROJECT_NAME} ${SC})
/*main.cpp*/ #include <Windows.h> #include <iostream> using namespace std; typedef int(*max_func_type)(int, int); int main(int argc, char** argv) { HMODULE hDll = LoadLibrary("max.dll"); /*加载dll文件*/ if (!hDll) { cerr << "Load max.dll error" << endl; } max_func_type pMax = (max_func_type)GetProcAddress(hDll, "max"); /*获取函数地址,赋值函数指针变量pMax*/ if (!pMax) { cerr << "function max not found" << endl; } else { int ret = pMax(100, 10); /*通过函数指针调用max函数*/ cout << "result: " << ret << endl; } FreeLibrary(hDll); return 0; }
PS:其实还有一个进阶的用法, 就是已经有一段编译好,且可独立运行的代码(目标代码),先加载到内存,把对应的内存地址赋值给一个函数指针,方便后面的调用,但这里涉及到一个内存权限问题,一般这样的加载只有可读写权限,但调用是需要执行权限的,这就需要调用系统API对这段内存进行赋权!!