爬爬爬之路:C语言(十一) 函数指针(回调函数)

函数指针

函数指针就是指向函数的指针

  • 类似于结构体和数组, 函数的的名字 就是这个函数保存的常量地址
    可以通过函数指针指向指定的函数空间, 实现该函数的功能

    1. 例如两个函数:
    int Sum(int a, int b);
    int Sub(int a, int b);

    定义这两个函数的函数指针的声明方法:
    int (*p)(int a, int b) (参数名可以省略, 写成 int(*p)(int, int), 下面统一省略参数名)
    这个函数指针的类型是:
    int (*)(int a, int b)
    这个函数指针可指向的函数类型:
    int (int a, int b)
    也就是说, int (*p)(int , int) 可以指向一切有两个int 型参数 返回值是int型的函数
    用这个函数指针指向上述函数的方法:

    p = Sum;  // 函数指针p 指向函数名为Sum的函数
    p = Sub;  // p重指向函数名为Sub的函数

    调用方法:
    用函数指针代替函数名进行调用
    如:

    int sum = Sum(5, 6);

    可以直接等价替换成

    int (*p)(int,int) = Sum;
    int sum = p(5, 6);
  • 定义无参函数指针并使用 的方法同上
    如:

    void printHello(){
        printf("hello");
    }

    定义指向printHello()函数的函数指针如下:
    void (*pr)() = printHello;
    注意无参函数的括号不能省略
    调用方法如下:
    pr();
    用指针函数指向同类型的函数, 动态调用的方法例题代码如下:

    int Sum(int a, int b) {
        return a + b;
    }
    
    int Max(int num1, int num2) {
        return num1 > num2 ? num1 : num2;
    }
    
    int main(int argc, const char * argv[]) {
    
        int (*p)(int, int) = NULL;
    
         while (1) {
            printf("两个数:5, 3.请输入功能(maxValue或者sum)完成计算:\n");
            char *str = malloc(50);
            scanf("%s", str);
            BOOL right = NO;
            if (strcmp(str, "maxValue") == 0) {
                p = Max;
                printf("maxValue = ");
                right = YES;
            } else if(strcmp(str, "sum") == 0) {
                p = Sum;
                printf("sum = ");
                right = YES;
            } else {
                printf("看提示, 煞笔\n");
            }
            free(str);
            str = NULL;
            if (right) {
                printf("%d\n", p(5 ,3));
            }
        }
        return 0;
    }
    

      例题代码中, 函数指针p, 通过输入方法名, 让系统判断用户想要p指向的函数. 然后动态编译, 让p指向指定函数, 并完成相应的功能. 这就是函数指针的功能,通过调用函数指针, 可以指定选择完成某个功能(前提是这些功能函数的类型得相同).


