参考书籍:《c++ primer plus》。
这是一个非常容易搞晕的概念,特此记录。
基本使用
函数指针:指向函数的指针
与变量指针相同,要使用函数指针,至少需要获知以下信息:
1. 获取函数地址:函数名,不含括号
double pam(int); //声明,又称原型:prototype
2. 声明函数指针:返回类型 (*函数名)(传参)
double (*pf)(int); // 函数指针
注意:“ * ”和括号的位置
(*pf)(int);//pf是指向函数的指针
*pf(int);//pf是返回指针的函数
正确声明函数指针pf后,可以将相应函数的地址赋给它:
double pam(int); //函数
double (*pf)(int); // 函数指针
pf=pam;//pf指向函数pam(int)
注意:函数的特征标和返回类型必须与函数指针一致
将函数指针运用于其他函数中。例如
void estimate(int line,double (*pf)(int));//声明一个函数,传参为一个int和一个函数指针
estimate(50,pam);// 传址简单
3. 使用函数指针
一个十分方便的tip:函数指针pf等价于(*pf)。
这是由于(*pf)相当于函数名,而函数名本身又相当于函数的地址。
这是一个逻辑冲突的现实——也得益于c++的新特性。
double pam(int);
double (*pf)(int);
pf=pam;
double x=pam(3);
double y=(*pf)(3);//代码正在使用函数指针
double z=pf(3);
以上三种操作结果完全相同,只是内在逻辑不同。
深入探讨
复习一个知识:以下三种函数声明的特征标看似不同,实则完全相同
1、函数指针数组
const double *f1(const double arr[],int n);
const double *f2(const double [],int);
const double *f3(const double *,int);
- const double arr[]和const double *arr含义完全相同
- 函数原型中可以省略标识符:
const double arr[] -> const double []
const double *arr -> const double *
顺带一提,函数定义必须又完整的参数类型和参数名
声明一个指针pa,指向三个函数之一。直接将目标函数原型中的函数名替换为(*pa)即可
const double* (*pa)(const double arr[],int n);
//第一个"*":返回类型指向为const double的指针;
//第二个"*":声明函数指针
或者在声明时进行初始化:
const double* (*pa)(const double arr[],int n)= f1;
//类似变量指针:int * ptr = &a;
更方便的,使用auto(自动类型推断)
auto p2=f2;
现在,调用函数f1有三种方法:
f1(arr,2);// 直接的函数调用
(*p1)(arr,2); // p1指向的函数调用
p1(arr,2); // p1存储的地址=函数名的调用
以上返回值都与函数f1的返回值相同:一个指向const double类型变量的指针,即一个double值的地址。如果想查看这一指针指向的double变量的值,需要将运算符*应用于这些地址:
*f1(arr,2);// 直接的函数调用
*(*p1)(arr,2); // p1指向的函数调用
*p1(arr,2); // p1存储的地址=函数名的调用
继续考虑更复杂的情况:将多个函数指针变量存入一个数组时,这时的声明(初始化)应为:
const double* (*pa[3])(const double arr[],int n)= {f1,f2,f3};
//类似变量指针:int * ptr[3] = {&a,&b,&c};
运算符[]的优先级高于*,*pa[3]表明pa是一个包含三个指针的数组;
其他部分表明了这些指针是函数指针:指向一个返回值为const double*类型,特征标为const double *和int。
此处不能使用auto。自动类型判断只能用于单值初始化,而不能用于初始化列表。但声明函数pa后,声明同样类型的数组就很简单了:
auto pb=pa;//pb也是一个包含三个函数指针的数组
利用这个数组进行函数调用:
//pa[i]和pb[i]都表示数组中的函数指针,因此借用上文调用的方法:
const double * x = pa[0](av,3);// double地址
const double y = *(pa[0])(av,3);// double值
2、指向函数指针的指针(的指针)
借用变量数组的概念
int arr[3];//arr=&arr[0](为一个元素的地址),是指向这个数组/第一个元素的指针
const double* (*pa[3])(const double arr[],int n)= {f1,f2,f3};
auto pb=pa;//pb也是一个包含三个函数指针的数组
//类似变量指针:int * ptr[3] = {&a,&b,&c};
pa和pb即指向函数指针的指针。(开始绕了)
创建指向整个数组的指针:
auto pc=&pa;//自动类型判断
如果想挑战自己书写声明,可以这样思考
*arr[3];//包含三个指针的数组
(*arr)[3];//指向包含三个元素的数组的指针
因此我们可以得到这样的一个声明
const double* (*(*pd)[3])(const double *, int)=&pa;
考虑如何调用这个圈圈绕绕:
pd指向数组,那么*pd就是数组,(*pd[i])就是数组中的元素,为函数指针。
(*pd[0])(arr,3);//函数调用,返回double的地址
(*(*pd)[0])(arr,3);//函数调用,返回double的地址。
//*pd:指向数组的数组名,(*pd)[0]:指向数组的第一个元素(函数指针,pa)
//*(*pd)[0]:调用这个指针(*pa)
*(*pd[0])(arr,3);//函数调用,返回double的值
*(*(*pd)[0])(arr,3);//函数调用,返回double的地址