服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言中回调函数的含义与使用场景详解

C语言中回调函数的含义与使用场景详解

2022-10-31 12:04物联网老王 C/C++

这篇文章主要为大家详细介绍了C语言中回调函数的含义与使用场景,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

举例

在下述程序中函数 test2_cal() 中调用 函数指针 s_cal 指定的函数执行数值的计算。则 s_cal 指定的那些函数就可以看作一个回调函数。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{
    printf("now is sum\r\n");
    return a + b;
}
static int cal_sub(int a, int b)
{
    printf("now is sub\r\n");
    return a - b;
}
static int cal_mul(int a, int b)
{
    printf("now is mul\r\n");
    return a * b;
}
static my_calculate_t s_cal = cal_sum;
static int test2_cal (int a, int b)
{
    int result = 0;
    if(s_cal) {
        result = s_cal(a ,b);
        printf("result=%d\r\n", result);
    }
    return result;
}
void app_main(void)
{
    printf("init done\r\n");
    int m = 10, n = 1, ret;
    ret = test2_cal(m, n);
}

上述程序中 s_cal 的值为 cal_sum()函数 cal_sum()就是一个回调函数;即当前要执行的运算为 cal_sum 定义的加法运算。

当前程序的输出结果:

init done
now is sum
result=11

也可以改变 s_cal 的值为 cal_sub,cal_mul,它们分别对应减法、乘法运算。读者可自行赋值进行测试。
 

小结:从上述测试,我们不难理解,仅仅通过更改一个 s_cal函数指针的值分别指向cal_sumcal_sub,cal_mul就可以实现整个程序运行不同的运算。运算的接口被统一为 test2_cal(),它具备了执行多种运算的功能(通过更改s_cal函数指针的值指定其功能)。另外,从该示例中,对于回调函数,我们还认识到它往往具有一个外壳,如本例中的 test2_cal()就是外壳,和一个核心,即函数指针,如本例中的 s_cal函数指针。

回调对于编写库文件有很大的好处,比如我们要实现一个加法,但加法分很多种:整数的加法、字符串的加法、指针的加法等等。我们可以定义一个统一的 add(),并在 add()中定义一个函数指针s_add,通过更改s_add所指的函数,来适应多种数据类型的加法。这在C++ 中被称为“多态”,即根据输入的数据类型,调用符合该数据类型运算的函数。

同样的,在编写驱动程序时,由于不同的设备具备不同的特性,初始化时的内容可能不一样。使用回调函数,可以通过改变对应的函数指针的值,来指向不同的设备的初始化函数,实现能够兼容许多设备的驱动程序。

动态改变回调函数的实现的方法:

如上所示,回调函数是通过函数指针来调用的。因此想改变回调函数的功能,就是研究如何改变函数指针的值。主要有以下三种方法:

1)编译时直接赋值

如上一节所示的示例,通过对函数指针 s_cal赋值,可以改变函数 test2_cal()实际运行的计算。这在编译时就知道要将函数指针 s_cal赋予的值的情况下,可以使用。若在编译的时候不知道具体要将 s_cal赋予什么值,或者需要程序运行时动态地改变 s_cal的值,直接赋值的方法无法正常使用。

2)运行时实现动态注册

运行时,可以通过其他函数对函数指针进行赋值,在程序运行的时候,动态地改变函数的行为。

示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{
    printf("now is sum\r\n");
    return a + b;
}
static int cal_sub(int a, int b)
{
    printf("now is sub\r\n");
    return a - b;
}
static int cal_mul(int a, int b)
{
    printf("now is mul\r\n");
    return a * b;
}
static my_calculate_t s_cal = cal_sum;
static int test2_cal (int a, int b)
{
    int result = 0;
    if(s_cal) {
        result = s_cal(a ,b);
        printf("result=%d\r\n", result);
    }
    return result;
}
static void my_cal_calculate_register(my_calculate_t cal)
{
    s_cal = cal;
}
static void my_cal_calculate_unregister(void)
{
    s_cal = NULL;
}
void app_main(void)
{
    printf("init done\r\n");
    int m = 10, n = 2, ret;
    ret = test2_cal(m, n);
    my_cal_calculate_register(cal_sub);
    ret = test2_cal(m, n);
    my_cal_calculate_unregister();
    my_cal_calculate_register(cal_mul);
    ret = test2_cal(m, n);
    my_cal_calculate_unregister();
}

