C语言--回调函数

1、函数的运行

#include <avr/io.h>
void fun1(void)
{
	unsigned char i=0;
	while(1)
	{
		PORTB=i++;
		PORTC=0x01<<(i%8);
	}
}
int main(void)
{
	fun1();
}

首先,提出一个问题:如果要调用一个函数,真是只能以上面的方式进行吗?
No!我们还有一种方式,就是“用函数指针变量调用函数”
例子:用函数指针变量调用函数

#include <avr/io.h>
void fun1(void)
{
	unsigned char i=0;
	while(1)
	{
		PORTB=i++;
		PORTC=0x01<<(i%8);
	}
}

void (*pfun)(); //指向函数的指针

int main(void)
{
	pfun=fun1; //指向函数
	(*pfun)(); //运行指针所指向的函数
}

第二种,是“把指向函数的指针变量作函数参数”

#include <avr/io.h>
void fun1(void)
{
	unsigned char i=0;
	while(1)
	{
		PORTB=i++;
		PORTC=0x01<<(i%8);
	}
}
void RunFun(void (*pfun)()) //获得了要传递的函数的地址
{
	(*pfun)(); //在 RunFun 中,运行指针所指向的函数
}
int main(void)
{
	RunFun(fun1); //将函数的指针作为变量传递
}

这也就是回调函数!其本质就是将 CallBack函数 作为其他函数的参数传入,进行Call

2. 什么是回调函数?

回调函数,光听名字就比普通函数要高大上一些,那到底什么是回调函数呢?恕我读得书少,没有在那本书上看到关于回调函数的定义。我在百度上搜了一下,发现众说纷纭,有很大一部分都是使用类似这么一个场景来说明:A君去B君店里买东西,恰好缺货,A君留下号码给B君,有货时通知A君。感觉这个让人更容易想到的是异步操作,而不是回调。另外还有两句英文让我印象深刻:1) If you call me, I will call you back; 2) Don’t call me, I will call you. 看起来好像很有道理,但是仔细一想,普通函数不也可以做到这两点吗?所以,我觉得这样的说法都不是很妥当,因为我觉得这些说法都没有把回调函数的特点表达出来,也就是都看不到和普通函数到底有什么差别。不过,百度百科的解析我觉得还算不错(虽然经常吐槽百度搜索…):回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

下面先说说我的看法。我们可以先在字面上先做个分解,对于"回调函数",中文其实可以理解为这么两种意思:1) 被回调的函数;2) 回头执行调用动作的函数。那这个回头调用又是什么鬼?

先来看看来自维基百科的对回调(Callback)的解析:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. 也就是说,把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。关于同步和异步,这里不作讨论,请查阅相关资料。

再来看看来自Stack Overflow某位大神简洁明了的表述:A “callback” is any function that is called by another function which takes the first function as a parameter。 也就是说,函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。到此应该明白回调函数的定义了吧?

3. 为什么要使用回调函数?

很多朋友可能会想,为什么不像普通函数调用那样,在回调的地方直接写函数的名字呢?这样不也可以吗?为什么非得用回调函数呢?有这个想法很好,因为在网上看到解析回调函数的很多例子,其实完全可以用普通函数调用来实现的。要回答这个问题,我们先来了解一下回到函数的好处和作用,那就是解耦,对,就是这么简单的答案,就是因为这个特点,普通函数代替不了回调函数。所以,在我眼里,这才是回调函数最大的特点。来看看维基百科上面我觉得画得很好的一张图片。
在这里插入图片描述

#include<stdio.h>
#include<softwareLib.h> // 包含Library Function所在读得Software library库的头文件

int Callback() // Callback Function
{
    // TODO
    return 0;
}
int main() // Main program
{
    // TODO
    Library(Callback);
    // TODO
    return 0;
}

乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。现在再把main()、Library()和Callback()函数套回前面 F1、F2和F3函数里面,是不是就更明白了?

在来看一个例子

#include<stdio.h>

int Function_1() // Callback Function 1
{
    printf("Hello, this is Callback_1 ");
    return 0;
}

int Function_2() // Callback Function 2
{
    printf("Hello, this is Callback_2 ");
    return 0;
}

int Function_3() // Callback Function 3
{
    printf("Hello, this is Callback_3 ");
    return 0;
}

int Handle(int (*Callback)())
{
    Callback();
}

int main()
{
    printf("Entering Main Function. ");
    Handle(Function_1);
    Handle(Function_2);
    Handle(Function_3);
    printf("Leaving Main Function. ");
    return 0;
}

在这个例子中,表面上看就是在Handle里面调用了三次Function,看上去也可以直接将Handle写成

int Handle(void)
{
    Function_1();
    Function_2();
    Function_3();
}

这样好像也能实现相似的功能,但是
1、如果Handle是下面这样,在调用回调前后还有其他操作,这时采用回调函数的方式才能实现代码间的低耦合

int Handle(int (*Callback)())
{
    printf("Entering Handle Function. ");
    Callback();
    printf("Leaving Handle Function. ");
}

2、如果Function函数是A团队写的,Handle函数是B团队写的,C团队负责写main函数的逻辑,此时C团队不能修改其他团队的代码,也就不能修改Handle函数,同样B团队不知道C团队调用Function 1 2 3的先后顺序(123,321,213等),也不能直接将Handle写死,这时采用直接调用函数的方式也就无法实现回调函数的效果
明白了回调函数的特点,是不是也可以大概知道它应该在什么情况下使用了?没错,你可以在很多地方使用回调函数来代替普通的函数调用,但是在我看来,如果需要降低耦合度的时候,更应该使用回调函数。
此外,不仅仅是团队间协作时回调函数能够发挥巨大作用,在不同项目间进行代码移植的时候,回调函数也能极大减少工作量

4. 怎么使用带参数的回调函数?

#include<stdio.h>

int Callback_1(int x) // Callback Function 1
{
    printf("Hello, this is Callback_1: x = %d ", x);
    return 0;
}

int Callback_2(int x) // Callback Function 2
{
    printf("Hello, this is Callback_2: x = %d ", x);
    return 0;
}

int Callback_3(int x) // Callback Function 3
{
    printf("Hello, this is Callback_3: x = %d ", x);
    return 0;
}

int Handle(int y, int (*Callback)(int))
{
    printf("Entering Handle Function. ");
    Callback(y);
    printf("Leaving Handle Function. ");
}

int main()
{
    int a = 2;
    int b = 4;
    int c = 6;
    printf("Entering Main Function. ");
    Handle(a, Callback_1);
    Handle(b, Callback_2);
    Handle(c, Callback_3);
    printf("Leaving Main Function. ");
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值