神奇的函数指针

一、什么是函数指针

​ 我们知道函数名称反映了一个函数的地址,而指针恰恰也可以用地址来赋值,那么我们可不可以把二者结合起来呢?答案是可以的,我们可以通过指针来反应函数的地址,甚至可以声明一个有着函数指针语法的函数。

举一个简单的例子

double (*pf)(int);

​ 这就是一个函数指针的声明,当然如普通指针一样,他需要一个初始化的地址,如果不初始化,就会出一些问题,所以我们可以在声明的同时或者声明以后为他赋值。

double pam(int);
double (*pf)(int)=pam;
double (*pf1)(int);
pf1=pam;

​ 是不是和指针十分相似,没错,函数指针其实就是一种指向函数体的开始地址的指针(通俗来说就是指向函数的指针)。

二、为什么会出现函数指针

​ 一些人可能在刚刚了解到函数指针的时候,会觉得好多鱼,奥不是,好多余啊,函数指针有什么用啊?

​ 事实上,函数指针是一种十分优秀的设计。

​ 下面举例说明

double pam(int lns){
   
	return 0.03*lns+0.0004*lns*lns;
}
double besty(int lns){
   
    return 0.05*lns;
}
void estimate(int lines,double (*pf)(int)){
   
    cout<<(*pf)(lines);
}

​ 这是什么意思呢,假如我们把pam作为参数传入estimate函数,那么estimate输出的值,就会使pam函数的返回值,假如我们把besty作为参数传入,输出的值又会是besty的返回值。

​ 看,我们通过一个简易的示例说明了,我们可以在不修改主要函数的情况下,通过传入自己编写的函数,可以起到修改主要函数行为的作用,这种设计,很有助于大型的程序开发,可以使一个函数成为其他函数的**“载体”**,提高了代码的复用。

三、深入函数指针

​ 我们也可以声明一个包含多个函数指针的数组。

const double *(*pa[3])(const double*,int)={
   f1,f2,f3};

1.补充易错

区分数组指针和指针数组

(1)首先看数组指针

int (*p)[3];

这是一个指向包含三个元素的数组的指针p

(1)接着看指针数组

int *p[3];

这是一个包含三个指针的数组

为什么是这样呢?

因为运算符[]的优先级大于*所以不加()的话, []先和p结合,构成p[3],表示这是一个有三个元素的数组,注意了,这里仅仅说包含三个元素,而没说元素的类型,因为元素的类型声明在前面**int ***,即int指针类型。

2.auto

auto是c++11新增的自动类型推断,它也可以用于简化函数指针的声明和初始化

const double *f1(int a,int b);
auto p1=f1;

3.如何创建指向整个函数指针数组的指针

(1)使用auto
const double *(*pa[3])(const double*,int)={f1,f2,f3};
auto p1=&pa

注意,这里加了一个**&**,代表获取了整个数组的地址。

对p1解引用,——*p1就获得了数组pa,对 *p1再一次解引用,——**p1就获得了数组pa的首元素
在这里插入图片描述

简单的证明了一下上述,函数指针也是同样的。

(2)不使用函数指针
const double *(*(*p1)[3])(const double *,int n)=&pa;

很繁琐吧,我们从内到外剥开,最内层(*p1),p1是指向数组的指针,因为这里加了括号,上文有解释。所以( *p1)就是这个数组, *( *p1)就是数组第一个元素的值,也就是第一个函数指针。

(*p1)[i]表示数组中下标为i的函数指针。

函数调用的两种方式

(1)

(*pd)[i](av,3);

(2)

(*(*pd)[i])(av,3);

我们可以这么理解吧,函数的构成简单来说是这样的

int a(int b);

返回值类型 函数名 特征标

为什么会有两种函数调用的方式呢?他们俩各有各的道理

先看第一个,这里就是表示一个指向函数的指针,也就是函数的地址,而函数名表示的就是地址,所以可以这么写

再看第二个,对函数指针解引用了,解出了函数名,似乎也能这么写