嵌入式程序中的回调函数



    嵌入式程序中比较让人头疼的是回调函数的运用,下面说一下回调函数的意义,以帮助程序理解。

    我们写了两个用于向用户输出信息的函数,它们的功能基本相同,只是使用了不同的输出设备,假设为:

void Printer(char a)

{

使用打印机做输出;

}

void Screen(char b)

{

使用显示器做输出;

}

    我们检查自己的编程设备后发现它只连接了打印机,那么要做输出就只能使用打印机,假设有两个程序要这样做输出,就应该这样调用它:

void Program1(void)

{

 Printer (“开始输出”);

}

void Program2 (void)

{

 Printer (“开始输出”);

}

    这样看起来也挺好用的。但是,如果换了一个设备,我们发现它只有显示器,怎么办?你是不是觉得把这两个函数替换成Screen(“开始输出”)就可以了?没错,这样很有效。但是就是觉得有点不舒服,心中肯定惦记,如果下次又换个设备,并且有很多的输出调用,那岂不是很麻烦。

    于是需求出现了,有没有比较省事的办法,让我不用改整个程序而是只改一处就搞定。这可能首先想到的是使用#define,但它的局限性是可想而之的,一两个还行,一套程序下来全篇都是#define难于书写和扩展,而且也不professional。与#define类似的,C为我们提供了一个更高级的关键字typedef,它可以用来定义我们自己的类型,这有什么意义呢?这意义可大了,你觉得C给你提供的类型太少,不够用?没问题,给你自己定义的自由,随便去定义吧。通常我们用它来定义结构体类型,实现我们希望的各种封装。但是这里,我们用它来定义一个函数类型:

typedef void (*OutputFunc) (char c)

    这个OutputFunc代表的是void XX (char c)这种形式的一类函数,typedef的强大之处正在于此。

    但是只有类型是不够的,就像只有一个int,你依然没法做什么。下面我们要定义一个OutputFunc类型的变量,并且为它赋初值:

OutputFunc Myout = Printer;

    现在这个变量指向的就是Printer函数了。这样我们就通过OutputFunc这种函数类型统一了这一类函数的入口,我们以后再调用void XX (char c)这种形式的一类函数,只需要定义一个OutputFunc类型的变量并赋值给它,再调用这个变量就可以了,甚至这个变量还可以被用于参数传递,拥有极大的灵活性。刚才那个示例程序相应的要做一下修改:

void Program1(void)

{

 Myout (“开始输出”);

}

void Program2 (void)

{

 Myout (“开始输出”);

}

    这样无论有多少地方要怎么改,我们只要把Myout的赋值给改一下,就好了。甚至于它可以被用于参数传递,例如:

OutputFunc Myout = Screen;

void Program3(OutputFunc f)

{

 f (“开始输出”);

}

Program3(Myout);

    这样的调用简洁明了,极大的降低了耦合性,是程序猿们一直苦苦追求的境界。这就是回调函数的精髓,函数调用与被调用者通过中间变量被分隔开,互不关心。

    而这只是初级用法,更高级的用法是写成如下的形式:

typedef  struct {

           void (*Output) (char c)

   void (*Parse) (char c)

   char (*Input) (void);

   void (*Algorithm) (void);

} DeviceFunc;

    它定义了一个新的结构体类型DeviceFunc,它是一堆函数类型的集合,打包了设备全部的处理功能。接下来定义这种类型的两个变量:FuncA,FuncB,FuncC并分别赋值:

DeviceFunc FuncA;

FuncA. Parse =协议解析函数;

FuncA. Input =输入函数;

DeviceFunc FuncB;

FuncB. Output =输出函数;

FuncB. Input =输入函数;

DeviceFunc FuncC;

FuncC. Output =输出函数;

FuncC. Algorithm =算法处理函数;

    在用户的程序中,这样定义要传入的形参:

void Program1 (DeviceFunc IO)

{

 IO. Parse (“c”)

char c = IO. Input ()

}

void Program2 (DeviceFunc IO)

{

 IO. Output(“c”)

char c = IO. Input()

}

void Program3 (DeviceFunc IO)

{

 IO. Output(“c”)

IO. Algorithm ()

}

    接下来就是设备调用:

Program1(FuncA);  /*使用A功能的全部函数*/

Program2(FuncB);  /*使用B功能的全部函数*/

Program3(FuncC);  /*使用C功能的全部函数*/

    注意新定义的DeviceFunc结构体类型,它是一堆函数类型的集合,它不但打包了设备全部的处理功能,而且实现了统一的调用入口。它让调用者只需要操作传入的FuncAFuncBFuncC变量就可以使用它们各自所指向的设备的全部功能了,而且还可以定义其他变量,让更多的调用者去使用,并且调用者之间,调用者和被使用的设备之间都互不关心。

    其实回调函数就是一种封装,它把功能封闭在一个小圈子里,你只能看到它的两个接口:一个是类型,一个是它所指向的功能,去调用就行了,它会按照所指向的功能函数去执行动作,而用户不用再管具体执行的事。这在大型系统级程序中尤其有用,能够把用户与底层分隔开来,例如程序库的API很多都是这样给出来的,只给出了类和方法名,底层代码是怎么实现的谁都不知道。下面来张图看一下它的好处吧:



  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值