1、数组指针
二维数组。
2、函数指针
函数指针做函数参数的思想精华——调用者任务的人 和 实现任务的人 解耦合
(回调函数的本质:提前做了一个协议的约定(把函数的参数、函数返回值提前约定))
(C++编译器通过多态的机制(提前布局vptr指针和虚函数表,找虚函数入口地址来实现))
1)基本功能演示:
#define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <string.h> #include <stdio.h> //=====> 函数指针做函数参数的思想精华: // 调用者任务的人 和 实现任务的人 解耦合 int myadd(int a, int b) //原来子任务 { int c = 0; c = a + b; printf("func myadd() do \n"); return c; } int myadd2(int a, int b) //后续子任务 { int c = 0; c = a + b; printf("func myadd2() do \n"); return c; } int myadd3(int a, int b) //后续子任务 { int c = 0; c = a + b; printf("func myadd3() do \n"); return c; } int myadd4(int a, int b) //后续子任务 { int c = 0; c = a + b; printf("func myadd4() do \n"); return c; } // int (*myFuncVar)(int a, int b)——函数指针变量——返回类型为int,两个int型变量 int myMianOp( int(*myFuncVar)(int a, int b) ) { myFuncVar(10, 20); //间接的调用 myadd函数 return 0; } void main() { myMianOp(myadd); //调用者 myMianOp(myadd2); //调用者 myMianOp(myadd3); //调用者 myMianOp(myadd4); //调用者 }
2)函数指针应用一——正向调用:甩开C++编译器,自己找到DLL中函数的地址,然后去调用函数
a)新建一个MFC应用程序(以便使用系统函数),拖一个Button控件(DllPlay),双击控件编写代码;
//声明一个函数指针类型 //客户端初始化 获取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*/); void C函数指针正向调用Dlg::OnBnClickedButton1() { HINSTANCE hInstance; hInstance = ::LoadLibrary("c:/socketclient.dll"); //导入动态库(先把DLL放到C盘下) if (hInstance == NULL) { AfxMessageBox("LoadLibrary(DLL) 失败!"); return; } //首先获得了"cltSocketInit"函数的地址(GetProcAddress),然后再转换成CltSocketInit类型,再利用上面定义的函数指针类型定义变量cltSocketInit CltSocketInit cltSocketInit = (CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit"); if (cltSocketInit == NULL) { return; } 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_s((char *)buf,128, "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) { AfxMessageBox("发送数据和接受的数据一样 ok"); } else { AfxMessageBox("发送数据和接受的数据不一样"); } }选择项目属性->配置属性->常规->字符集,改为“未设置”(error C2665: “AfxMessageBox”: 2 个重载中没有一个可以转换所有参数类型)。运行,出现以下错误:
错误 1 error MSB8031: Building an MFC project for a non-Unicode character set is deprecated. You must change the project property to Unicode or download an additional library. See http://go.microsoft.com/fwlink/p/?LinkId=286820 for more information. C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppBuild.targets 369 5 函数指针正向调用这是因为:用于多字节字符编码 (MBCS) 的 MFC 库 (DLL) 不再包含于 Visual Studio 中,但是可用作插件,您可以在任何装有 Visual Studio Professional、Visual Studio Premium 或 Visual Studio Ultimate 的计算机上下载和安装。(在 Visual Studio 中,必须启用 MFC。)包括英语(美国)和 DLL 的本地化版本。
运行:
3)函数指针应用二——动态库升级成框架(动态库:抽象类一个套接口,单独封装成模块,供别人调用;无法扩展。框架:能自由的扩展,方便后续产品入围,而不轻易改变框架)
需求:比如在一个socket通信库中,完成数据加密功能,有n个厂商的加密产品供你选择,还有后续的加密产品,所以就需要实现动态库和第n个厂商产品的解耦合,支持后续加密产品的入围。
//完成发送报文的时候,进行数据加密 //定义函数指针类型,通过函数指针类型 来约定 厂商 去实现 加密解密函数的原型 //方式一: typedef int (*EncData)(unsigned char *in, int inlen, unsigned char *out, int *outlen); typedef int (*DecData)(unsigned char *in, int inlen, unsigned char *out, int *outlen); int socketclient_sendAndEnc1(void *handle, unsigned char *buf, int buflen, EncData encDataCallback); //方式二:直接函数指针做参数 int socketclient_sendAndEnc2(void *handle, unsigned char *buf, int buflen, int (*EncData)(unsigned char *in, int inlen, unsigned char *out, int *outlen) );
//动态库中定义实现以上函数 __declspec(dllexport) int socketclient_sendAndEnc1(void *handle, unsigned char *buf, int buflen, EncData encDataCallback) //encDataCallback函数指针类型变量 { int ret = 0; unsigned char cryptbuf[4096]; int ryptbuflen = 4096; Sck_Handle *tmpHandle = NULL; if (handle == NULL || buf==NULL || encDataCallback==NULL) { ret = -1; printf("func socketclient_sendAndEnc1() err :%d check handle == NULL err \n", ret); return ret; } ret = encDataCallback(buf, buflen, cryptbuf, &cryptbuflen); //间接的调用子任务,实现第三方加密产品和调用者的分离 if (ret != 0) { ret = -2; printf("func socketclient_sendAndEnc1() err :%d check handle == NULL err \n", ret); return ret; } tmpHandle = (Sck_Handle *)handle; tmpHandle->len = cryptbuflen; tmpHandle->p = (unsigned char *)malloc(cryptbuflen); if (tmpHandle->p == NULL) { ret = -3; printf("func socketclient_sendAndEnc1() err :%d mallocerr \n", ret); return ret; } //把加密的明文 缓存到 内存中 memcpy(tmpHandle->p, cryptbuf, cryptbuflen); return 0; }2)、加密厂商完成协议函数的编写:
//某后续第三方加密产品实现 int Hw_EncData(unsigned char *in, int inlen, unsigned char *out, int *outlen) { printf("func Hw_EncData begin....\n "); //此处不是具体加密实现 strcpy((char *)out, "123456789"); //伪加密 *outlen = 9; printf("func Hw_EncData end....\n "); return 0; }3)、对接调试——通过函数指针做参数,加入到动态库中。也就是说,函数指针建立起了第三方和主动态库之间的联系。
int ret = 0; ret = socketclient_sendAndEnc1(handle, buf, buflen, Hw_EncData); //把第三方加密产品——Hw_EncData注入
分析具体是怎么分离的:main函数调用动态库的socketclient_sendAndEnc1(发送并加密),但是并不调用自己的加密函数,而是通过其中的函数指针转而去调用第三方加密产品Hw_EncData。也就是说不管第三方产品有多少,我的动态库都不用改变!实现了动态库和第三方加密产品的解耦合!赋予了动态库生命力!
回调函数:利用函数指针做函数参数,实现的一种调用机制,具体任务的实现者,可以不知道什么时候被调用。
回调机制原理:
当具体事件发生时,调用者通过函数指针调用具体函数
回调机制将调用者和被调函数分开,两者互不依赖
任务的实现 和 任务的调用 可以耦合 (提前进行接口的封装和设计)
上述使用到的动态库和完整工程下载:http://pan.baidu.com/s/1c0x2kP6