前言
继上一章【指针的进阶(1)】,继续完善第二章内容
一、函数指针
含义:指向函数的指针
1.1 函数指针的写法
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int arr[10] = { 0 }; printf("%p\n", Add); //函数名是函数的地址 printf("%p\n", &Add); //&函数名也是函数的地址 int(*pf)(int,int) = &Add;//函数指针 return 0; }
思路: 把函数地址存起来,存到变量pf里,pf是用来存放函数地址的,所以pf类型是函数指针。
函数指针写法:去掉指针变量pf,剩下的其实就是函数指针类型 int(*)(int,int) 左边是返回类型,右边是参数类型
和数组指针很像,int(ptr)[5]=&arr,去掉指针变量ptr,剩下的就是数组指针类型 int()[5]
1.2 函数指针的用法
可以通过指针间接访问函数,调用这个函数
int Add(int x, int y) { return x + y; } int main() { int (*pf)(int, int) = &Add; int r = Add(3, 5); printf("%d\n", r); int m = pf(4, 5); printf("%d\n", m); return 0; }
1.3 练习题
int main() { (*(void(*)()) 0)(); }
解读这段代码的意思是:
void( * )() 是函数指针类型
( void( * )() ) 把0强制类型转换成函数指针类型
* (void( * )() ) 是对地址解引用
* (void( * )() ) () 最后面的括号是调用这个函数
其实就是
1.将0强制类型转换成void(*)() 函数指针类型的地址
2.调用0地址处的函数
int main() { void ( * signal(int, void(*)(int) ) ) (int); return 0; }
解读这段代码的意思是:
- signal是一个函数声明
- signal(int, void( * )(int) )说明:signal函数有两个参数,第一个参数的类型是int,另一个参数的类型是void( * )(int)函数指针类型,该函数指针指向的函数有一个int类型,返回类型是void
- signal 函数的返回类型也是void( * )(int)函数指针类型,该函数指针指向的函数有一个int类型,返回类型是void
1.4 typedef简化代码
这段代码太过复杂,解读性不高,可以使用 typedef 可以简化
例如
typedef unsigned int uint;
对unsigned int重命名为uint
typedef int * ptr;
对指针重命名为ptr,int* p1;
等同于 ptr p2;
typedef void(*ptr_t)(int); ptr_t signal(int, ptr_t);
注意 指针函数重命名是在( * )里面换,如把 void(*)(int)重命名为ptr_t,写法是 void(*ptr_t)(int)。signal 函数的返回类型也是void( * )(int)函数指针类型,所以最后简写成了ptr_t signal(int, ptr_t);
二、函数指针数组
数组的每个元素是函数指针类型
类比
- int * arr[5]; 整型指针数组
- char * arr2[5]; 字符指针数组
2.1 函数指针数组的写法
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(*pf1)(int,int)= Add; int(*pf2)(int,int) = Sub; int(*pf3)(int,int) = Mul; int(*pf4)(int,int) = Div; return 0; }
存放四个函数的函数指针的类型都是一样的,使用函数指针数组更简便
写法如下:
int main() { int(*pfarr[4])(int, int) = { Add,Sub,Mul,Div }; return 0; }
2.2 函数指针数组的用处
写一个计算器,有加减乘除功能,一般的写法如下:
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 menu() { printf("***************************\n"); printf("***** 1.add 2.sub ******\n"); printf("***** 3.mul 4.div ******\n"); printf("***** 0.exit ******\n"); printf("***************************\n"); } 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("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = Add(x, y); printf("ret = %d\n", ret); break; case 2: printf("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = Sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = Mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("请输入两个操作数:"); 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; }
mani函数里有很多重复性的代码,过于冗余,如果要添加更多的函数进去,就要写更多的case语句,工作量大又麻烦。
而使用函数指针数组将大大提高效率
用函数指针数组的方式实现
思路:函数指针数组的使用 - 也叫转移表,意思是函数的下标对应一个数字,通过下标找到某个函数地址,继而调用某个函数。
在使用计算器的时候,通常会出现三种情况,第一种不想使用了,想退出计算器,第二种选择了正确的数字(函数的下标),实现计算;第三种选了大于函数下标的数字,则要重新选择。
对于三种情况,显然用if,else if,else语句最合适不过,因为数组的下标是从0开始的,那么可以让NULL占了0的下标,让函数从下标1开始,会更方便选择。
int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div}; // 0 1 2 3 4
先选择一个数字,如果数字大于0小于下标数字,则进入循环
if (input >= 1 && input <= 4)
再输入两个数字,进行计算,打印。
printf("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = pfArr[input](x, y); printf("ret = %d\n", ret);
如果选择的数字为0,则退出计算器;
else if(input == 0) { printf("退出计算器\n"); }
如果选的数字大于下标数字,则选择错误,重新选择。
else { printf("选择错误,重新选择\n"); }
想选择多次计算的话,把if,else if,else语句放在dowhile()循环里面进行,整个过程就可以循环起来。
具体代码如下:
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 menu() { printf("***************************\n"); printf("***** 1.add 2.sub ******\n"); printf("***** 3.mul 4.div ******\n"); printf("***** 0.exit ******\n"); printf("***************************\n"); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div}; // 0 1 2 3 4 do { menu(); printf("请选择:>"); scanf("%d", &input); if (input >= 1 && input <= 4) { printf("请输入两个操作数:"); scanf("%d %d", &x, &y); ret = pfArr[input](x, y); printf("ret = %d\n", ret); } else if(input == 0) { printf("退出计算器\n"); } else { printf("选择错误,重新选择\n"); } } while (input); return 0; }
总结
本章对函数指针和函数指针数组进行了详细地讲解,如果对您有帮助的话,不妨来一个关注吧!