带你深入了解指针(2)

时间:2023-01-27 19:56:24

本篇文章继续与大家讲讲指针的一些知识,不过呢,在讲之前,分析一下以下两串代码,简单的检测一下我们的学习效果。

检测

第一串代码:(* ( void(*)() ) 0 )()

(* ( void(*)() ) 0 )()
//这行代码是函数的调用
//调用的是0地址处的函数
//为什么呢?让我们来分析一下
//首先,void(*)()是一个函数指针,而( void(*)() )则是把0强制转换成,
//类型为void(*)()的函数指针
//紧接着,*( void(*)() )0表示对0进行解引用拿到0地址处的函数,最后就是调用

第二串代码:void(*signal( int,void(*)(int) ) )(int)

void(*signal( int,void(*)(int) ) )(int);
//这行代码是对函数进行声明
//声明的函数的名字是signal
//为什么呢?同样的,我们一起分析一下
//首先,signal先与后面的括号结合(*的优先级小于括号),
//得到signal( int,void(*)(int)
//这很明显是一个函数,函数的参数有两个,一个参数是整型,另一个参数是函数指针
//函数的参数有了,是不是缺一个返回值,我门把函数的名字和参数去掉
//得到void(*)(int),这表示该函数的返回值是一个函数指针,该指针指向的函数
//返回类型是void,参数是int。

好了,小小的检测完了。我继续讲讲指针一些知识。

函数指针数组

我们已经学习了函数指针,本篇文章,我们来学习一下函数指针数组。函数指针数组怎么定义呢?我们就以函数Add为例先定义一个函数指针。

//函数Add
void Add(int)
{;}
//首先,取出Add的地址,赋给pf变量
pf=&Add;
//pf是一个指针,加上*来表示
*pf=Add;
//该指针指向的函数类型是void (int),由于*的优先级小于括号
//所以,我们不能直接这样写void *pf (int),这样会使pf先与后面的括号结合,
//就不是指针了,变成了一个函数,所以,我们要加上括号把*pf括起来
void(*pf)(int);//这样才是正确的书写
//现在我们需要一个容纳10个元素的数组,之前说过,数组的名字加方括号大小就表示
//一个数组,所以,pf[10]就表示一个容纳10个元素的数组,而我们的数组要存放
//的元素类型是void (*) (int) ,所以,就给数组加上相应的类型,就得出我们的
//对应的函数指针数组了
void (*pf[10])(int); //也就是在pf后面加上[10],让pf与[10]先结合形成数组
//而数组中每一个元素的类型是void(*)(int)

好了,我们函数指针数组的定义就讲完了,不知道我有没有讲明白,欢迎在评论区留言,接下来,我们讲讲函数指针数组的指针。

函数指针数组的指针

函数指针既然有数组,那么它也会有与之对应的指针。那它该如何去定义呢?接下来,让我们一起去了解一下。

//首先,先定义一个函数指针数组
void(*pf[10])(int);
//同样的操作,取出pf的地址,赋给变量ppf
ppf=&pf;
//它是一个指针,所以,加上*号来表示
*ppf=&pf;
//该指针指向的空间所储存的数据的类型是void(* [])(int),所以,加上这个类型
//就得到了我们的函数指针数组的指针
void(*(*ppf)[10])(int);
//学会了之后,我就直接在原基础上改造就好了,给void(*pf[10])(int)中的pf
//加上个p,再加上一个*,最后用括号括起来就得出结果了。方法有很多种,用合适
//自己的方法,敲正确的代码。以上两种方法,仅供参考。

说了这么多,我们来补充一下函数指针数组的用途:

函数指针数组的用途

//我们先简单看看以下这串简易计算器的代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
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.exist ****\n");
printf("********************\n");
printf("请输入:>");
}
int main()
{
int n,a,b,ret;

do
{
menu();
scanf("%d", &n);
switch (n)
{
case 1:
printf("请输入两个操作数:>");
scanf("%d%d",&a,&b);
ret = Add(a, b);
printf("%d\n", ret);
break;
case 2:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Sub(a, b);
printf("%d\n", ret);
break;
case 3:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Mul(a, b);
printf("%d\n", ret);
break;
case 4:
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = Div(a, b);
printf("%d\n", ret);
break;
case 0:
printf("退出计算机\n");
break;
default:
printf("输入错误\n");
}
} while (n);
return 0;
}
//上面的程序使用了switch语句,我们再简单的看一下,我们使用函数指针数组优化后
//的代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void menu()
{
printf("***********************************\n");
printf("*** 1.add 2.sub ***\n");
printf("*** 3.mul 4.div ***\n");
printf("*** 0.exist ***\n");
printf("***********************************\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 a, b, n, ret;
int(*calc[5])(int, int) = { NULL,add,sub,mul,div };
do
{
menu();
printf("请输入:>");
scanf("%d", &n);
if (n == 0)
{
printf("推出计算器\n");
break;
}
else if (n >= 1 && n <= 4)
{
printf("请输入两个操作数:>");
scanf("%d%d", &a, &b);
ret = calc[n](a, b);
printf("%d\n", ret);
}
else
printf("输入错误\n");
} while (n);
return 0;
}
//这两个程序这样看,没有明显的差距,但当功能的数量不断增加时,switch语句的
//劣势就凸显出来了,代码十分冗长,而第二个只需修改部分地方。所以,我们的函数
//指针数组可不是摆设噢。
//在第二个程序中,函数指针数组起到了一个跳板的作用,我通过数组下标找到对应的
//函数指针,再通过函数指针去调用函数。所以,我们也把函数指针数组称为转移表

接下来,我们讲讲回调函数

回调函数

回调函数就是通过一个函数指针调用的函数。当我们把一个函数的指针作为参数传给一个函数,而这个函数在使用的时候,通过函数指针调用的函数,我们就称之为会回调函数。

什么意思呢?让我们走进代码中去看看:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
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.exist ****\n");
printf("**************************************\n");
}
void calc(int (*pf)(int, int))
{
int a, b, ret;
printf("请输入两个操作的数字:>");
scanf("%d%d", &a, &b);
ret = pf(a, b);
printf("%d\n", ret);
}
int main()
{
int input;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出计算器\n");
break;
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
default:
printf("输入错误\n");
}
} while (input);
return 0;
}
//以上程序把函数的地址传给了函数calc,函数calc通过该函数指针调用了相应的函数,
//相应的函数就称之为回调函数。比如函数calc通过add这个地址调用了函数add,那么
//函数add就称之为回调函数

好了,本次分享就到此结束了,不知道我有没有说明白,给予你一点点收获。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。