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

来源:互联网 发布:51单片机cy怎么变 编辑:程序博客网 时间:2024/06/06 07:00

函数指针

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

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

    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; // 结构体 别名为Studentvoid 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
原创粉丝点击