爬爬爬之路:C语言(十一) 函数指针(回调函数)
来源:互联网 发布:51单片机cy怎么变 编辑:程序博客网 时间:2024/06/06 07:00
函数指针
函数指针就是指向函数的指针
类似于结构体和数组, 函数的的名字 就是这个函数保存的常量地址
可以通过函数指针指向指定的函数空间, 实现该函数的功能- 例如两个函数:
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; }}
优化代码中, 将功能全部在函数中实现, 也体现了面向对象的基本思想.
- 爬爬爬之路:C语言(十一) 函数指针(回调函数)
- C语言函数指针之回调函数
- 【C语言】二级指针,函数指针,回调函数
- C语言中的回调函数(函数指针)
- 函数指针中的回调函数(C语言版本)
- C语言回调函数与函数指针
- c语言-函数指针和回调函数
- C语言--函数指针和回调函数
- C语言函数指针与回调函数
- 【C语言】函数指针与回调函数
- C语言之最好理解的通过函数指针作为参数实现回调函数
- C语言中的指针之回调函数
- C/C++之回调函数---函数指针的作用
- C函数指针应用之回调函数
- C/C++之回调函数---函数指针的作用
- C/C++之回调函数---函数指针的作用
- C 函数指针 回调函数
- C 函数指针与回调函数
- JavaScript实现的图片循环播放
- 基于windows的多线程编程初步介绍
- Spark VS Hadoop
- Matlab 1-N之间的unique随机数
- 大数据环境部署7:SparkSQL配置使用
- 爬爬爬之路:C语言(十一) 函数指针(回调函数)
- 两个排序数组的中位数
- ios c语言学习第八天 ---指针
- 随即选题
- iOS开发网络篇—发送json数据给服务器以及多值参数
- lua用UTF-8处理汉字
- iOS开发网络篇—数据缓存
- OC第七天 --- NSDate Category Extension Protocol
- PhotoView与GifView的使用