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

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

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

第一步 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

tt.png

第二种 反向调用


 


 


tt.png


tt.png

目录
相关文章
|
25天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
44 4
|
2月前
|
存储 C语言 C++
如何通过指针作为函数参数来实现函数的返回多个值
在C语言中,可以通过将指针作为函数参数来实现函数返回多个值。调用函数时,传递变量的地址,函数内部通过修改指针所指向的内存来改变原变量的值,从而实现多值返回。
|
2月前
|
存储 搜索推荐 C语言
如何理解指针作为函数参数的输入和输出特性
指针作为函数参数时,可以实现输入和输出的双重功能。通过指针传递变量的地址,函数可以修改外部变量的值,实现输出;同时,指针本身也可以作为输入,传递初始值或状态。这种方式提高了函数的灵活性和效率。
|
2月前
利用指针函数
【10月更文挑战第2天】利用指针函数。
19 1
|
2月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
25 2
|
3月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
4月前
|
程序员 C语言
指针在函数参数和返回值中的使用
指针在函数参数和返回值中的使用
65 9
|
4月前
|
存储 搜索推荐 C语言
C语言中的指针函数:深入探索与应用
C语言中的指针函数:深入探索与应用
|
5月前
|
运维
系统日志使用问题之如何防止在打印参数时遇到NPE(空指针异常)
系统日志使用问题之如何防止在打印参数时遇到NPE(空指针异常)
|
25天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
87 13