前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >回调函数究竟有什么作用??

回调函数究竟有什么作用??

作者头像
Rice加饭
发布2022-05-10 08:40:37
7150
发布2022-05-10 08:40:37
举报
文章被收录于专栏:Rice嵌入式

《函数指针》

再讲回调函数之前,插播一下函数指针。

函数指针:其本质是一个指针变量,该指针指向这个函数。简单来说,函数指针就是指向函数的指针。

声明格式:类型说明符 (*函数名) (参数)

《回调函数》

回调函数:一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

--某度的解释

根据某度的解释有可能比较绕口;举一个简单的例子,pcb厂家提供打样pcb服务,pcb厂家都会要求客户决定如何制作自己的pcb板子。比如沉金,板子的厚度,SMT贴片等。其中pcb制作是psb厂家提供的服务,相当与库函数;pcb的制作方式要求是客户提出的,相当于回调函数,而客户告诉pcb厂家如何制作pcb的动作,相当于把回调函数传入到库函数,也称为回调函数。

从图片(来自于某网站)可以看到,回调函数通常和应用处于同一层(因为传入什么样的回调函数是在应用层决定的)。而回调就成了一个高层调用底层,底层再回过头来调用高层的过程。

《例子说明》

例子1:

代码语言:javascript
复制
#include <stdio.h>

typedef void (*p_fun)();
void call_fun() 
{
    printf("Hello work\n");
}

int main()
{
    p_fun fun = call_fun;
    fun();
    return 0;
}

输出结果:

代码语言:javascript
复制
Hello work

从这个例子可以看到,我们首先定义了一个函数指针fun,这个函数指针的返回值为void型,然后我们给函数指针赋值,赋值为call_fun,也就是call_fun函数的首地址,此时fun获得了call_fun的地址,fun的地址等于call_fun的地址,所以最终调用fun(),也就相当于调用了call_fun();

不过这个例子并没有实现回调函数本质(函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数),而是反应函数指针的的用法。

例子2:

代码语言:javascript
复制
#include <stdio.h>

int doubled(int k)
{
    return 2*k;
}

int quadruple(int k)
{
    return 4*k;
}

int get_odd_number(int k, int (*p_fun)(int))
{
    return (*p_fun)(k) + 1;
}

int main()
{
    printf("%d\n", get_odd_number(2, doubled));
    printf("%d\n", get_odd_number(2, quadruple));

    return 0;
}

输出结果:

代码语言:javascript
复制
5
9

这个例子就符合了回调函数的本质了,函数指针作为

get_odd_number函数的型参(符合:函数的指针(地址)作为参数传递给另一个函数)。其中如第20行的代码,函数指针指向doubled函数(符合:这个指针被用来调用其所指向的函数),doubled函数就是回调函数。

不妨你看,这也体现了C语言抽象的设计,上面的例子中分别求(2*k+1)的奇数,(4*k+1)的奇数。他们的共性就是求奇数,所以get_odd_number函数可以看作把它们的共性抽象出来。

例子3:

我们把上面的例子进行拆分为三个文件,并使用Linux环境来解释,创建三个文件:main.c lib.c lib.h。

main.c:

代码语言:javascript
复制
#include <stdio.h>
#include "lib.h"

int doubled(int k)
{
    return 2*k;
}

int quadruple(int k)
{
    return 4*k;
}

int main()
{
    printf("%d\n", get_odd_number(2, doubled));
    printf("%d\n", get_odd_number(2, quadruple));

    return 0;
}

lib.c:

代码语言:javascript
复制
#include "lib.h"

int get_odd_number(int k, int (*p_fun)(int))
{
    return (*p_fun)(k) + 1;
}

lib.h:

代码语言:javascript
复制
#ifndef __LIB_H
#define __LIB_H

int get_odd_number(int k, int (*p_fun)(int));

#endif

制作动态库:

代码语言:javascript
复制
rice@rice:~/rice_file$ gcc -shared -fPIC lib.c -o libtest.so
rice@rice:~/rice_file$ sudo cp libtest.so /usr/lib/
rice@rice:~/rice_file$ gcc main.c -L. -ltest -o output
rice@rice:~/rice_file$ ./output 
5
9

生成动态库,参数说明: gcc -shared -fPIC lib.c -o libtest.so

-shared : 生成动态库; -fPIC : 生成与位置无关代码; -o :指定生成的目标文件; 使用动态库,参数说明: gcc main.c -L . –ltest -o output -L : 指定库的路径(编译时); 不指定就使用默认路径(/usr/lib/) -lvendor : 指定需要动态链接的库是谁; 这个例子简单的实现了C的封装封装的方式,对外只提供了API。在linux中很多这种链接库的做事,比如第三方的算法等,只提供API,你看不到算法的实现。回调函数在linux里面用的提多的。动态链接库和静态链接库详细内容请看另外两篇

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档