1、函数指针
函数指针用于指向一个函数
函数名是函数体的入口地址
1)可通过函数类型定义函数指针: FuncType* pointer;
2)也可以直接定义:type (*pointer)(parameter list);
pointer为函数指针变量名
type为指向函数的返回值类型
parameter list为指向函数的参数类型列表
例:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int Add(int a, int b)
{
int c = 0;
c = a + b;
return c;
}
int main()
{
//直接调用
int sum = Add(1, 2);//函数名称代表函数的入口地址,就是一个函数指针 ADD
cout << "函数之和sum:" << sum << endl;
//声明一个函数类型 间接调用。
{
typedef int(MyFuncType)(int a, int b);
MyFuncType *myFuncVar = NULL;
myFuncVar = Add;
int sum1=myFuncVar(2, 4);
cout << "函数之和sum1:" << sum1 << endl;
}
//声明一个函数指类型。
{
typedef int(*PFuncType)(int a, int b);//声明函数指针 C编译器不会分配内存
PFuncType myFunc = NULL;
myFunc = &Add;
int sum2 = myFunc(3, 5);
cout << "函数之和sum2:" << sum2 << endl;
}
//定义一个函数指针,用来只想一个函数的入口地址
{
int(*MYPFUNC)(int a, int b);
MYPFUNC = Add;
int sum3=MYPFUNC(10, 5);
cout << "函数之和sum3:" << sum3 << endl;
}
return 0;
}
2、函数指针做函数参数
函数指针做函数参数 :当函数指针 做为函数的参数,传递给一个被调用函数,被调用函数就可以通过这个指针调用外部的函数,这就形成了回调。
例:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int Add(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add Done!\n");
return c;
}
int Add2(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add2 Done!\n");
return c;
}
int Add3(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add3 Done!\n");
return c;
}
int Add4(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add4 Done!\n");
return c;
}
int myMainFunc(int(*myFuncAdd)(int a, int b))
{
int sum= myFuncAdd(10, 20);
return sum;
}
int main()
{
myMainFunc(Add);
myMainFunc(Add2);
myMainFunc(Add3);
myMainFunc(Add4);
return 0;
}
运行结果:
思路剖析:
1、指针函数的用法将函数的调用和函数的实现有效分离,实现解耦。
2、int myMainFunc(int(*myFuncAdd)(int a, int b))是一个库中的函数,就只有使用回调了,通过函数指针参数将外部函数地址传入来实现调用
3、函数 Add 的代码作了修改,也不必改动库的代码,就可以正常实现调用,便于程序的维护和升级。
3、函数指针做函数参数在实际项目中的运用。
第一种 正向调用
例:
有个socketclient.dll,用dependency walker可以查看到dll的方法。
第一步 Load DLL
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCSTR sss = pathStr.c_str();
hInstance =LoadLibrary("socketclient.dll");
if (hInstance == NULL)
{
printf("LoadLibrary() 失败");
return 0;
}
在这里注意编码的问题。
右键属性如下图:
将字符集修改为使用多字节字符集。
如果使用Unicode字符集则需要做修改,Unicode时,LoadLibrary的参数是LPCWSTR。所以需要做转化。
LPCWSTR stringToLPCWSTR(string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t)*(orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
int main()
{
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCTSTR sss = stringToLPCWSTR(pathStr);
hInstance =LoadLibrary(sss);
if (hInstance == NULL)
{
printf("LoadLibrary() 失败");
return 0;
}
}
第二步 声明函数指针类型
//客户端初始化 获取handle上下
//声明一个函数指针类型
typedef int(*CltSocketInit)(void **handle /*out*/);
//客户端发报文
typedef int(*CltSocketSend)(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/);
//客户端收报文
typedef int(*CltSocketRev)(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);
//客户端释放资源
typedef int(*CltSocketDestory)(void *handle/*in*/);
第三步 实现函数的调用。
CltSocketInit cltSocketInit = (CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit");
if (cltSocketInit == NULL)
{
return 0;
}
CltSocketSend cltSocketSend = (CltSocketSend)::GetProcAddress(hInstance, "cltSocketSend");
CltSocketRev cltSocketRev = (CltSocketRev)::GetProcAddress(hInstance, "cltSocketRev");
CltSocketDestory cltSocketDestory = (CltSocketDestory)::GetProcAddress(hInstance, "cltSocketDestory");
//执行动态库函数调用
unsigned char buf[128];
int buflen = 128;
unsigned char outbuf[4096];
int outbuflen = 4096;
strcpy((char *)buf, "aaaaaaaaaafffffffffdddddd");
buflen = 9;
void *handle = NULL;
int ret = 0;
ret = cltSocketInit(&handle);
ret = cltSocketSend(handle, buf, buflen);
ret = cltSocketRev(handle, outbuf, &outbuflen);
ret = cltSocketDestory(handle);
if (memcmp(buf, outbuf, outbuflen) == 0)
{
printf("发送数据和接受的数据一样 ok\n");
}
else
{
printf("发送数据和接受的数据不一样\n");
}
完整代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <iostream>
using namespace std;
//客户端初始化 获取handle上下
//声明一个函数指针类型
typedef int(*CltSocketInit)(void **handle /*out*/);
//客户端发报文
typedef int(*CltSocketSend)(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/);
//客户端收报文
typedef int(*CltSocketRev)(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);
//客户端释放资源
typedef int(*CltSocketDestory)(void *handle/*in*/);
LPCWSTR stringToLPCWSTR(string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t)*(orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
int main()
{
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCTSTR sss = stringToLPCWSTR(pathStr);
hInstance =LoadLibrary(sss);
if (hInstance == NULL)
{
printf("LoadLibrary() 失败");
return 0;
}
CltSocketInit cltSocketInit = (CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit");
if (cltSocketInit == NULL)
{
return 0;
}
CltSocketSend cltSocketSend = (CltSocketSend)::GetProcAddress(hInstance, "cltSocketSend");
CltSocketRev cltSocketRev = (CltSocketRev)::GetProcAddress(hInstance, "cltSocketRev");
CltSocketDestory cltSocketDestory = (CltSocketDestory)::GetProcAddress(hInstance, "cltSocketDestory");
//执行动态库函数调用
unsigned char buf[128];
int buflen = 128;
unsigned char outbuf[4096];
int outbuflen = 4096;
strcpy((char *)buf, "aaaaaaaaaafffffffffdddddd");
buflen = 9;
void *handle = NULL;
int ret = 0;
ret = cltSocketInit(&handle);
ret = cltSocketSend(handle, buf, buflen);
ret = cltSocketRev(handle, outbuf, &outbuflen);
ret = cltSocketDestory(handle);
if (memcmp(buf, outbuf, outbuflen) == 0)
{
printf("发送数据和接受的数据一样 ok\n");
}
else
{
printf("发送数据和接受的数据不一样\n");
}
return 0;
}
运行结果:
第二种 反向调用