1.获取函数的地址
获取函数的地址获取函数的地址很简单:只要使用函数名(后面不跟参数)即可。也就是说,如果think()是一个函数,则think 就是该函数的地址。要将函数作为参数进行传递,必须传递函数名。一定要区分传递的是函数的地址还是函数的返回值:
process(think); //passes address of think()to process()
thought(think()); //passes return value of think()to thought()
process()调用使得 process()函数能够在其内部调用 think()函数。thought()调用首先调用 think()函数,然后将think()的返回值传递给 thought( )函数。
2.声明函数指针
声明指向某种数据类型的指针时,必须指定指针指向的类型。同样,声明指向函数的指针时,也必须指定指针指向的函数类型。这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。也就是说,声明应像函数原型那样指出有关函数的信息。例如,假设Pam leCoder 编写了一个估算时间的函数,其原型如下:
double pam(int);//prototype
则正确的指针类型声明如下:
double(*pf)(int); // pf points to a function that takes
//one int argument and that
//returns type double
这与 pam()声明类似,这是将 pam 替换为了(*pf)。由于 pam 是函数,因此(*pf)也是函数。而如果
(*pf)是函数,则pf就是函数指针。
提示:通常,要声明指向特定类型的函数的指针,可以首先编写这种函数的原型,然后用(*pf)替换
函数名。这样pf就是这类函数的指针。
为提供正确的运算符优先级,必须在声明中使用括号将*pf括起。括号的优先级比*运算符高,因此*pf(int)意味着 pf()是一个返回指针的函数,而(*pf)(int)意味着pf是一个指向函数的指针。
正确地声明pf后,便可以将相应函数的地址赋给它,
double pam(int);
double(*pf)(int);
pf = pam; //pf now points to the pam()function
注意,pam()的特征标和返回类型必须与pf相同。如果不相同,编译器将拒绝这种赋值:
3.使用指针来调用函数
(*pf)扮演的角色与函数名相同,因此使用(*pf)时,只需将它看作函数名即可。
4.深入探讨函数指针
下面是一些函数的原型,它们的特征标和返回类型相同:
const double *fl(const double arl[], 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*。因此,上述所有函数特征标的含义都相同。另外,函数定义必须提供标识符,因此需要使用const double ar[]或 const double* ar.
现在要声明一个指针,指向这三个函数之一。假定该指针名为pa,则只需将目标函数原型中的函数名替换为(*pa):
const double*(*pl)(const double*, int);
可在声明的同时进行初始化:
const double*(*p1)(const double*, int) = f1;
使用C++11的自动类型推断功能时,代码要简单得多:
auto p2 = f2; //C++11 automatic type deduction
现在来看下面的语句:
cout << (*p1)(av, 3) << ":" << *(*p1)(av, 3) << endl;
cout << p2(av, 3) << ":" << *p2(av, 3) << endl;
根据前面介绍的知识可知,(*p1)(av,3)和 p2(av,3)都调用指向的函数(这里为f1()和 f2()),并将 av 和3 作为参数。因此,显示的是这两个函数的返回值。返回值的类型为const double*(即 double 值的地址),因此在每条cout语句中,前半部分显示的都是一个 double值的地址。为查看存储在这些地址处的实际值,需要将运算符*应用于这些地址,如表达式*(*p1)(av,3)和*p2(av,3)所示。
鉴于需要使用三个函数,如果有一个函数指针数组将很方便。这样,将可使用for 循环通过指针依次调用每个函数。如何声明这样的数组呢?显然,这种声明应类似于单个函数指针的声明,但必须在某个地方加上[3],以指出这是一个包含三个函数指针的数组。问题是在什么地方加上[3],答案如下(包含初始化):
const double*(*pa[3])(const double*, int) = { fl,f2,f3 };
为何将[3]放在这个地方呢?pa是一个包含三个元素的数组,而要声明这样的数组,首先需要使用 pa[3]。该声明的其他部分指出了数组包含的元素是什么样的。运算符口的优先级高于*,因此*pa[3]表明 pa是一个包含三个指针的数组。上述声明的其他部分指出了每个指针指向的是什么:特征标为const double*,int,且返回类型为 const double *的函数。因此,pa是一个包含三个指针的数组,其中每个指针都指向这样的函数,即将 const double*和int作为参数,并返回一个 const double*。
这里能否使用 auto呢?不能。自动类型推断只能用于单值初始化,而不能用于初始化列表。但声明数组 pa后,声明同样类型的数组就很简单了:
auto pb = pa;
调用函数
pa[i]和 pb[i]都表示数组中的指针,因此可将任何一种函数调用表示法用于它们:
const double *px = pa[0](av, 3);
const double *py = (*pb[1])(av, 3);
要获得指向的 double 值,可使用运算符 * :
double x = *pa[0](av, 3);
double y = *(*pb[1])(av, 3);
创建指向整个数组的指针。
方式一:使用auto
由于数组名pa是指向函数指针的指针,因此指向数组的指针将是这样的指针,即它指向指针的指针。这听起来令人恐怖,但由于可使用单个值对其进行初始化,因此可使用 auto:
auto pc = &pa; //C++11 automatic type deduction
方式二:自己声明
pd 是一个指针,它指向一个包含三个元素的数组。如下:
const double *(*(*pd)[3])(const double *, int) = &pa;
要调用函数,需认识到这样一点:既然 pd指向数组,那么*pd 就是数组,而(*pd)[i]是数组中的元素,即函数指针。因此,较简单的函数调用是(*pd)[i](av,3),而*(*pd)[i](av,3)是返回的指针指向的值。也可以使用第二种使用指针调用函数的语法:使用(*(*pd)[i])(av,3)来调用函数,而*(*(*pd)[i])(av,3)是指向的double 值。
请注意 pa(它是数组名,表示地址)和&pa之间的差别。在大多数情况下pa都是数组第一个元素的地址,即&pa[0]。因此,它是单个指针的地址。但&pa是整个数组(即三个指针块)的地址。从数字上说,pa和&pa的值相同,但它们的类型不同。一种差别是,pa+1为数组中下一个元素的地址,而&pa+1为数组pa后面一个12字节内存块的地址(这里假定地址为4字节)。另一个差别是,要得到第一个元素的值,只需对pa解除一次引用,但需要对&pa解除两次引用:
**&pa ==*pa == pa [0]