精华内容
下载资源
问答
  • C语言中一般用typedef来为回调函数定义别名(参数名)。 别名通过宏定义typedef来实现,不是简单的宏替换。可以用作同时声明指针型的多个对象。 比如: 代码如下:char *pa,pb;//pa是一个char型指针,但pb是一个...
  • C语言 回调函数详解

    2021-01-20 11:16:35
    我们在使用C语言实现相对复杂的软件开发时,经常会碰到使用回调函数的问题。但是回调函数的理解和使用却不是一件简单的事,在本篇我们根据我们个人的理解和应用经验对回调函数做简要的分析。 1、什么是回调函数  ...
  • c语言写的一个典型的回调函数,适合c和c++语言初学者进阶。
  • c语言指针回调函数最全demo实例(简单明了一看就会)
  • c语言回调函数的使用及实际作用详解

    千次阅读 多人点赞 2021-07-16 23:49:20
    回调函数这个知识点其实并不是很难,难是难在网上很多讲解回调函数的都说的太学术化了化了,一点也不亲民。 很多人即使知道怎么写回调函数也根本就搞不懂它们在实际产品中也有什么用,什么时候用。 所以这节课呢...

    大家好,我是无际。

    今天给大家讲一下芯片/模块厂家写SDK必须会使用的一种技术:回调函数。

    回调函数这个知识点其实并不是很难,难是难在网上很多讲解回调函数的都说的太学术化了化了,一点也不亲民。

    很多人即使知道怎么写回调函数也根本就搞不懂它们在实际产品中也有什么用,什么时候用。

    所以这节课呢我们会以程序架构的需求为出发点,讲解回调函数是怎么满足它这个需求的。

    为了方便大家理解,这篇内容也对应有一篇文章,大家可以找无际单片机编程获取。

    一、通过这节课程你能掌握以下知识:

    1. 掌握程序架构的核心理念或需求。
    2. 掌握回调函数的作用
    3. 掌握回调函数的程序编写
    4. 掌握回调函数在产品中的应用

    二、程序架构的核心理念和需求

    很多人可能会说一个好的程序架构啊,就是代码很紧凑、执行效率也很高。

    其实这个说的很片面,不完全对,这只能说明你程序算法写的好,但架构不一定做的好。

    即然是架构,那自然是以从”大局”为重,思维不能局限于当下的产品功能,还要考虑到以后功能的增加和裁剪,那么对于单片机开发来说,我认为一个好的程序架构至少要达到以下要求:

    硬件层和应用层的程序代码分开,相互之间的控制和通讯使用接口,而且不会共享的全局变量或数组。

    这里呢,我就这个要求,别小看这一个要求,因为这个要求里面蕴藏着很多学问的,比如用专业称为可移植性、可扩展性。

    那么我们来想象一下我们通常写单片机代码的方式啊,在51的时候基本一个.c文件解决,包括寄存器配置啊,产品功能啊。

    这种就是没有架构的程序,然后我们进化到STM32这个单片机以后,程序大了,慢慢也会在工程文件里加几个文件夹目录把硬件层和应用层代码分开了。

    于是我们会把一些不同的外设功能,比如Led、按键、串口等外设功能代码分别写在不同的.c文件里,然后统一用函数接口去调用它。

    比方说控制一个LED灯亮,直接在led.c文件里写一个驱动led灯状态的函数然后给外部调用就好了。

    那我们我们看这种Led的控制函数确实也是满足程序架构的需求的,硬件层和应用层代码分开,应用层用硬件层提供的接口来控制,而且又不会有硬件层和应用层共享的全部变量或数组。像这种是不是很简单?

    那么不知道你们有没有碰到另外一种情况,就是应用程序需要采集硬件层的数据,比如串口接收数据,按键采集、ADC值采集。

    这种硬件层的数据怎么通知应用层来拿,或者怎么主动给它?

    我们以往最简单粗暴的方式是不是就是用一个全局变量,比方说硬件层串口接收到数据来了,那么我们把数据丢到数组里,然后把接收完成全局变量标志位置1。

    比方说全局变量名为RcvFlag,然后应用层程序会轮询判断RcvFlag==1?是的话就开始把数组里的数据取出来解析。

    很多人就会说了,你看我用这种方法照样能实现功能啊,为什么还要学习别的架构。

    这样做当然可以实现功能,但是会存在移植性很差的问题。

    比如说你们老板让你把这个串口的硬件层封装起来给客户用,但不能让客户看到你实现的源代码,只提供接口(函数名)给对方用。

    那么这时候难道你要告诉客户先判断哪个变量为1,然后再取哪个数组的数据这么LOW的做法吗?

    那么如果是懂行的客户一定会怀疑你们公司的技术实力是不是小学生水平。

    那怎样做才会既方便又专业呢? 这里我们就需要用到回调函数啦。

    三、回调函数的作用

    那么在讲回调函数之前呢,对于函数调用呢我一般分为2种类型:

    1.输出型

    不知道大家有没有用过C语言自带的一些库函数,比如说sizeof()获取数据长度的函数,memcpy()是内存拷贝函数,我们调用这个函数之后呢就能完成相应的功能。

    还有我们基于单片机的一些程序函数,比方说控制LED点亮熄灭、继电器吸合断开、LCD驱动等等。

    那么这些呢,我一般称为输出型的函数。

    输出型函数我们是主导的角色,我们知道什么时候该调用它。

    2.输入型

    输入型呢,也称为的是响应式的函数

    什么叫响应式的函数呢?

    比方说接收串口的数据,我们不知道什么数据什么时候来。

    再比方说,我们按键检测的函数,我们不知道什么时候会按下按键,那么这些就要定义成响应式函数来实现,而响应式函数就可以用回调函数来实现

    所以通过这两个种类型的分析啊,我们就可以知道,回调函数基本是用在输入型的处理中。

    比方说串口数据接收,那么数据是输入到单片机里面的,单片机是处于从机角色。

    按键检测,按键状态是输入到单片机里的。

    再比方说ADC值采集,ADC值也是输入到单片机里的。

    那么它们输入的时间节点都是未知的,这些就能够用回调函数来处理。

    具体怎么处理后面我们会用代码来给大家举例。

    回调函数还有一个作用就是为了封装代码

    比如说做芯片或者模组的厂家,我们拿典型的STM32来举例,像外部中断、定时器、串口等中断函数都是属于回调函数,这种函数的目的是把采集到的数据传递给用户,或者说应用层。

    所以回调函数的核心作用是:

    1.把数据从一个.c文件传递到另一个.c文件,而不用全局变量共享数据这么LOW的方法。

    2.对于这种数据传递方式,回调函数更利于代码的封装。

    四、掌握回调函数的程序编写

    前面说了很多概念性的东西,可能大家也比较难理解,回调函数最终呢是靠函数指针来实现的。

    那么我这里通过一些模拟按键的例子来演示下怎么回通过调函数来处理它们。

    下面是我们的c-free工程,用这个来模拟方便点:

    从模块化编程的思想来看,整个工程分为2个部分,应用层main.c文件,硬件层key.c和key.h文件。

    不管再怎么复杂的程序,我们都要先从main函数一步步往下挖,main函数代码如下。

    int main(int argc, char *argv[])
    {
    KeyInit();
    KeyScanCBSRegister(KeyScanHandle);
    KeyPoll();
    
    return 0;
    }

    KeyInit();是key.c文件的按键初始化函数

    KeyScanCBSRegister(KeyScanHandle);是key.c的函数指针注册函数。

    这个函数可能大家会有点蒙,请跟进我们的节奏,下面开始烧脑环节,也是写回调函数的必须步骤,

    想理解这个回调函数注册函数,我们要先从硬件层(key.h)头文件的函数指针定义说起,具体看下图。

    这里自定义了一个函数指针类型,带两个形参。

    然后,我们在key.c这个文件里定义了一个函数指针变量。

    重点来了,我们就是通过这个函数指针,指向应用层的函数地址(函数名)

    具体怎么实现指向呢?就是通过函数指针注册函数。

    这个函数是在main函数里调用,使用这种注册函数的方式注册灵活性也很高,你想要在哪个.c文件使用按键功能就在哪里调用。

    这里要注意,main.c这个文件要定义一个函数来接收硬件层(key.c)过来的数据。

    这里定义也不是乱定义的,一定要和那个自定义函数指针类型返回值、形参一致。

    然后把这个函数名字直接复制给KeyScanCBSRegister函数的形参就可以了。

    这样调用后,我们key.c文件的pKeyScanCBS这个指针其实就是指向的KeyScanHandle函数。

    也就是说执行pKeyScanCBS的时候,就是执行KeyScanHandle函数。

    那具体检测按键的功能就是KeyPoll函数,这个在main函数里调用。

    当检测到键盘有输入以后,最终会调用pKeyScanCBS。

    最终执行的是main.c文件的KeyScanHandle函数。

    所以,我们来看下输出结果。

    如果还是有点模糊,下面我再给大家捋一捋编写和使用回调函数的流程:

    1. 自定义函数指针,形参作为硬件层要传到应用层的数据。
    2. 硬件层定义一个函数指针和函数指针注册函数。
    3. 应用层定义一个函数,返回值和形参都要和函数指针一致。
    4. 应用层调用函数指针注册函数,把定义好的函数名称作为形参传入。

    Ok,这就是回调函数的使用。

    如果还看不懂建议多看两遍。

    下面请大家思考一下,这个程序虽然简单,但是不是架构还不错?应用层和硬件层完全独立?

    展开全文
  • C语言回调函数学习

    2021-01-19 22:44:34
     对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来...
  • 一文搞懂C语言回调函数

    万次阅读 多人点赞 2019-07-09 00:40:58
    什么是回调函数 我们先来看看百度百科是如何定义回调函数的: 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我...

    转载自:https://segmentfault.com/a/1190000008293902?utm_source=tag-newest

    博主:Rdou Typing

    来源:segmentfault


    什么是回调函数


    我们先来看看百度百科是如何定义回调函数的:

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

    这段话比较长,也比较绕口。下面我通过一幅图来说明什么是回调:
    在这里插入图片描述
    假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。

    结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。那函数指针是什么?不着急,下面我们就先来看看什么是函数指针。

    什么是函数指针


    函数指针也是一种指针,只是它指向的不是整型,字符型而是函数。在C中,每个函数在编译后都是存储在内存中,并且每个函数都有一个入口地址,根据这个地址,我们便可以访问并使用这个函数。函数指针就是通过指向这个函数的入口,从而调用这个函数。

    函数指针的使用


    函数指针的定义

    函数指针虽然也是指针,但它的定义方式却和其他指针看上去很不一样,我们来看看它是如何定义的:

    /* 方法1 */
    void (*p_func)(int, int, float) = NULL;
    
    /* 方法2 */
    typedef void (*tp_func)(int, int, float);
    tp_func p_func = NULL;
    

    这两种方式都是定义了一个指向返回值为 void 类型,参数为 (int, int, float) 的函数指针。第二种方法是为了让函数指针更容易理解,尤其是在复杂的环境下;而对于一般的函数指针,直接用第一种方法就行了。

    如果之前没见过函数指针,可能会觉得函数指针的定义比较怪,为什么不是 void ()(int, int, float) *p_func 而是 void (*p_func)(int, int, float) 这种形式?这个问题我也不知道,也没必要纠结,花点时间理解下它与普通指针的区别,实在不行就先记住它的形式。

    函数指针的赋值

    在定义完函数指针后,我们就需要给它赋值了我们有两种方式对函数指针进行赋值:

    void (*p_func)(int, int, float) = NULL;
    p_func = &func1;
    p_func = func2;
    

    上面两种方法都是合法的,对于第二种方法,编译器会隐式地将 func_2 由 void ()(int, int, float) 类型转换成 void (*)(int, int, float) 类型,因此,这两种方法都行。想要了解更详细的说明,可以看看下面这个stackoverflow的链接。

    使用函数指针调用函数

    因为函数指针也是指针,因此可以使用常规的带 * 的方法来调用函数。和函数指针的赋值一样,我们也可以使用两种方法:

    方法1和我们平时直接调用函数是一样的,方法2则是用了 * 对函数指针取值,从而实现对函数的调用。

    将函数指针作为参数传给函数

    函数指针和普通指针一样,我们可以将它作为函数的参数传递给函数,下面我们看看如何实现函数指针的传参:

    /* func3 将函数指针 p_func 作为其形参 */
    void func3(int a, int b, float c, void (*p_func)(int, int, float))
    {
        (*p_func)(a, b, c);
    }
    
    /* func4 调用函数func3 */
    void func4()
    {
        func3(1, 2, 3.0, func_1);
        /* 或者 func3(1, 2, 3.0, &func_1); */
    }
    

    函数指针作为函数返回类型

    有了上面的基础,要写出返回类型为函数指针的函数应该不难了,下面这个例子就是返回类型为函数指针的函数

    void (* func5(int, int, float ))(int, int)
    {
        ...
    }
    

    在这里, func5 以 (int, int, float) 为参数,其返回类型为 void (*)(int, int) 。在C语言中,变量或者函数的声明也是一个大学问,想要了解更多关于声明的话题,可以参考我之前的文章 - C专家编程》读书笔记(1-3章)。这本书的第三章花了整整一章的内容来讲解如何读懂C语言的声明。

    函数指针数组
    在开始讲解回调函数前,最后介绍一下函数指针数组。既然函数指针也是指针,那我们就可以用数组来存放函数指针。下面我们看一个函数指针数组的例子:

    /* 方法1 */
    void (*func_array_1[5])(int, int, float);
    
    /* 方法2 */
    typedef void (*p_func_array)(int, int, float);
    p_func_array func_array_2[5];
    

    上面两种方法都可以用来定义函数指针数组,它们定义了一个元素个数为5,类型是 void (*)(int, int, float) 的函数指针数组。

    回调函数


    我们前面谈的都是函数指针,现在我们回到正题,来看看回调函数到底是怎样实现的。下面是一个四则运算的简单回调函数例子:

    #include <stdio.h>
    #include <stdlib.h>
    
    /****************************************
     * 函数指针结构体
     ***************************************/
    typedef struct _OP {
        float (*p_add)(float, float); 
        float (*p_sub)(float, float); 
        float (*p_mul)(float, float); 
        float (*p_div)(float, float); 
    } OP; 
    
    /****************************************
     * 加减乘除函数
     ***************************************/
    float ADD(float a, float b) 
    {
        return a + b;
    }
    
    float SUB(float a, float b) 
    {
        return a - b;
    }
    
    float MUL(float a, float b) 
    {
        return a * b;
    }
    
    float DIV(float a, float b) 
    {
        return a / b;
    }
    
    /****************************************
     * 初始化函数指针
     ***************************************/
    void init_op(OP *op)
    {
        op->p_add = ADD;
        op->p_sub = SUB;
        op->p_mul = &MUL;
        op->p_div = &DIV;
    }
    
    /****************************************
     * 库函数
     ***************************************/
    float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
    {
        return (*op_func)(a, b);
    }
    
    int main(int argc, char *argv[]) 
    {
        OP *op = (OP *)malloc(sizeof(OP)); 
        init_op(op);
        
        /* 直接使用函数指针调用函数 */ 
        printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2), 
                (op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));
         
        /* 调用回调函数 */ 
        printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", 
                add_sub_mul_div(1.3, 2.2, ADD), 
                add_sub_mul_div(1.3, 2.2, SUB), 
                add_sub_mul_div(1.3, 2.2, MUL), 
                add_sub_mul_div(1.3, 2.2, DIV));
    
        return 0; 
    }
    

    这个例子有点长,我一步步地来讲解如何使用回调函数。

    第一步

    要完成加减乘除,我们需要定义四个函数分别实现加减乘除的运算功能,这几个函数就是:

    /****************************************
     * 加减乘除函数
     ***************************************/
    float ADD(float a, float b) 
    {
        return a + b;
    }
    
    float SUB(float a, float b) 
    {
        return a - b;
    }
    
    float MUL(float a, float b) 
    {
        return a * b;
    }
    
    float DIV(float a, float b) 
    {
        return a / b;
    }
    

    第二步

    我们需要定义四个函数指针分别指向这四个函数:

    /****************************************
     * 函数指针结构体
     ***************************************/
    typedef struct _OP {
        float (*p_add)(float, float); 
        float (*p_sub)(float, float); 
        float (*p_mul)(float, float); 
        float (*p_div)(float, float); 
    } OP; 
    
    /****************************************
     * 初始化函数指针
     ***************************************/
    void init_op(OP *op)
    {
        op->p_add = ADD;
        op->p_sub = SUB;
        op->p_mul = &MUL;
        op->p_div = &DIV;
    }
    

    第三步

    我们需要创建一个“库函数”,这个函数以函数指针为参数,通过它来调用不同的函数:

    /****************************************
     * 库函数
     ***************************************/
    float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
    {
        return (*op_func)(a, b);
    }
    

    第四步

    当这几部都完成后,我们就可以开始调用回调函数了:

    /* 调用回调函数 */ 
    printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", 
            add_sub_mul_div(1.3, 2.2, op->p_add), 
            add_sub_mul_div(1.3, 2.2, op->p_sub), 
            add_sub_mul_div(1.3, 2.2, MUL), 
            add_sub_mul_div(1.3, 2.2, DIV));
    

    简单的四部便可以实现回调函数。在这四步中,我们甚至可以省略第二步,直接将函数名传入“库函数”,比如上面的乘法和除法运算。回调函数的核心就是函数指针,只要搞懂了函数指针再学回调函数,那真是手到擒来了。

    总结


    本文主要讲了如何使用函数指针和回调函数。回调函数的核心就是函数指针,因此我花了大量篇幅讲解函数指针。对于回调函数的实现,我给出了一个例子,希望这个例子能给你帮助。回调函数很重要,如果连它都不会,C语言真不算入门了。当然了,即使会了它,也不要骄傲,因为C语言还有太多的东西需要我们去学习、实践。

    如果觉得本文对你有帮助,去原博文地址,为其点赞吧!

    展开全文
  • 讲解回调函数相关文章:  关于C语言回调函数的理解

      回调函数在C语言中是通过函数指针来实现的,通过将回调函数的地址传给调用函数从而实现回调。因此,要实现回调,必须首先定义函数指针。
      指向函数入口地址的指针称为函数指针。如下所示定义函数指针callback。
      callback无非就是将函数作为参数来传递,本质上是将代码作为数据来使用。

    #include<stdio.h>
    
    //定义回调函数
    int add(int a, int b)
    {
         return(a+b);
    }
    
    //callback函数指针
    //定义实现回调函数的“调用函数”,比如
    int fun(int (*callback)(int,int), int a, int b)
    {
        return (*callback)(a, b);
    }
    
    int main(void)
    {
       int value_1 = 1,value_2 = 2;
       int result = fun(add, value_1, value_2);
       printf("result is:%d", result);
       return 0;
    }
    

      函数main调用函数fun的时候,函数 main通过参数给 函数fun传递了另外一个函数add的指针,在函数 fun执行的过程中,函数fun 调用了函数add,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 add 就是回调函数。简化回调fun(add)执行函数fun前要先执行函数add,add就是回调函数。

    到此应该明白回调函数的定义了吧?

    普通函数和回调函数区别
    普通函数被直接调用
    回调函数被间接调用
    在这里插入图片描述

    讲解回调函数相关文章
    关于C语言回调函数的理解

    相关书籍
    <<冒泡课堂:编程范式与 OOP 思想>> 3.4 事件驱动——有事我叫你,没事别烦我 (这个章节讲回调比较清楚)

    展开全文
  • C语言 回调函数原理及实现

    千次阅读 2019-08-30 18:32:38
    最近需要实现处理AWSIOT传来的消息回调函数。作为库编程,在老司机的指导下发现不能直接把AWS IOT的回调接口暴露到上层而是应该自己封装回调函数以供上层调用,这样可以更好地解耦合,上层即不需要了解下层的细节。...

    最近需要实现处理AWSIOT传来的消息回调函数。作为库编程,在老司机的指导下发现不能直接把AWS IOT的回调接口暴露到上层而是应该自己封装回调函数以供上层调用,这样可以更好地解耦合,上层即不需要了解下层的细节。这里就发现了原来对回调函数的使用还存在一定的误区,这里特地整理一篇文章以供查阅。实质上就是传入一个函数指针 内部去调用。 可以参考Linux内核callback调用方式。

    第一节主要展示什么是回调函数
    第二节主要讨论回调函数的常用用法

    1 回调函数基本概念及源码

    1.1 回调函数概念

    个人理解回调函数就是:
    在一个函数中调用另一个通过参数传入的函数,而这个传入的函数就是回调函数。

    用伪代码标识如下:

    functionB(){
      printf("i am callback");
    }
    functionA(paramA){
      printf("I will call the callback next");
      paramA();
    }
    
    functionA(functionB);
    

    可以看到调用functionA时传入functionB, 而在functionA中 调用functionB,所以functionB被称为回调函数。

    而在C语言的实现可以借用百度百科的定义:
    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

    1.2 sample

    这里就提供一份sample code 进行说明回调函数形式:

    #include <stdio.h>
    
    // 架设libPrintfFunction 函数即为 系统编程人员 提供的接口,这个函数会调用应用开发者提供的回调函数
    typedef int (*libCallbackFunction_t)(char* str); //定义回调函数格式 为传入为char* return 为int的函数指针
    int libPrintfFunction(char *str, libCallbackFunction_t callback){//假设我是lib开发者(系统编程),在提供接口时,想给应用开发者(应用编程)执行它想要执行自定操作操作 提高灵活性
      printf("This is lib function,printf main str: %s\n", str);
      char* libstr = "this is lib str";
      callback(libstr);
      
      return 0;
    }
    
    int mainCallbackPrintf(char *str){
      printf("This is main function,printf lib str: %s\n", str);
      return 0;
    }
    
    int main(){
      char* mainStr = "this is main str";
      libPrintfFunction(mainStr, mainCallbackPrintf);
      return 0;
    }
    

    编译

    gcc callback.c -o test
    

    这份代码功能即为 主函数会调用lib开发人员提供的libPrintfFunction, 并传入约定好的回调函数得到lib函数中传入值。 这里我们在回调函数中打印出lib传入值。这里就可以很清楚地看到mian函数调用了libPrintfFunction,并调用时传入了自己定义的mainCallbackPrintf 这个函数。而这个函数在libPrintfFunction 中被调用,所以mainCallbackPrintf 就是回调函数。

    1.3 封装成lib的sample

    可能这样通常大家看到的回调函数不太一样,我们将这个函数封装成lib,可能就会更加熟悉了。
    现在将上面这个函数拆成三个部分,并把lib函数分装成库函数。将库函数单独定义成一个.c和对应.h
    lib.c:

    #include <stdio.h>
    #include "lib.h"
    int libPrintfFunction(char *str, libCallbackFunction_t callback){//假设我是lib开发者(系统编程),在提供接口时,想给应用开发者(应用编程)执行它想要执行自定操作操作 提高灵活性
      printf("This is lib function,printf main str: %s\n", str);
      char* libstr = "this is lib str";
      callback(libstr);
      
      return 0;
    }
    

    lib.h:

    #ifndef _LIB_H_
    #define _LIB_H_
    
    typedef int (*libCallbackFunction_t)(char* str); //定义回调函数格式 为传入为char* return 为int的函数指针
    int libPrintfFunction(char *str, libCallbackFunction_t callback);
    
    #endif
    

    callback.c

    #include <stdio.h>
    #include "lib.h"
    
    int mainCallbackPrintf(char *str){
      printf("This is main function,printf lib str: %s\n", str);
      return 0;
    }
    
    int main(){
      char* mainStr = "this is main str";
      libPrintfFunction(mainStr, mainCallbackPrintf);
      return 0;
    }
    

    这里把lib.c 打包成静态库

    gcc -c lib.c -o lib.o
    ar rcs -o lib.a lib.o
    

    这样就得到了静态lib.a 和lib.h
    使用lib.a和我们主函数编译可支持文件

    gcc callback.c lib.a -o test
    

    测试

    This is lib function,printf main str: this is main str
    This is main function,printf lib str: this is lib str
    

    这里就是通常我们用到的回调函数的场景了
    通过我们使用第三方lib时,会提供一个.a 和,h

    #ifndef _LIB_H_
    #define _LIB_H_
    
    typedef int (*libCallbackFunction_t)(char* str); //定义回调函数格式 为传入为char* return 为int的函数指针
    int libPrintfFunction(char *str, libCallbackFunction_t callback);
    
    #endif
    

    我们在.h中就可以看到 libPrintfFunction 这个方法提供了一个回调 函数的接口。
    而回调函数的形式是传入char* 返回int的函数。所以就在主函数构造一个这个形式的函数传入libPrintfFunction,当
    libPrintfFunction执行到一定逻辑时,就会触发我们传入的回调函数。

    2 回调函数使用场景

    上面已经介绍什么是回调函数,这里描述一下我理解回调函数的应用场景。

    2.1 库函数开发

    举一个例子:
    比如现在需要开发一个lib 对上层提供调动,基础的功能是lib 中固定对传入数据执行一些处理(比如打印传入),然后执行一段算法,又进行一些处理(比如打印算法处理后的结果)。这里我们可以大概描述出这段代码

    我们假设这个算法是对传入值乘100:

    int libfunction(int a){
    	printf("%d\n", a);
        a = a * 100;
        printf("%d\n", a);
    }
    

    上面的函数就是你要编写的lib的实现。看起来还不错 也没有回调函数什么事。
    但这里实质上是lib提供者实现的,如果产品应用中经常变更这种算法(md 需求又变了),那么意味着lib提供者也要更新lib。这种情况下lib开发者就会觉得特别烦,自然而然可以可以对外提供一个回调函数,把算法实现交给上层应用程序员去实现,每次算法改动就不会反复需要lib提供者更新,这样编写的lib才更加灵活。

    int libfunction(int a, int (* callback)(int *)){
    	printf("%d\n", a);
        a = callback(a);
        printf("%d\n", a);
    }
    

    而main函数中回调函数就可以写成这样了

    int callback(int a){
    	a = a * 100;
        return a;
    }
    
    libfunction(10, callback);
    

    这样提供的代码的灵活性。当然上面这种回调是阻塞的。

    2.2 基于事件驱动编程

    事件驱动是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数).当然事件不仅限于用户的操作. 事件驱动的核心自然是事件。

    很简单应用场景就是,货到了通知我。
    货到了就是事件,而通知我就是事件对应的处理操作。
    而在实际使用中,我们就调用一个监听函数,可以相应的回调函数(处理函数)。
    来段伪代码:

    int callback(int a){
    	printf("I will buy Thing");
    }
    
    eventListen('ThingArrive', callback);
    

    在我们实际应用中就有很多这种操作,比如与系统硬件交互。
    我们调用打开摄像头时,实质上是发起了命令,系统操作硬件打开,这里我们使用事件驱动编程的话,就可以定义一个回调函数执行摄像头打开成功后的操作,并把回调函数注册到监听摄像头打开成功这个事件上。
    在网络上通信中就更加常用这种回调地处理方式。

    上诉回调操作就可能都为非阻塞的。
    实质上我们想要实现这种非阻塞回调函数,可以在传入回调函数的函数中开辟一个线程即可。这样主线程就可以实现非阻塞的效果。而子线程触发相应事件后就会调用回调函数。

    3 源码

    c_callback.zip
    链接: https://pan.baidu.com/s/1Hd0I8B82h9An_pm8fQ60xQ 提取码: 4mpt

    参考链接

    https://blog.csdn.net/morixinguan/article/details/65494239
    https://www.zhihu.com/question/19801131

    展开全文
  • 我们先来看看百度百科是如何定义回调函数的: 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数回调...
  • PAGE / NUMPAGES 回调函数 什么是回调函数 回调函数就是函数指针的一种用法 使用回调函数实际上就是在调用某个函数通常是API函数时将自己的一个函数回调函数的地址作为参数传递给那个函数而那个函数在需要的时候利用...
  •  对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被...
  • https://blog.csdn.net/qq_27568125/article/details/55094616?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
  • 引用维基百科上的关于回调函数的概念: 在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。这...
  • C语言回调函数的学习

    2020-08-04 04:51:06
    回调函数是一个通过函数指针调用的函数。本文主要讲述了回调函数的基本原理和用法。
  • C语言中的回调函数

    2020-12-22 15:21:25
    回调函数C语言的一种常用的高级应用,在库函数和应用程序中都有广泛的应用。  要点:  1.回调函数的调用方式  2.回调函数的完全形式的参数传递原则  3.C语言库函数的回调函数使用  回调函数的概念和作用...
  • 主要实现自主定义回调函数结合及其在动态数组功能中的使用实例。存储数据使用void*、void**,其中包括结构体数据结构。主要功能有初始化动态数组、释放动态数组、尾插法、删除指定下标、更新指定下标数据、打印数据...
  • C语言回调函数

    2019-02-27 21:46:41
    函数指针 如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来...
  • C语言 回调函数

    2016-06-22 21:06:49
    对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简言之,回调函数就是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来...
  • C语言回调函数一个简单的例子

    千次阅读 2016-10-27 19:42:47
    回调函数在linux内核或是单片机上应用得太多,由此也可以大致判断,一个初学者和有个有经验的区别。我转写在这儿,希望更多的人能够看到,一起进步!!!! 回调函数通俗的解释:  普通函数:你所写的...
  • C语言回调函数callback

    2020-11-18 16:04:11
    一、定义和使用场合 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为...C语言回调函数主要通过函数指针的方式实现。 图1-1 回调函数在软件系统的调用结果 ..
  • C语言回调函数–数组

    千次阅读 2018-08-15 10:38:50
    本人理解的回调函数数组,实际上是用于存储回调函数及对应信息的函数表。这个数组用于存储所有的回调函数,新注册的回调函数也要增加到这个数组(函数表)中。根据上述的特点,回调函数非常适用于嵌入式系统中菜单、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,654
精华内容 17,861
关键字:

c语言回调函数

c语言 订阅