函数指针做函数参数 使用总结及其意义

简介: 函数指针做函数参数 使用总结及其意义

目录


1、函数指针


2、函数指针做函数参数


3、函数指针做函数参数在实际项目中的运用。


第一种 正向调用


第一步 Load DLL


第二步 声明函数指针类型


第三步 实现函数的调用。


完整代码如下:


第二种 反向调用


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;

tt.png


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;


}


运行结果:

tt.png

思路剖析:


1、指针函数的用法将函数的调用和函数的实现有效分离,实现解耦。


2、int myMainFunc(int(*myFuncAdd)(int a, int b))是一个库中的函数,就只有使用回调了,通过函数指针参数将外部函数地址传入来实现调用


3、函数 Add 的代码作了修改,也不必改动库的代码,就可以正常实现调用,便于程序的维护和升级。


3、函数指针做函数参数在实际项目中的运用。

第一种 正向调用

例:


有个socketclient.dll,用dependency walker可以查看到dll的方法


tt.png

tt.png

第一步 Load DLL

HINSTANCE hInstance;


   string pathStr= "socketclient.dll";


   LPCSTR sss = pathStr.c_str();


   hInstance =LoadLibrary("socketclient.dll");


   if (hInstance == NULL)


   {


        printf("LoadLibrary() 失败");


        return 0;


   }


在这里注意编码的问题。


右键属性如下图:

tt.png

将字符集修改为使用多字节字符集。


如果使用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;


}


运行结果:


tt.png


目录
相关文章
|
13天前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
10天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
12 1
|
10天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
9 1
|
16天前
|
存储 C语言
字符指针作为函数参数
字符指针作为函数参数
25 2
|
16天前
|
存储 C语言
指针数组作为main函数的形参
指针数组作为main函数的形参
7 0
|
16天前
|
存储 C语言
指针变量作为函数参数
指针变量作为函数参数
9 0
|
16天前
|
C语言
怎样定义和使用指向函数的指针变量
怎样定义和使用指向函数的指针变量
7 0
|
16天前
|
算法 C语言
使用指向函数的指针作为函数参数
使用指向函数的指针作为函数参数
12 0
|
17天前
|
C++
结构体变量与结构体变量指针作为函数参数
结构体变量与结构体变量指针作为函数参数
11 0
|
16天前
|
C语言
C语言指针带代码
C语言指针带代码
21 3