C语言 回调函数

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简言之,回调函数就是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用指向的函数时,我们就说这个函数是回调函数。

在高级语言中,回调函数也就是观察者模式的一种应用。本质上都是“你想让别人的代码执行你的代码,而别人的代码你又不能动”这种需求下产生的。

很文艺的解释:

1.告诉我一些关于你的事情,在需要的时候我能够找到你。
2.把电话号码留下,我Call(回调)你
3.诸葛亮给赵子龙一个锦囊,吩咐他危急时打开按锦囊指示办,锦囊里的命令就是回调函数,危急时刻就是回调的时机,不同的锦囊里可以有不同的命令。

通俗一些的解释:

我们对回调函数的使用无非是对函数指针的应用,函数指针的概念本身很简单,但是把函数指针应用于回调函数就体现了一种解决问题的策略,一种设计系统的思想。

那么什么是回调函数呢,那是不得以而为之的设计策略,想象一种系统实现:在一个下载系统中有一个文件下载模块和一个下载文件当前进度显示模块,系统要求实时的显示文件的下载进度,想想很简单在面向对象的世界里无非是实现两个类而已。但是问题恰恰出在这里,显示模块如何驱动下载进度条?显示模块不知道也不应 该知道下载模块所知道的文件下载进度(面向对象设计的封装性,模块间要解耦,模块内要内聚),文件下载进度是只有下载模块才知道的事情,解决方案很简单给下载模块传递一个函数指针作为回调函数驱动显示模块的显示进度。

比如打印一堆数据,我只管要打印的数据,至于怎么打印,我不管,回调打印函数就可以了。

在面向对象的世界中这样的例子还真不少,造成这样的问题的根源,相信大家已经从上面的叙述中体会到了,就是面向对象的程序设计思想,就是设计模式中要求的模块独立性,高内聚低耦合等特性。

封装变化的编程策略给编程人员第一位的指导思想就是面向接口编程,即设计模式中提到的面向虚拟编程而不是面向实现。这样的编程思想极大地革新了编程世界,可以说没有这一原则就没有面向对象的程序设计,这一原则给程序设计一种指导思想: 即如何更高的将现实模型映射成程序模型。这样的设计思想在极大地催生高 度独立性模块的同时削弱了模块间的协作性,也就是耦合性,它使得模块间更多的从事着单向的调用工作,一个模块需要某种服务就去找另一个模块,这使得程序呈 现出层次性,高层通过接口调用底层,底层提供服务。但是现实世界中严格遵循现层次特性的系统是很少见的,绝对的MVC是不存在的,因为更多的模块要求通并 协作,可见没有耦合就没有协作没有好的调用关系,耦合真的不是错。

既然我们需要模块间的协作,同时我们又厌恶的摒弃模块间你中有我我中有你的暧昧关系那如何生成系统呢,答案是函数指针(不一定一定是函数指针)也就是使用 回调的方式。如果一个对象关心另一个对象的状态变化那么给状态的变化注册回调函数让它通知你这类状态的改变,这样在封装了模块变化的同时实现了模块间的协作关系另辟独径的给对象解耦。

例子1:

#include <stdio.h>

void PrintNum(int n);
void ShowNum(int n,void (* ptr)());

void PrintMessage1();
void PrintMessage2();
void PrintMessage3();
void ShowMessage(void (* ptr)());

int main(){
   ShowNum(11111,PrintNum);
   ShowNum(22222,PrintNum);
   ShowMessage(PrintMessage1);
   ShowMessage(PrintMessage2);
   ShowMessage(PrintMessage3);
}

void PrintNum(int n){
   printf("Test1 is called,the number is %d\n",n);
}

void ShowNum(int n,void (* ptr)()){
   (* ptr)(n);
}


void PrintMessage1(){
   printf("This is the message 1!\n");
}

void PrintMessage2(){
   printf("This is the message 2!\n");
}

void PrintMessage3(){
   printf("This is the message 3!\n");
}

void ShowMessage(void (* ptr)()){
    (* ptr)();
}

输出为:

Test1 is called,the number is 11111
Test1 is called,the number is 22222
This is the message 1!
This is the message 2!
This is the message 3!

例子2:

#include<stdio.h>
    // 方法指针的格式为:int (*ptr)(char *p) 即:返回值(指针名)(参数列表)
    typedef int (*CallBackFun)(char *p); // 为回调函数命名,类型命名为 CallBackFun,参数为char *p
    // 方法 Afun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun   
    int Afun(char *p)
    {
        printf("Afun 回调打印出字符%s!\n", p);   
        return 0;
    }

    // 方法 Bfun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun
    int Cfun(char *p)
    {   
        printf("Cfun 回调打印:%s, Nice to meet you!\n", p);   
        return 0;
    }

    // 执行回调函数,方式一:通过命名方式
    int call(CallBackFun pCallBack, char *p)
    {   

        printf("call 直接打印出字符%s!\n", p);   
        pCallBack(p);   
        return 0;
    }

    // int call2(char *p, int (*ptr)(char *p))
    // 执行回调函数,方式二:直接通过方法指针   

    int call2(char *p, int (*ptr)())
    {

        printf("=======%s=======\n",p);    
        (*ptr)(p);
    }

    // 执行回调函数,方式三:通过命名方式
    int call3(char *p, CallBackFun pCallBack)
    {
        printf("------%s--------\n", p);   
        pCallBack(p);
    }

    int main()
    {   
        char *p = "hello";
        call(Afun, p);   
        call(Cfun, p);
        call2(p, Afun);   
        call2(p, Cfun);
        call3(p, Afun);   
        call3(p, Cfun);
        return 0;

    }

输出为:

call 直接打印出字符hello!
Afun 回调打印出字符hello!
call 直接打印出字符hello!
Cfun 回调打印:hello, Nice to meet you!
=======hello=======
Afun 回调打印出字符hello!
=======hello=======
Cfun 回调打印:hello, Nice to meet you!
——hello——–
Afun 回调打印出字符hello!
——hello——–
Cfun 回调打印:hello, Nice to meet you!

例子3:

#include <stdlib.h>
#include <stdio.h>
int Test1()
{
    int i;
    for(i=0; i<30; i++)
    {
        printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));
    }
    return 0;
}

int Test2(int num)
{
    int i;
    for (i=0; i<num; i++)
    {
        printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));
    }
    return 0;
}

void Caller1( int (*ptr)() )//指向函数的指针作函数参数
{
    (* ptr)();
}

void Caller2(int n, int (*ptr)())//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的,
{
    //不能写成void Caller2(int (*ptr)(int n)),这样的定义语法错误。
    (* ptr)(n);
}

int main()
{
    printf("************************\n");
    Caller1(Test1);     //相当于调用Test1();
    printf("&&&&&&************************\n");
    Caller2(30, Test2); //相当于调用Test2(30);
    return 0;
}

例子参考文章:
1. C语言回调函数一个简单的例子
http://blog.csdn.net/zgrjkflmkyc/article/details/9198519
2. 一个c回调函数的例子
http://blog.csdn.net/hanchaoman/article/details/8013511
3.c语言之回调函数
http://blog.csdn.net/xlhcgd/article/details/45040195

当我们想通过一个统一的接口实现不同的内容时,用回调函数就比较合适。

任何时候,如果所编写的函数必须在不同的时刻执行不同类型的工作或者执行由函数调用者定义的工作,我们就可以用回调函数实现。

许多窗口系统就是使用多个回调函数连接动作,如拖拽鼠标和点击按钮来指定调用特定的函数完成工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值