各位CSDN的uu们你们好呀,今天小雅兰的内容仍旧是深度剖析指针噢,在上一篇博客中,我已经写过了字符指针、数组指针、指针数组、数组传参和指针传参的知识点,那么这篇博客小雅兰会讲解一下函数指针、函数指针数组 、指向函数指针数组的指针的知识点,现在,就让我们进入指针的世界吧
函数指针
函数指针数组
指向函数指针的数组
回调函数
函数指针
仍然是采用我们的类比法!!!
整型指针——指向整型的指针 int *
字符指针——指向字符的指针 char *
数组指针——指向数组的指针 int arr[10]; int (*p)[10]=&arr;
函数指针——指向函数的指针
数组指针中存放的是数组的地址
函数指针中存放的应该是函数的地址
那么,函数有地址吗?
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
//&Add和Add就是一样的,没有区别
int main()
{
printf("%p\n", Add);
printf("%p\n", &Add);
return 0;
}
输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
//&Add和Add就是一样的,没有区别
int main()
{
printf("%p\n", Add);
printf("%p\n", &Add);
//函数的地址要存起来,就得放在函数指针变量中
//pf就是函数指针
int (*pf)(int, int) = Add;
int ret = (*pf)(3, 5);
int ret = Add(3, 5);
int ret = pf(3, 5);
//这三种写法都是可以的
//pf前面的这颗*就是一个摆设
return 0;
}
下面,再来看看:
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
阅读两段有趣的代码:
源于《C陷阱和缺陷》
(* ( void (*)() ) 0 )();
- 将0强制类型转化为void (*)()类型的函数指针
- 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
- 调用0地址处的这个函数
其实这句代码的意思就是一次函数调用
void ( *signal ( int , void(*)(int) ) )(int);
signal括号里面只有类型,没有变量名,说明这是一个函数声明
void (*)(int) signal(int, void(*)(int));——可以这样理解
- 这句代码是一次函数声明
- 函数的名字是signal
- signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
- 该函数指针指向的函数参数是int,返回类型是void
- signal函数的返回类型也是一个函数指针
- 该函数指针指向的函数参数是int,返回类型是void
可以把这句代码简化一下:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);//将void(*)(int)重新起个别名叫pfun_t
注意:
typedef void (*pf_t2)(int);
//pf_t2是类型名
void (*pf)(int);
//pf是函数指针变量的名字
函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组, 比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
函数指针数组的使用:
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
int main()
{
//存放函数指针的数组——函数指针数组
int (*pf[4])(int, int) = { Add,Sub,Mul,Div };
//0 1 2 3
int i = 0;
for (i = 0; i < 4; i++)
{
int ret = pf[i](8, 4);
printf("%d\n", ret);
}
return 0;
}
下面,我们来写一个计算器,来完成整数的+ - * /
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
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("请选择:>\n");
scanf("%d", &input);
printf("请输入两个操作数:>\n");
scanf("%d %d", &x, &y);
switch (input)
{
case 1:
ret=Add(x, y);
break;
case 2:
ret=Sub(x, y);
break;
case 3:
ret=Mul(x, y);
break;
case 4:
ret=Div(x, y);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
printf("%d\n", ret);
} while (input);
return 0;
}
很轻松的,一个简易计算器的功能就实现了,我们来运行一下这个程序
但是这有问题啊!!!我选择了一个8,理应打印选择错误,而不应该打印请输入两个操作数呀!!!也不应该再打印这个ret!!!所以,我们要把程序修改一下!!!
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
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("请选择:>\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:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
这样一修改,功能实现才完全正确,但是,还是有问题,这个代码代码冗余的问题非常严重!!!如果未来要增加一些其他的功能,例如:<< >> & | && || 那么我们的代码会越写越长,case会·越写越多,这样显然是不太好的!!!
使用函数指针数组的实现:
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
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[])(int, int) = { 0,Add,Sub,Mul,Div };
// 0 1 2 3 4
do
{
menu();
printf("请选择:>\n");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
break;
}
else if (input >= 1 && input <= 4)
{
printf("请输入两个操作数:>\n");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("%d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
函数指针数组的用途:转移表
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组 ,数组的元素都是函数指针
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int main()
{
int (*pf)(int, int) = Add;
//函数指针数组
int (*pfArr[4])(int, int) = { Add,Sub };
//ppfArr是一个指向函数指针数组的指针变量
int (*(*ppfArr)[4])(int, int) = &pfArr;
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
之前我们实现了一个简易版计算器功能,但是那个代码的冗余问题非常严重,后面我们用函数指针数组的方法改造了一下这段代码,那现在,我们换一种新的方式来改造,就是我们的回调函数啦!!!
改造前:
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
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("请选择:>\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:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
改造后:
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
void menu()
{
printf("#######################################\n");
printf("#######1.Add 2.Sub################\n");
printf("#######3.Mul 4.Div################\n");
printf("#######0.exit ################\n");
printf("#######################################\n");
}
void Calc(int (*pf)(int,int))//函数指针
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入两个操作数:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("%d\n", ret);
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
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:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
也就是说:上述代码中的Add、Sub、Mul、Div都是回调函数!!!
之前用函数指针数组改造的那个代码实质上解决的是case语句过多的问题,而用回调函数改造的此代码实质上解决的是代码冗余的问题。
改造的两份代码解决的是完全不一样的问题,所以谈不上哪个方法更好!!!
其实回调函数还有更多内容,下一篇博客小雅兰带你玩转qsort,今天的内容就先到这里啦
好啦,告辞!!!