回调函数
  • 将函数指针当成参数的函数, 即可完成回调函数的功能.
    如定义声明函数如下形式:
    int getValue(int a, int b, int (*p)(int, int));
    回调函数的核心用途是为了函数代码的封装. 同时可以节省代码量, 根据需求动态选择需要的函数, 具有极强的选择性.
    比如, getValue函数是某个技术的核心代码, 函数指针p 指向的是一个普通的功能函数. 只需要通过修改p 指向的函数, 即可在核心代码不需要修改的情况下完成核心功能, 节省了代码维护的成本和调试的工作量
    例题如下:

    typedef struct { 
        char name[50]; // 姓名
        int age;       // 年龄
        float score;   // 分数
        int studentId; // 学号
    } Student; // 结构体 别名为Student
    
    void printStudent(Student *stu) {
            printf("姓名:%s\t, 年龄:%d, 分数:%.2f, 学号:%d\n", stu->name, stu->age, stu->score, stu->studentId);
    }
    
    void printStudents(Student *stu, int count) { // 打印count个学生的信息
        for (int i = 0; i < count; i++) {
            printStudent(stu + i);
        }
    }
    
    
    BOOL conditionsByName(Student *stu, Student *stu2) { // 按姓名排序的条件判断函数
        int num = strcmp(stu->name, stu2->name); // 比较两个学生的姓名
        if (num > 0) {                         
            return YES;
        }
        return NO;
    }
    BOOL conditionsByAge(Student *stu, Student *stu2) {  // 按年龄排序的条件判断函数
        return stu->age > stu2->age;
    }
    BOOL conditionsByScore(Student *stu, Student *stu2) { // 按分数排序的条件判断函数
        return stu->score < stu2->score;
    }
    BOOL conditionsByStudentId(Student *stu, Student *stu2) { // 按学号排序的条件判断函数
        return stu->studentId > stu2->studentId;
    }
                                   // 排序的主体函数, 回调函数为4个条件判断函数的其中之一
    void sortStudents(Student *stu, int count, BOOL (*p)(Student*,Student*)) {
        for (int i = 0; i < count - 1; i++) {
            for (int j = 0; j < count - 1 - i; j++) {
                if (p(stu + j, stu + j + 1)) {
                    Student temp = stu[j];
                    stu[j] = stu[j + 1];
                    stu[j + 1] = temp;
                }
            }
        }
        printStudents(stus, count);
    }
    int main(int argc, const char * argv[]) {
        Student stu1 = {"xiaoming", 18, 88.0, 1000};       // 在main函数中给5个学生信息
        Student stu2 = {"jingfeng", 21, 87.0, 1008};
        Student stu3 = {"luchong", 23, 68.0, 1006};
        Student stu4 = {"qiyong", 24, 80.0, 1002};
        Student stu5 = {"liujingchao", 19, 98.0, 1001};
        Student stu[] = {stu1, stu2, stu3, stu4, stu5};
        printStudents(stu, 5);
        while (1) {
            BOOL (*p)(Student*,Student*) = NULL;            // p为指向条件函数的指针
            printf("请输入排序的编号: 1.按姓名, 2.按年龄排序, 3.按分数, 4.按学号\n");
            int num = 0;
            scanf("%d",&num);
            switch (num) {                           // 根据不同的需求, 指针指向不同的函数地址
                case 1:
                    p = conditionsByName;
                    break;
                case 2:
                    p = conditionsByAge;
                    break;
                case 3:
                    p = conditionsByScore;
                    break;
                case 4:
                    p = conditionsByStudentId;
                    break;
                default:
                    printf("看提示, 傻叉");
                    break;
            }
            if (p != NULL) {
                sortStudents(stu, 5, p);     // 排序并打印结果
            }
        }
    }

      上述代码完成的功能是, 将5个保存在结构体中的学生信息按某个排序方式排序, 排序方式由用户定义.
      

  • 给函数指针的类型起别名
    以上述代码中的条件函数为例:
    typedef BOOL (*SORT)(Student*, Student*);
      给类型为带两个Student*类型的参数, 返回值为BOOL值的函数定义函数指针的别名, 别名为SORT.
      此时可以将函数指针的声明BOOL (*p)(Student*,Student*) = NULL;改写成SORT p = NULL;
      同理, 上述代码中的函数名:  

    void sortStudents(Student *stu, int count, BOOL (*p)(Student*,Student*));

    改写成

    void sortStudents(Student *stu, int count, SORT p);

    后者明显比前者简洁了许多, 且函数主体部分不需要修改
    上述例题代码可以根据需求:输入排序条件, 根据输入的条件选择相应的条件函数. 进行代码优化:
    优化代码如下:

    typedef struct {
        char name[50];
        int age;
        float score;
        int studentId;
    } Student;
    
    // 具体实现代码与上段例题代码相同
    BOOL conditionsByName(Student *stu, Student *stu2);
    BOOL conditionsByAge(Student *stu, Student *stu2);
    BOOL conditionsByScore(Student *stu, Student *stu2);
    BOOL conditionsByStudentId(Student *stu, Student *stu2);
    typedef BOOL (*SORT)(Student*, Student*); 
    typedef struct {  // 定义一个结构体, 将输入条件和选择函数联系成一个整体
        char name[50];
        SORT p;
    } Conditions;
               // 新定义一个函数, 用来判断输入的条件名称, 并返回相应的方法地址
    SORT getFunctionByName(char *name) { 
        // 创建结构体, 并放进数组     也可以在函数外部声明这个结构体数组, 并把它当成参数传进本函数
        Conditions condition1 = {"name", conditionsByName};
        Conditions condition2 = {"age", conditionsByAge};
        Conditions condition3 = {"score", conditionsByScore};
        Conditions condition4 = {"id", conditionsByStudentId};
        Conditions conditions[] = {condition1, condition2, condition3, condition4};
        for (int i = 0; i < 4; i++) {
            if (strcmp(name, conditions[i].name) == 0) {
                return conditions[i].p;
            }
        }
        return NULL;
    }
    
    void sortStudents(Student *stu, int count, SORT p);
    int main(int argc, const char * argv[]) {
        Student stu1 = {"xiaoming", 18, 88.0, 1000};
        Student stu2 = {"jingfeng", 21, 87.0, 1008};
        Student stu3 = {"luchong", 23, 68.0, 1006};
        Student stu4 = {"qiyong", 24, 80.0, 1002};
        Student stu5 = {"jingchao", 19, 98.0, 1001};
        Student stu[] = {stu1, stu2, stu3, stu4, stu5};
        printStudents(stu, 5);
        while (1) {
            SORT p = NULL;
            printf("请输入排序的条件:(name, age, score, id)\n");
            char *str = malloc(50);
            scanf("%s",str);
            p = getFunctionByName(str);
            if (p != NULL) {
                sortStudents(stu, 5, p);       // 根据选择的条件进行排序
            } else {
                printf("看提示, 傻叉!\n");
            }
            free(str);
            str = NULL;
        }
    }

    优化代码中, 将功能全部在函数中实现, 也体现了面向对象的基本思想.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值