一、现在有一个函数func(),要实现将一个函数作为参数传进这个函数中。为此,必须要完成以下操作:
1、获取函数的地址,使用函数名即可
2、声明一个函数指针,声明应该像函数原型那样指出有关函数的信息。
double pam(int);//prototype
double (*pf)(int);
pam是函数,因此(*pf)也是函数,所以pf是函数指针。
3、使用函数指针来调用函数
(*pf)扮演的角色与函数名相同,因此使用(*pf)时,只需将它看做函数名即可。
double pam(int);
double (*pf)(int);
pf = pam;
double x = pam(4);
double y = (*pf)(5);
实际上,C++也允许像使用函数名那样使用pf:
double y = pf(5);
二、深入探讨函数指针
下面是一些函数原型,它们的特征标和返回类型相同:
const double* f1(const double ar[], int n);
const double* f2(const double [], int);
const double* f3(const double *,int);
这些函数的特征标看似不同,但实际相同。首先,在函数原型中,参数列表const double ar[]与const double * ar的含义完全。其次,在函数原型中,可以省略标识符。因此,const double ar[]可简化为const double []。因此,上述所有函数特征标的含义完全都相同。另一方面,函数定义必须提供标识符,因此需要使用const double ar[]或const double *ar。
声明一个指针:
const double* (*p1)(const double *, int) = f1;;
这里声明的时候使用自动类型推断:
auto p2 = f2;
看下面的语句:
cout<<(*p1)(av, 3)<<" :"<<*(*p1)(av, 3)<<endl;
cout<<p2(av, 3)<<" :"<<*p2(av, 3)<<endl;
为查看存处在这些地址的实际值,需要用运算符*应用于这些地址。
三、函数指针数组
还是上面的代码,因为需要使用三个函数,有一个函数指针数组就非常方便:
const double * (*pa[3])(const double *, int) = {f1, f2, f3};
运算符[ ]的优先级高于*,因此*pa[3]表明pa是一个包含三个指针的数组。特征标为const double *, int, 返回值为const double *的函数。
这里能否使用auto呢?不能。自动类型转换只能用于单值初始化,而不能用于初始化列表。但声明数组pa后,声明同样的类型的数组就简单了:
auto pb = pa;
pa[i]和pb[i]都表示数组中的指针,如果要调用它们,一下两种方法都可以:
double x = pa[0](av, 3);
double y = (*pb[1])(av, 3);
还可以创建指向整个数组的指针。由于数组名pa是指向函数指针的指针,因此指向数组的指针将是这样的指针,即它指向指针的指针,但由于可以使用单个值对其进行初始化,因此可以使用auto:
auto pc = &pa;
如果要自己声明,该怎么办呢?这种声明类型与pa的声明,但由于增加了一层间接,因此需要在一个地方添加一个*。具体的说,如果这个指针名为pd,则需要指出它是一个指针,而不是数组。这意味着核心部分应该是(*pd)[3],其中括号让标识符pd先与*结合:
const double *(*(*pd)[3])(const double *, int) = &pa;
需要调用函数,须认识到这一点:既然pd指向数组,那么*pd就是数组,而(*pd)[i]是数组中的元素,即函数指针。因此,较简单的调用方法是:
(*pd)[i](av, 3)
复杂的是:
(*(*pd)[i])(av, 3);
注意:从数字上说,pa和&pa的值相同,但它们的类型不同。一种差别是:pa + 1为数组中下一个元素的地址,而&pa + 1为数组pa后面一个12字节内存块的地址。另一个差别是:要得到第一个元素的值,只需要对pa解除引用一次,但需要对&pa解除引用两次:
**&pa == *pa == pa[0];