【C/C++】浅谈C/C++中函数指针与回调函数

7 篇文章 0 订阅

01、函数指针

1.1、函数指针定义

一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

简言之:函数指针其实就是一个指针变量,代表着一段内存。

1.2、函数指针的定义形式
returnType (*pointerName)(param list);

returnType 为函数返回值类型,pointerName 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。
返回类型(*函数名)(参数表)

例如下面两种写法都是正确的。

  1. 写法一:

    void (*F_TYPE)(int nParam,char strName);  //带参数变量名
    
  2. 写法二:

    void (*F_TYPE)(int,char);  //不带参数变量名
    

注意事项:注意( )的优先级高于*,第一个括号不能省略,如果写作returnType pointerName(param list);就成了函数原型,它表明函数的返回值类型为returnType*

1.3、函数指针与typedef

想必大家都能看出来,函数指针的定义是很长一段的,如果每次是用都要写这么长一段,额,大可不必,因为,typedef能够解决这个问题。

typedef:程序中的作用一般是取别名,例如下面这个例子,我们把函数指针换个名字。
typedef 返回类型(*新类型)(参数表)

//1.默认原始写法:
void callbackint(int a, int b, int (*pMsg)(int a,int b)); 
//2.typedef优化后:
typedef void (*PSM)(int a, int b);
void callbackint(int a, int b, PSM p);

这样做的好处是,以后只要需要使用 int (*pMsg)(int a, int b)的地方我们都能使用PSM p这种方式来替换他。
typedef是C语言基础,这里不浪费时间在关键字上面,感兴趣的可以自行了解一下。

02、回调函数

2.1、回调函数定义

百度给的解释是:

回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。
回调函数的使用可以大大提升编程的效率,这使得它在现代编程中被非常多地使用。同时,有一些需求必须要使用回调函数来实现。
最著名的回调函数调用有C/C++标准库stdlib.h/cstdlib中的快速排序函数qsort和二分查找函数bsearch中都会要求的一个与strcmp类似的参数,用于设置数据的比较方法。

关于名词仿函数,请参考STL源码剖析中的算法一块儿,会有非常细致的解释。

我自己的理解就是:通过参数将函数地址传入,在多线程中,如果主线程在执行此函数时,通过参数中的函数地址,相当于异步执行了另外一个函数,另外一个函数也就是我们所称的回调函数,回调函数多用于socket、http等协议中的消息交互处理,回调函数的本质就是C/C++中的函数指针,如上所述。

2.2、回调函数的使用场景

著名的 lone wolf 曾经解释过这个问题:

其实回调就是一种利用函数指针进行函数调用的过程.
为什么要用回调呢?比如我要写一个子模块给你用, 来接收远程socket发来的命令.当我接收到命令后, 需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令, 我也不知道你的主模块是什么.cpp或者.h, 或者说, 我根本不用关心你在主模块里怎么处理它, 也不应该关心用什么函数处理它…… 怎么办?
使用回调!

什么是回调函数?
  回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。
理解回调函数!

在很多第三方库中,比如libcurl中:

CURL_EXTERN curl_easy_setopt(CURL* curl,CURLoption option, ...);

就是用了回调函数,通过设置一个函数地址,当我们将内容发送到http请求页面之后,页面会通过字节流返回一个stringstream类型数据,里面就是我们请求服务器之后的返回结果,在一些频繁请求中,你不可能做到每一个都要程序停下来处理,这个时候,就可以使用回调函数来处理,异步通讯。

多线程中:
主线程收发数据、子线程处理响应数据。大致都是一个意思。

03、实例重释

我这里分文件举一个简单的例子阐述刚才上面说到的内容。可能内容不够精炼,大佬请随意,初学者建议看一下,很好理解。

  1. 功能:创建一个函数,传递两个int类型参数,通过回调函数返回其两个int类型参数的计算结果。

  2. CallBack.h内容如下:

    #ifndef _CALLBACK_H
    #define _CALLBACK_H
    
    //使用typedef进行简化代码,按部就班的写肯定没问题
    typedef int (*PMSGMessage)(int a, int b);
     //回调函数
    int callbackInt(int a, int b, PMSGMessage p); 
    
    #endif
    
  3. CallBack.cpp内容如下:

    #include <iostream>
    #include "CallBack.h"
    
    using namespace std;
    
    int callbackInt(int a ,int b, PMSGMessage p)
    {//回调函数实现
    		retrun p(a,b)
    }
    
  4. test.cpp函数内容如下:

    #include <iostream>
    #include "CallBack.h"
    using namespace std;
    
    int Multiplication(int a, int b)
    {
    	//参数检查(避免出现0)
    	if( a == 0 || b == 0)
    	{
    		cout << "其中有参数为0,结果应避免为0" << endl;
    		return 0;
    	}
    	
    	return a * b;
    }
    
    int main()
    {
    	int n = 6;
    	int m = 11;
    
    	int bRet = callbackInt(n,m,Multiplication);
    	//如果我们用了类,就是函数等都是成员函数,我们这里要带上域操作符(" :: ")
    	cout << "回调函数执行的结果是:" << bRet << endl; 
    	
    	system("pause");
    	return 0;
    }
    
  5. 小结:回调函数的简单应用大概就是这么多,如果有机会,建议多用点开源库,那里面很多地方都是用了回调函数机制,而且都是比此例子高级一点的用法,但是基本思想都一样,不要因为用法高级一点就害怕了,自己写两个例子之后,想必对于回调会有更深的理解。

04、总结

函数指针是C语言中的一个重要知识点,如果忘记了,一定要多复习一下,最近搞回调函数,用第三方库,栽了很多跟头,天天写程序,忘记了函数指针这些东西,查阅资料之后才复习了一下,才搞定了工作中的内容。

其实有小伙伴问我,写blog是为了什么?写blog不一定是为了获得什么,这是自己成长的一个见证,当你若干年后回头看你自己写的东西,也许你会一笑而过,但是,这是属于自己的珍贵财富,而且最重要的一点,你会更有底气。

版权声明:转载请注明出处,谢谢!

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cain Xcy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值