目录
一、函数指针变量
1、概念
2、函数调用
3、复杂的函数指针变量
分析:
结论:
二、typdef
普通类型重命名:
指针类型重命名:
三、函数指针数组
1、概念
2、转移表—函数指针数组的用途
模拟计算器
四、回调函数
1、概念
2、应用——模拟计算器
一、函数指针变量
1、概念
函数指针是指针,是指向函数的指针, 是存放函数地址的指针!!
对于函数而言,函数也是具有地址的,而函数指针变量就是存储函数地址的。
&函数名 和 函数名 都代表着地址的意思。
2、函数调用
如上所示,在使用指针变量进行函数调用的同时,我们还需要考虑传参的参数。
而 当pf3 拿去后,只留下 int (*)(int,int)时。
我们便知道,int (*)(int,int)是pf3这个函数指针变量的类型。
3、复杂的函数指针变量
void ( *signal(int , void(*)(int) ) ) ( int );
分析:
结论:
上面一串代码是一个函数声明。
signal 是一个函数名,它的有两个函数,分别是int类型的和返回值是void的参数是int的函数指针类型。
而signal这个函数的类型又是一个返回值是void的,参数是int类型的函数指针类型。
所以又有一种写法,但这种写法编译器是不会通过的:
void ( * ) ( int ) signal(int , void(*)(int) ) ;
意思便是,返回类型为void(*)(int)的函数signal ,它的两个参数,一个参数的类型是int,另一个参数的类型是void(*)(int)
二、typdef
typedef是用来重命名的,可以将复杂的类型简单化
普通类型重命名:
typdef unsigned int unint ;
int main()
{
unsigned int a = 0;
unint b = 0;
//a和b的类型是一样的
return 0;
}
指针类型重命名:
typdef int* ptr ;
int main()
{
int * p1;
ptr p2;
//p1和p2的类型是一样的
return 0;
}
但是对于函数指针变量的重命名又有所不同:
如上一个复杂的函数指针变量为点,signal的函数指针变量类型是void(*)(int)
而要进行重新命名的话,与其他命名方式有所不同。
typdef void(*) (int) ptr ; //错误!
typdef void(*ptr ) (int) ; //正确!
//意思是想把 函数指针变量类型 void(*) (int) 重新命名为ptr
ptr p1 ;
void(*p2) (int) ;
//p1和p2都是函数指针变量,且二者的函数指针变量类型都是一样的,都是void(*) (int)
//而后 void ( *signal(int , void(*)(int) ) ) ( int );
//又可以写为 ptr signal( int , ptr ) ;
相同,对于数组指针类型也如图函数指针类型的重命名方式一致:
typdef int (*)[10] parr; //错误!
typdef int (* parr)[10] ; //正确!
//int (*)[10] 是数组指针变量的类型
parr p1 [10] = &arr;
int (*p2)[10] = &arr;
// p1和p2的指针类型一致
三、函数指针数组
1、概念
函数指针数组:是数组,数组中存放的都是函数指针。
如图所示,parr[ ]中parr是一个数组名,而 int(*)(int, int)是一个函数指针变量的类型,二者相结合,就是一个函数指针数组。
而改数组中的每一个元素类型都是int(*)(int, int)
add和sub在数组中充当的是元素,也是函数指针,也是地址。
2、转移表—函数指针数组的用途
模拟计算器
加减乘除的函数调用。
主函数部分:
int main()
{
int input = 0;
do
{
int x = 0;
int y = 0;
int ret = 0;
printf("请选择");
scanf("%d",&input);
switch (input)
{
case 1:
printf("输入两个操作数");
scanf("%d %d",&x,&y);
ret = add(x, y);
break;
case 2:
printf("输入两个操作数");
scanf("%d %d", &x, &y);
ret = sub(x, y);
break;
case 3:
printf("输入两个操作数");
scanf("%d %d", &x, &y);
ret = mul(x, y);
break;
case 4:
printf("输入两个操作数");
scanf("%d %d", &x, &y);
ret = div(x, y);
break;
case 0:
printf("退出\n");
break;
defalut:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
使用了函数指针数组后:
int main()
{
int x = 0;
int y = 0;
int ret = 0;
int input = 0;
int(*parr[])(int, int) = { 0,add,sub,mul,div };//这一步操作就是转移表
printf("请选择");
scanf("%d", &input);//此刻的input是下标
do
{
if (input >= 1 && input <= 4)
{
printf("输入两个操作数");
scanf("%d %d", &x, &y);
ret = parr[input](x, y);//和函数指针的有些相似 函数指针:ret = pf(x,y)
printf("%d", ret);
}
else if (input == 0)
{
printf("退出\n");
break;
}
else
{
printf("输入错误请重新选择\n");
}
} while (input);
return 0;
}
转移表的局限性:调用函数中的参数的数据类型必须保持一致!
就列如:如果add的参数类型是int和int ,那么和add在同一个函数指针数组中的sub的调用参数类型也必须是int和int
四、回调函数
1、概念
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2、应用——模拟计算器
当我们需要使用add函数时,我们先调用了add函数的地址交予了cacl然后cacl的指针通过add的地址抵达了add函数并且返回了add中得到的数值。
int main()
{
int input = 0;
do
{
int x = 0;
int y = 0;
int ret = 0;
printf("请选择");
scanf("%d", &input);
switch (input)
{
case 1:
calc(add);//将add函数的地址传给calc函数,再有calc通过地址(指针)调用函数add
break;
case 2:
calc(sub);//将sub函数的地址传给calc函数,再有calc通过地址(指针)调用函数sub
break;
case 3:
calc(mul);//将mul函数的地址传给calc函数,再有calc通过地址(指针)调用函数mul
break;
case 4:
calc(div);//将div函数的地址传给calc函数,再有calc通过地址(指针)调用函数div
break;
case 0:
printf("退出\n");
break;
defalut:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}