进阶指针
导言
大家好,很高兴又和大家见面啦!!!
经过前面的学习,我相信大家对指针、数组这一块的内容已经理解的更加透彻了。在上一篇内容中我们还介绍了函数与指针的结合体——函数指针。虽然只是简单的介绍,但是大家对这一块需要掌握的内容我相信各位应该是没什么问题的。今天咱们要介绍的内容与函数指针密切相关,下面我们一起来看一下吧!
十七、函数指针数组
在学习了指针和数组后,我们会发现,数组真的是个好东西,只要是相同类型的元素,我们都可以将它们放进数组中。比如我们熟知的常见的数据类型的元素——字符型、整型、浮点型……
- 字符型的元素我们可以放入字符数组中;
- 整型的元素我们可以放进整型数组中;
- 浮点型的元素我们可以放进浮点型数组中;
在前面我们还介绍了两种新的类型——指针型、数组型;
- 当我们将指针型的元素放入数组时,数组被称为指针数组;
- 当我们将数组型的元素放入数组时,数组被称为多维数组,如二维数组;
对于上一篇介绍的函数指针来说,它属于指针类型的元素,只不过它的具体类型时函数类型的指针,就像字符指针、整型指针一样;
- 当我们将字符指针类型的元素放入数组时,数组被称为字符指针数组;
- 当我们将整型指针类型的元素放入数组时,数组被称为整型指针数组;
- 同理,当我们将函数指针类型的元素放入数组时,数组就被称为函数指针数组;
既然函数指针数组的数组元素为函数指针类型,那我们应该如何创建这么一个数组呢?
17.1 函数指针数组的创建
我们在介绍函数指针数组的创建之前我们先来复习一下函数指针的创建:
//函数指针的创建格式
return_type(*point_name)((parameter_type, ……);
//return_type——函数的返回类型
//point_name——指针名
//*——指针标志
//parameter_type——函数参数类型
在创建函数指针时我们之所以要将指针标志与指针名用括号括起来,这是因为我们需要确保指针是与指针名结合起来,当指针名与返回类型结合起来时表示的是函数的返回类型为指针类型:
//指针函数的创建格式
return_type* point_name((parameter_type, ……);
//return_type*——函数的返回类型为指针型
//point_name——指针名
//*——指针标志
//parameter_type——函数参数类型
指针标志与数据类型结合时,就是指针型的函数,指针标志与变量名结合时,就是函数型的指针,我这样应该描述清楚了吧。下面我们再来看一下函数指针数组的创建格式:
//函数指针数组的创建格式
return_type (*point_arr_name[size])(parameter_type,……)
//return_type——函数返回类型
//*——指针标志
//point_arr_name——指针数组名
//size——数组大小
//parameter_type——参数类型
这个创建格式我们可以理解为这是一个函数指针型的数组,既然是函数指针,那指针标志肯定是要与变量名结合,所以需要用()
将*
和变量名
结合起来;
下面我们在来回顾一下指针数组的创建格式:
//指针数组的创建格式
type* arr_name[size]
//type*——数据类型
//type*[size]——数组类型
//arr_name——数组名
//size——数组大小
可以看到我们如果要创建一个指针型的数组的话,变量名需要先于[]结合才行,所以理论上我们应该写成:
(*(point_arr_name[size]))
的形式,但是因为[]
的有限级要高于*
,所以里面的括号可以省略不写,于是我们就得到了(*point_arr_name[size])
;
我们现在知道了函数指针数组的创建格式,那它又应该如何初始化呢?
17.2 函数指针数组的初始化
经过前面的分析,我们确定了函数指针数组它是一个函数型的指针数组,所以实际上我们是在给指针数组进行初始化。既然是指针数组,那当我们没有明确的指向对象时,我们需要使用NULL对指针数组进行初始化:
当有明确的指向对象时,我们可以直接进行初始化:
现在已经知道了函数指针数组的创建和初始化了,那函数指针数组我们又应该如何使用呢?
17.3 函数指针数组的使用
函数指针数组的使用是函数和数组的一个结合体,我们可以通过数组下标找到对应的数组元素,因为数组的元素是函数指针类型,所以我们在找到元素后可以通过函数调用操作符来调用函数,如下所示:
看到这里,可能会有朋友觉得这个函数指针数组很鸡肋,这个东西可以用来干什么呢?这样我们就不得不提到函数指针数组的实际运用——转移表;
十八、转移表
什么是转移表?
这里我们可以简单的理解为函数的中转中,我们在调用函数前需要通过一个中转站来进行函数的调用。
这个中转站其实就是函数指针数组。所以转移表的实质就是通过函数指针数组来将复杂的函数调用简单化。如下所示:
当然,我们在实际使用时调用函数的数量是根据实际情况而定的,只要被调用的函数满足以下三个条件即可通过函数指针数组来进行调用:
- 函数具有相同的返回类型
- 函数具有相同的参数数量
- 函数具有相同的参数类型
下面我们通过模拟实现计算器的例子来说明转移表的使用;
18.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)
{
retur服务器托管网n x / y;
}
//按位与
int Bit_And(int x, int y)
{
return x & y;
}
//按位或
int Bit_Or(int x, int y)
{
return x | y;
}
//按位异或
int Bit_Xor(int x, int y)
{
return x ^ y;
}
- 用户界面
做好了计算器的核心内容,下面我们就要进行面向用户的程序编写了,首先肯定是用户界面,用户需要知道他能在这个程序里做什么内容,如下所示:
//用户界面
void menu()
{
printf(" 欢迎使用简易版计算器 n");
printf("##########################n");
printf("#### 0.退出计算器 ####n");
printf("#### 1.加 法 运 算 ####n");
printf("#### 2.减 法 运 算 ####n");
printf("#### 3.乘 法 运 算 ####n");
printf("#### 4.除 法 运 算 ####n");
printf("#### 5.按位与运算 ####n");
printf("#### 6.按位或运算 ####n");
printf("#### 7.按位异或运算 ####n");
printf("##########################n");
}
- 用户功能编写
对于用户来说,一个计算器只需要能够输入数据和输出数据以及能够重复进行运算就可以了,因此我们可以通过循环语句和输入输出来完成:
//用户功能编写
int main()
{
int input = 0;
int x = 0;
int y = 0服务器托管网;
int ret = 0;
int (*p[])(int, int) = { NULL,Add,Sub,Mul,Div,Bit_And,Bit_Or,Bit_Xor };
do
{
menu();
printf("请输入您想进行的运算序号>:");
scanf("%d", &input);
if (0 == input)
{
printf("正在退出,请稍后n");
Sleep(1000);//停留1秒
system("cls");//清空屏幕
}
else if (1 :");
scanf("%d%d", &x, &y);
ret = p[input](x, y);
printf("%dn", ret);
}
else
{
printf("功能暂未开发,请重新输入n");
}
} while (input);
return 0;
}
在定义函数指针数组时,我们通过给首元素赋值空指针,达到一个占位的效果,这样我们在调用函数时就能够根据输入的数值来选择对应的函数进行调用了。
- 效果演示
- 运算功能演示:
可以看到,我们很好的对各个函数进行了调用;
- 报错功能演示:
当我们输入0-7以外的数字时,系统会进行报错并让用户重新进行选择;
- 退出功能演示
在输入0后,系统会先进行提示;
在等待1s后,系统会先清空窗口的内容,然后再退出程序;
现在咱们简易的计算器程序的编写就完成了,可以看到,与以往的对函数调用相比,我们通过函数指针数组进行中转调用函数时只需要一句代码通过输入值来确定需要调用的函数,这样就提高了代码的编写效率;
结语
到这里咱们今天的内容也就全部介绍完了,接下来我会跟新结构体的相关知识点,大家记得关注哦!
最后感谢各位的翻阅,咱们下一篇再见!!!
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net