函数指针
函数指针就是指向函数的指针
类似于结构体和数组, 函数的的名字 就是这个函数保存的常量地址
可以通过函数指针指向指定的函数空间, 实现该函数的功能- 例如两个函数:
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; } }
优化代码中, 将功能全部在函数中实现, 也体现了面向对象的基本思想.