zoukankan      html  css  js  c++  java
  • 函数指针与回调函数新感悟

    2021年5月22日12:57:47

    关于函数指针的理解可以参考之前写的博文。

    对于一个指针而言,我们需要考察它的几点性质:

    • 1、这个指针是什么类型的?
    • 2、这个指针的大小是多少?
    • 3、这个指针是什么时候指向什么地址的?

    其实,我们对于一个指针一般只关注两点,1和3.

    对于1,肯定是在声明的时候就知道这个指针应该指向什么类型。
    对于2,肯定是在赋值的时候知道它指向对应的类型的地址的。
    对于3,指针的大小和系统有关,一般的系统,指针大小为4字节,也就是和int类型的大小一样。

    明确以上1,2点之后,我们对照着int *来看就会很明白了。

    int a = 0;
    int * p;
    p = &a;
    

    那么接下来我们聊聊函数指针。
    现在再回顾一下,我用自己的语言来描述一下什么是函数指针。
    函数指针 本质是一个指针。
    结合上面关于指针的2点需要关注的点,我们对照一下函数指针该怎么去解释或者说理解。

    像声明int类型的指针一样,我们应该声明一个函数类型的指针。
    这个时候的疑问是,系统里没有我们需要的函数类型啊。
    那怎么办?
    别忘了一件事:
    C语言为我们提供了声明自定义类型的工具:那就是typedef关键字。
    我们一般使用这个关键字做结构体的声明,在声明结构体的时候还要结合另外一个关键字struct。

    typedef struct person_s { //这里后缀s是认为添加的,我们可以理解成使用person_s这个结构体时候,必须和struct一起使用,因为现在的结构体名字是struct person_s 而不是person_s
        int age;
        char sex;
        double height;
        double weight;
    };
    

    从上面可以看到typedef的一种用法就是用来声明结构体。
    还有另外一种就是为类型取别名。

    typedef struct person_s person_t;
    

    通过上面这个语句,我们就成功的为struct person_s 取了一个别名,也就是一个外号:叫person_t。 这个时候我们在需要使用struct person_s 的地方可以使用person_t进行替换了,是不是方便很多?

    ok。
    到这里我们认识了typedef的取别名的用法。
    回到正题。声明函数类型的指针,肯定需要一个函数类型了。
    那么我们就用typedef来声明一个函数为了就行了。

    typedef void * (*FP)(int *, int);
    

    上面这句话就是为函数void * (*)(int *, int);取了一个别名叫FP。

    那么我们就可以使用fp去声明一个指针了。

      FP *fp1;  //这样fp1就必须指向一个形如void * (*)(int *, int)的函数。
      int a[] = {1,2,3,4,5,6,7,8};
      int n = 3;
      void * sumArray(a, n);//此函数在其他地方声明和实现的,计算一个数组前n的和并返回该值的地址(这个例子不好,主要是返回内容是一个地址了)
      //上面这个函数就是FP类型的。我们可以使用fp1来替换sumArray函数进行调用
      fp1 = sumArray;
      fp1(a,n);//这样就可以和直接调用sumArray一样的效果。
    

    好,从上面的例子,我们隐约能够觉得,函数指针是可以被赋值的,也是可以当做被赋值过来的函数进行调用的。
    慢慢品味这里的逻辑。
    有了这个意识之后,我们可以更大胆一点。
    如果在一个结构体里声明了一个函数指针,那么会发生什么?
    看例子:

    typedef void (*EAT)(char *, int);//吃的方法,传入水果的名字和数量,表示吃了几个XX水果
    void (*_eat)(char * name, int n){
        //吃了n个name水果
    }
    typedef struct person_s{
        int age;
        EAT *eat;//
    }person_t;
    
    person_t *xiaoming = (person_t)malloc(sizeof(person_t));//给xiaoming申请一块内存
    //开始给xiaoming赋值
    xiaoming->age =18;
    xiaoming->eat = _eat;//注意这里为xiaoming的eat方法实际赋值了,赋值为_eat方法,看清这里,是直接把函数名赋值给对应的指针
    
    //程序某处需要调用xiaoming的eat方法
    char *name = "Orange";
    int n = 2;
    xiaoming->eat(name,n); //开始执行_eat函数。
    
    

    这个函数的赋值,和在响应需要的时候调用指定函数显示不同的功能的过程,就叫做函数的回调。这个函数就是回调函数。

  • 相关阅读:
    Android Studio 单刷《第一行代码》系列 05 —— Fragment 基础
    Android Studio 单刷《第一行代码》系列 04 —— Activity 相关
    Android Studio 单刷《第一行代码》系列 03 —— Activity 基础
    Android Studio 单刷《第一行代码》系列 02 —— 日志工具 LogCat
    Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld
    IDEA 内网手动添加oracle,mysql等数据源,以及server returns invalid timezone错误配置
    eclipse maven设置
    IntelliJ IDE 常用配置
    eclipse maven 常见问题解决方案
    Maven 安装和配置
  • 原文地址:https://www.cnblogs.com/dhu121/p/14798663.html
Copyright © 2011-2022 走看看