好久不见了各位,甚是想念啊!!!
五、函数指针
上次我们只是浅说了一下函数指针,我们知道了函数指针是函数的地址,&函数名和直接用函数名都能代表函数的地址
函数返回类型 (* 指针名称)(参数类型) = &函数名
指针类型:函数返回类型(*)(参数类型) = &函数名
当我们进行函数的调用的时候:我们不仅仅可以用函数名来调用函数了,我们可以解引用刚刚的函数指针来调用函数,当然我们不进行解引用也是可以正常调用的,因为函数名本身就可以代表函数的地址.
六、函数指针数组
指针数组:
char* arr[5] // 字符指针数组----数组-----存放的是字符指针
int* arr2[5] // 整形指针数组----数组------存放的是整形指针
那函数指针顾名思义就是数组-----存放的是函数指针
int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int main() { int (*pf1)(int, int) = &Add; int (*pf2)(int, int) = ⋐ //数组中存放类型相同的多个元素 int (* pfArr[4])(int, int) = {&Add, &Sub};//pfArr 是函数指针数组 - 存放函数指针的数组 return 0; }
那么这个到底有什么用呢?还这么的烧脑,绕来绕去的,那么我们举一个最简单的例子
倘若我们要实现一个计算器,我们一般的方式当然是写四个加减乘除的函数然后分别调用,代码如下:
int Add(int x, int y) { return x + y; } 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 main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Add(x, y); printf("ret = %d\n", ret); break; case 2: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出计算器\n"); break; default: printf("选择错误, 重新选择\n"); break; } } while (input); return 0; }
我们可以看到运行结果也可以正常的实现计算机功能,但是不好,不好到哪了呢?代码有冗余的地方,就是下面红色圈起来的地方 :
而且还有一个不好的地方就是如果我想要再添加别的函数,那么我还是要写一边一摸一样的四行代码,如果是稍微复杂的函数功能,代码可读性就会下降。
那我们用函数指针数组的方式再来改进一下,我们把加减乘除的四个函数的函数地址放到一个函数指针数组里面,代码实现如下:
void menu() { 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) { return x + y; } 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 main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); //函数指针数组 - 转移表 int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div}; // 0 1 2 3 4 if (0 == input) { printf("退出计算器\n"); } else if (input >= 1 && input <= 4) { printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = pfArr[input](x, y); printf("ret = %d\n", ret); } else { printf("选择错误,重新选择!\n"); } } while (input); return 0; }
可以看到效果依旧是计数器,但是消除了代码冗余,代码更加简洁,并且如果我们想要继续添加新功能的话,只需要将函数的地址增添到函数指针数组中就好了。
七、指向函数指针数组的指针(拓展一下)
刚刚我们提到了函数指针数组,它是用来存放函数指针的一种数组,那么是数组就会有地址,那么将这个地址取出来,那这个地址就是指向函数指针数组的指针
但这个了解就好,实例中几乎不会用
八、回调函数
回调函数就是一个通过函数指针调用的函数,如果你把函数指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时有另外的一方调用的,用于对该事件或条件进行响应。
概念很不好懂,我们直接上例子。
还是刚刚的计数器的例子,我们刚刚说代码有冗余
然后我们想能不能定义一个函数,然后每次调用函数,只用调用那一个函数就可以实现加减乘除该多好啊,于是我们有了以下代码:
这里的calc函数就是一个回调函数,他一个函数可以实现多个函数的功能,只需要把想要的函数地址传过去,然后calc就会帮你调用它来实现想要的功能,源代码和编译结果如下:
void menu() { 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) { return x + y; } 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; } void calc(int (*pf)(int,int)) { int x = 0; int y = 0; int ret = 0; printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = pf(x, y); printf("ret = %d\n", ret); } int main() { int input = 0; do { menu(); printf("请选择:>"); 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: printf("选择错误, 重新选择\n"); break; } } while (input); return 0; }
回调函数最典型的例子就是qsort函数,大家可以跳转到我的qsort函数的文章中来进一步学习(http://t.csdnimg.cn/3vGtz) 。
那么今天对于指针的更深一步学习就到这里了,感谢大家的观看。