运行结果:

init done
now is sum
result=12
now is sub
result=8
now is mul
result=20

小结:上述程序通过函数 my_cal_calculate_register()动态地改变函数指针s_cal的值,从而实现在函数中动态地改变test2_cal功能的目的。一些库文件的源代码是不开放的,因此,一些库中使用这种通过动态注册函数的方法来动态地指定库函数实际功能。

3)作为函数参数传递到指定的函数内

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{
    printf("now is sum\r\n");
    return a + b;
}
static int cal_sub(int a, int b)
{
    printf("now is sub\r\n");
    return a - b;
}
static int cal_mul(int a, int b)
{
    printf("now is mul\r\n");
    return a * b;
}
static int test1_cal (my_calculate_t actual_cal, int a, int b)
{
    int result = 0;
    result = actual_cal(a ,b);
    printf("result=%d\r\n", result);
    return result;
}
void app_main(void)
{
    printf("init done\r\n");
    int m = 10, n = 2, ret;
    ret = test1_cal(cal_sum, m, n);
    ret = test1_cal(cal_sub, m, n);
    ret = test1_cal(cal_mul, m, n);
}

运行结果:

init done
now is sum
result=12
now is sub
result=8
now is mul
result=20

小结:上面的示例在调用 test1_cal() 时,通过传递要运行的运算函数,实现同一个函数test1_cal执行多个功能。同名函数传递不同参数执行不同的功能,也是增强函数兼容性的常见方法。

总结

本篇文章重点简述了回调、回调函数的概念。本质上,回调函数是 C 语言中 函数指针 的一种用法。回调函数用于在统一的接口内,实现可以动态地改变函数的功能。

回调函数的实现至少需要两个函数,一个是外壳,一个是可以通过函数指针指定实际的被执行的函数。

动态改变回调函数的实现的方法主要有三种:

  • 编译时直接赋值
  • 运行时实现动态注册
  • 作为函数参数传递到指定的函数内

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!     

原文链接:https://blog.csdn.net/wangyx1234/article/details/123775440

延伸 · 阅读

精彩推荐
  • C/C++C语言实现扫雷小游戏

    C语言实现扫雷小游戏

    这篇文章主要为大家详细介绍了C语言实现扫雷小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Stephen_curry_667122021-08-05
  • C/C++C++单链表实现大数加法

    C++单链表实现大数加法

    这篇文章主要为大家详细介绍了C++单链表实现大数加法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Alex山南水北11332021-09-08
  • C/C++贪心算法 WOODEN STICKS 实例代码

    贪心算法 WOODEN STICKS 实例代码

    贪心算法 WOODEN STICKS 实例代码,需要的朋友可以参考一下...

    C语言教程网2392020-12-07
  • C/C++C++特殊成员详解

    C++特殊成员详解

    这篇文章主要为大家介绍了C++特殊成员,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助,希望能够给你带来帮助...

    why are you so serious8482022-03-02
  • C/C++C++实现逆波兰式

    C++实现逆波兰式

    这篇文章主要为大家详细介绍了C++实现逆波兰式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Who_Am_I.7342021-09-30
  • C/C++详解散列表算法与其相关的C语言实现

    详解散列表算法与其相关的C语言实现

    这篇文章主要介绍了详解散列表算法与其相关的C语言实现,平时经常出现于各大考试竞赛与程序员面试题目当中,需要的朋友可以参考下...

    zinss269145812021-03-06
  • C/C++C++实现高校人员信息管理系统

    C++实现高校人员信息管理系统

    这篇文章主要为大家详细介绍了C++实现高校人员信息管理系统项,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    名名名名11602021-11-16
  • C/C++VC中删除类的两种操作方法

    VC中删除类的两种操作方法

    这篇文章主要介绍了VC中删除类的两种操作方法,较为详细的描述了在VC中实现删除类的具体步骤,非常具有实用价值,需要的朋友可以参考下 ...

    好人一个9112021-02-26