博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数指针基础及应用(正向调用、动态库升级成框架)
阅读量:4124 次
发布时间:2019-05-25

本文共 6061 字,大约阅读时间需要 20 分钟。

1、数组指针

    二维数组。

2、函数指针

      函数指针做函数参数的思想精华——调用者任务的人  和 实现任务的人  解耦合

    (回调函数的本质:提前做了一个协议的约定把函数的参数、函数返回值提前约定))
    (C++编译器通过多态的机制(提前布局vptr指针和虚函数表,找虚函数入口地址来实现))

1)基本功能演示:

#define  _CRT_SECURE_NO_WARNINGS #include 
#include
#include
//=====> 函数指针做函数参数的思想精华:// 调用者任务的人 和 实现任务的人 解耦合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),双击控件编写代码;

          
         b)一般情况下,提供DLL时还会同时提供一个.h文件,通过这个.h文件,我们就可以知道DLL提供了哪些函数。(如果没有提供,我们可以借助VC6.0Tools——Depends,导入DLL,查看DLL提供的Function)。下述SOCKETCLIENT.DLL可在http://yun.baidu.com/s/1c0x2kP6函数指针动态库下载。
       c)接着a),双击进去以后,首先在该文件的全局域声明要使用的函数的函数指针类型,然后在Button的单击事件中继续编写。

//声明一个函数指针类型//客户端初始化 获取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 的本地化版本。
需要下载安装Multibyte MFC Library for Visual Studio 2013:http://www.microsoft.com/zh-cn/download/details.aspx?id=40770

运行:

          

        3)函数指针应用二——动态库升级成框架(动态库:抽象类一个套接口,单独封装成模块,供别人调用;无法扩展。框架:能自由的扩展,方便后续产品入围,而不轻易改变框架)

需求:比如在一个socket通信库中,完成数据加密功能,有n个厂商的加密产品供你选择,还有后续的加密产品,所以就需要实现动态库和第n个厂商产品的解耦合,支持后续加密产品的入围。

        
实现步骤:
    1)、动态库中定义协议,并完成任务的调用:

//完成发送报文的时候,进行数据加密//定义函数指针类型,通过函数指针类型 来约定 厂商 去实现 加密解密函数的原型//方式一: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。也就是说不管第三方产品有多少,我的动态库都不用改变!实现了动态库和第三方加密产品的解耦合!赋予了动态库生命力!

       回调函数:利用函数指针做函数参数,实现的一种调用机制,具体任务的实现者,可以不知道什么时候被调用。

       回调机制原理:
           当具体事件发生时,调用者通过函数指针调用具体函数
           回调机制将调用者和被调函数分开,两者互不依赖
           任务的实现 和 任务的调用 可以耦合  (提前进行接口的封装和设计)

上述使用到的动态库和完整工程下载:

你可能感兴趣的文章
浅谈JavaScript的语言特性
查看>>
LeetCode第39题思悟——组合总和(combination-sum)
查看>>
LeetCode第43题思悟——字符串相乘(multiply-strings)
查看>>
LeetCode第44题思悟——通配符匹配(wildcard-matching)
查看>>
LeetCode第45题思悟——跳跃游戏(jump-game-ii)
查看>>
LeetCode第46题思悟——全排列(permutations)
查看>>
LeetCode第47题思悟—— 全排列 II(permutations-ii)
查看>>
LeetCode第48题思悟——旋转图像(rotate-image)
查看>>
驱动力3.0,动力全开~
查看>>
记CSDN访问量10万+
查看>>
Linux下Oracle数据库账户被锁:the account is locked问题的解决
查看>>
记CSDN访问20万+
查看>>
Windows 环境下Webstorm 2020.3 版本在右下角找不到Git分支切换部件的一种解决方法
查看>>
Electron-Vue项目中遇到fs.rm is not a function问题的解决过程
查看>>
飞机换乘次数最少问题的两种解决方案
查看>>
有向无回路图的理解
查看>>
设计模式中英文汇总分类
查看>>
WPF实现蜘蛛纸牌游戏
查看>>
单例模式
查看>>
工厂方法模式
查看>>