指针作为c语言的重难点,想必也是让很多小伙伴在学习的时候叫苦不迭吧(bushi)。今天主要写一下指针中的函数指针以及使用函数指针的回调函数。
函数指针的妙用,我们将通过写一个计算器(有菜单,加减乘除)来get。这样的代码对于已经学到指针的我们来说应该是轻车熟路了吧,那就废话不多说直接上代码:
#include<stdio.h> void meun() { printf("************************************\n"); printf("********1.add 2.sub *********\n"); printf("********3.mul 4.div *********\n"); printf("*********0.exit ************\n"); printf("************************************\n"); } int add(int x, int y) { int z = x+y; return z; } int sub(int x, int y) { return x - y; } int mul(int x, int y) { return x * y; } int div(int x, int y) { return x / y; } int calc(int(*pf)(int, int))//参数为函数指针 { int x = 0; int y = 0; printf("请输入两个操作数:\n"); scanf("%d%d", &x, &y); int ret = pf(x, y); printf("%d\n", ret); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { meun();//调用菜单,根据input输入的值来确定下一步的函数调用 printf("请选择:\n"); scanf("%d", &input); switch(input) { case 1: printf("请输入两个操作数—>\n"); scanf("%d%d", &x, &y); ret=add(x, y); printf("%d\n", ret); break; case 2: printf("请输入两个操作数—>\n"); scanf("%d%d", &x, &y); ret=sub(x, y); printf("%d\n", ret); break; case 3: printf("请输入两个操作数—>\n"); scanf("%d%d", &x, &y); ret=mul(x, y); printf("%d\n", ret); break; case 4: printf("请输入两个操作数—>\n"); scanf("%d%d", &x, &y); ret=div(x, y); printf("%d\n", ret); break; case 0: printf("退出计算器\n"); break; default: break; } } while (input); return 0; }
这个时候我们就发现在每一个case后面都有相同代码,这样大量重复出现的代码使我们的代码太过冗余了,有没有什么解决办法呢?对于重复出现的代码我们通常将其封装成函数来让代码更简洁,但是封装函数的办法在这好像行不通,因为每一个case后面调用的函数都不相同,那怎么办呢?
ok,这个时候就体现函数指针的妙用了(当然也是可以使用函数指针数组的)。我们单独封装一个calc函数(参数为函数指针的函数)并把add,sub,mul,div这些函数作为参数传给calc函数。先上代码:
#include<stdio.h> void meun() { printf("************************************\n"); printf("********1.add 2.sub *********\n"); printf("********3.mul 4.div *********\n"); printf("*********0.exit ************\n"); printf("************************************\n"); } int add(int x, int y) { int z = x+y; return z; } int sub(int x, int y) { return x - y; } int mul(int x, int y) { return x * y; } int div(int x, int y) { return x / y; } int calc(int(*pf)(int, int))//参数为函数指针 { int x = 0; int y = 0; printf("请输入两个操作数:\n"); scanf("%d%d", &x, &y); int ret = pf(x, y); printf("%d\n", ret); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { meun();//调用菜单,根据input输入的值来确定下一步的函数调用 printf("请选择:\n"); scanf("%d", &input); switch(input) { case 1: calc(add); break; case 2: calc(sub); break; case 3: calc(mul); break; case 4: calc(div); break; case 0: printf("退出计算器\n"); break; default: break; } } while (input); return 0; }
这样代码是不是少了些,看起来更加简洁,如果使用函数指针数组来实现会更加简洁,此方法放在文末,因为顺着这个代码应该引入回调函数的概念。
这样通过函数指针调用的函数就是一个回调函数,它并不是由本身直接调用,而是在calc函数执行到适合的位置时,由calc函数调用。回调函数不但能使代码更简洁,也能使部分函数实现更加强大的功能,比如冒泡排序,以前我们写的冒泡排序只能排序整形数组,但引入回调函数后,可以改良它使它能实现任何数据类型的排序。
#include<stdio.h> struct Stu { char name[20]; int age; }; void Swap(char*buf1, char* buf2, int width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort(void*base, int sz, int width, int(*cmp)(const void*e1, const void*e2)) { int i = 0; //趟数 for (i = 0; i < sz - 1; i++) { int flag = 1;//假设数组是排好序 //一趟冒泡排序的过程 int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (cmp((char*)base+j*width, (char*)base+(j+1)*width)>0) { //交换 Swap((char*)base + j * width, (char*)base + (j + 1) * width, width); flag = 0; } } if (flag == 1) { break; } } } int cmp_stu_by_name(const void* e1, const void* e2) { //strcmp --> >0 ==0 <0 return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } int main() { struct Stu s[] = { {"zhangsan", 15}, {"lisi", 30}, {"wangwu", 25} }; int sz = sizeof(s) / sizeof(s[0]); bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name); }
只要改动bubble_sort的最后一个参数就可以实现排序不同类型的目标。这就是函数指针与回调函数的强大。当然肯定还有更多妙用之处等待我们去挖掘。
写在后面:
用函数指针数组实现计算器:
int main() { int input = 0; int x = 0; int y = 0; int ret = 0; //转移表 int (*pf[5])(int, int) = { 0,add,sub,mul,div };//有了这个函数指针数组就可以直接通过数组下标来调用函数 do { meun(); printf("请选择\n"); scanf("%d", &input); if (input == 0) printf("退出计算器\n"); else if (input >= 1 && input <= 4) { //通过input访问数组来调用函数 printf("请输入两个操作数\n"); scanf("%d%d", &x, &y); ret = pf[input](x, y);//pf是数组名 printf("%d\n", ret); } else { printf("输入错误\n"); } } while (input); return 0; }
我好想在文末说两句骚话啊,奈何文化太差,只能等我下次想到再说了。