- C++程序设计基础(上)
- 周霭如 林伟健编著
- 1780字
- 2020-08-28 09:48:05
3.4 函数地址和函数指针
函数、应用程序是编译器处理的对象。每一个函数的语句序列经过编译之后,生成的都是二进制代码。这些代码要调入内存才能够运行。每一条指令、每一个函数模块都有一个首地址。函数块的首地址称为函数的入口地址,或函数指针。
一个已经定义的函数,它的名字就是函数的入口地址。我们已经熟悉了用名字调用函数的方式,本节将更深入地讨论有关调用函数的问题。
3.4.1 函数的地址
为了弄清楚函数地址的表示,下面考察一个简单的程序。
【例3-23】用不同方式调用函数。
#include<iostream> using namespace std; void simple() { cout<<"It is a simple program.\n"; } int main() { cout<<"Call function …\n"; simple(); //名方式调用 (&simple)(); //地址方式调用 (*&simple)(); //间址调用 cout << "Address of function :\n"; cout<<simple<<endl; //函数名是地址 cout<<&simple<<endl; //取函数地址 cout<<*&simple<<endl; //函数地址所指对象 }
程序运行结果:
Call function … It is a simple program. It is a simple program. It is a simple program. Address of function : 0043B248 0043B248 0043B248
下面分析上述例程。
① 函数名、函数地址都是函数的入口地址。对于
simple &simple *&simple
它们在形式上看起来很不相同,但实际上,在C++中,它们都是函数simple的代码模块在内存的入口地址值,称为函数地址。
但是,对于一个数据对象:
double x = 1.414;
如果有输出:cout << &x << x;
&x是地址表达式,将显示变量x在内存的地址;名访问x表示对象的值1.414。
程序设计语言对数据对象的地址和名加以区别。采用名访问数据对象时,编译器能够根据数据的类型解释存储单元的值。“函数”这种代码对象和普通数据对象性质不同。对函数的访问(调用)一旦找到入口地址,将按照指令的规则执行。编译器调用函数不需要区分地址和名。对于一个已经定义的函数,函数名、函数地址(指针)、函数指针所指对象都是同一样东西,表示函数的入口地址。
② 函数调用指令要做的事情是:找到函数入口地址,传递参数。因此,调用函数的语句(或表达式)由两部分组成:
函数地址 (实际参数表)
其中,“函数地址”实质上是一个地址表达式,可以是函数名,或者能够表示函数入口地址的式子。以下调用都可以达到相同的效果:
simple() (& simple)() (* & simple)()
注意,在后两种调用方式中,第一个括号不能省略,因为地址表达式计算要优先于参数结合。
3.4.2 函数指针
函数定义后,函数名表示函数代码在内存的直接地址。可以用一个指针变量获取函数的地址,通过指针变量的间址方式调用函数。指向函数的指针变量简称为函数指针。
1.函数的类型
要定义函数指针,首先要明确什么是函数的类型。
函数的类型(注意,不是函数的返回值类型)是指函数的接口,包括函数的参数定义和返回类型。例如,以下是类型相同的函数:
double max(double, double); double min(double, double); double average(double, double);
这些函数都有两个double类型参数,返回值为double类型,它们的类型(接口)为:
double (double, double)
一般地,表示函数类型的形式为:
类型(形式参数表)
C++中,可以用关键字typedef定义函数类型名。函数类型名定义的一般形式为:
typedef 类型 函数类型名(形式参数表);
其中,“函数类型名”是用户定义标识符。例如:
typedef double functionType (double, double);
函数类型名 functionType 定义了一类接口相同的函数的抽象,即抽象了两个 double 参数,返回double类型的相同类型函数。此时:
functionType max, min, average;
等价于前面的3个函数原型声明。
2.函数指针
要定义指向某一类函数的指针变量,可以用以下两种说明语句:
类型(* 指针变量名)(形式参数表);
或 函数类型 * 指针变量名;
在第一种说明语句中,因为“()”的优先级比“*”高,所以“(*指针变量名)”的圆括号不能省略;否则,就会变成一个返回指针值的函数原型。例如:
double * f (double, double);
是函数原型声明,f是函数名,具有两个double参数,返回double类型指针。
对于上述已经定义的functionType,如果要定义指向这一类函数的指针变量,可以用说明语句:
double (*fp)(double, double);
或 functionType * fp;
有了函数类型定义,便可以同时定义多个类型相同的函数指针:
functionType *fp1,*fp2;
还可以用关键字typedef定义指针类型。函数指针类型定义的一般形式为:
typedef 类型(* 指针类型)(形式参数表);
或 typedef 函数类型 * 指针类型;
其中,“指针类型”是用户定义的类型名。例如:
typedef double (*pType)(double,double);
或 typedef functionType * pType;
定义了一个函数指针类型pType。
又如, pType pf1, pf2;
定义了两个pType类型的指针变量pf1和pf2,分别指向不同的函数:
pf1=max; //pf1指向函数max pf2=min; //pf2指向函数min
3.用函数指针调用函数
一个已经定义的函数指针,赋给函数地址后就可以调用函数。使用函数指针调用函数的一般形式为:
(* 指针变量名)(实际参数表)
或 指针变量名 (实际参数表)
例如,向fp赋予不同函数的地址,通过fp调用不同函数:
fp=max; //获取max函数地址 x=fp(0.5,3.92); //相当于 x=max(0.5,3.92); fp=min; //获取min函数地址 x=fp(0.5,3.92); //相当于 x=min(0.5,3.92); fp=average; //获取average函数地址 x=fp(0.5,3.92); //相当于 x=average(0.5,3.92);
从函数调用性质可知:(*fp) (0.5, 3.92)与fp(0.5, 3.92)是等价的调用方式。
但是(&fp) (0.5, 3.92)是错误的。fp是指针变量,它的地址不是函数的地址,它的值才是函数的地址。
【例3-24】用函数指针调用不同函数。
程序定义了 4 个接口相同的函数,分别是:计算圆周长的 circlePerimeter、计算圆面积的circleArea、计算球面积的bollArea和计算球体积的bollVolume函数,它们都有一个double参数,返回类型为double。随着代码执行,函数指针pf指向不同函数,实现不同的计算。
#include<iostream> #include<cmath> using namespace std; const double PI = 3.14159; double circlePerimeter(double radius) { return 2*PI*radius; } double circleArea(double radius) { return PI*radius*radius; } double bollArea(double radius) { return 4*PI*radius*radius; } double bollVolume(double radius) { return 4.0/3*PI*pow(radius,3); } int main() { double(*pf)(double); //定义函数指针 double r, cP, cA, bA, bV; cout << "enter the radius of a circle : "; cin >> r; pf=circlePerimeter; //获取函数地址 cP=pf(r); //等价于circlePerimeter(r)
pf=circleArea; //获取函数地址 cA=pf(r); //等价于circleArea(r) cout << "the perimeter of circle is : "<< cP << endl; cout << "the area of circle is : "<< cA << endl; cout << "enter the radius of a boll : "; cin >> r; pf=bollArea; //获取函数地址 bA=pf(r); //等价于bollArea(r) pf=bollVolume; //获取函数地址 bV=pf(r); //等价于bollVolume(r) cout << "the area of boll is : "<< bA << endl; cout << "the volume of boll is : "<< bV << endl; }
程序运行结果:
enter the radius of a circle:3.5 the perimeter of circle is:21.9911 the area of circle is:38.4845 enter the radius of a boll:4.2 the area of boll is:221.671 the volume of boll is:310.339
当函数指针作为函数参数时,可以传递函数的地址,通过参数调用不同函数。
【例3-25】使用函数指针参数调用函数。本程序与例3-24的功能相同。
#include<iostream> #include<cmath> using namespace std; typedef double funType(double); //定义函数类型 funType circlePerimeter; //用函数类型名定义函数原型 funType circleArea; funType bollArea; funType bollVolume; double callFun(funType*,double); //第一个参数是函数指针参数 int main() { double r; cout << "enter the radius : "; cin >> r; //用函数地址作为实际参数调用函数callFun cout << "the perimeter of circle is : " << callFun(circlePerimeter, r)<< endl; cout << "the area of circle is : "<< callFun(circleArea, r) << endl; cout << "enter the radius of a boll : "; cin >> r; cout << "the area of boll is : "<< callFun(bollArea, r) << endl; cout << "the volume of boll is : "<< callFun(bollVolume, r) << endl; } const double PI = 3.14159; double circlePerimeter(double radius) { return PI*radius*radius; } double circleArea(double radius) { return 2*PI*radius; } double bollArea(double radius) { return 4*PI*radius*radius; } double bollVolume(double radius) { return 4.0/3*PI*pow(radius,3); } double callFun(funType * qf, double r) { return qf(r); }
程序中定义了函数类型funType,抽象接口为:
double (double)
的一类函数。类型名funType可以用于说明函数原型:
funType circlePerimeter;
等价于 double circlePerimeter(double);
还可用于callFun函数的参数说明:
double callFun(funType * qf, double r) { return qf(r); }
qf是funType类型的指针。main函数调用callFun时,用函数名(即函数地址)作为实际参数,传递给形参指针qf,调用所指函数:
callFun(circlePerimeter, r) callFun(circleArea, r) callFun(bollArea, r) callFun(bollVolume, r)
上面4次调用,实际参数为不同函数的地址,使得执行callFun时调用不同的函数。
函数指针不仅可以调用自定义函数,还可以调用库函数。例如,正弦函数和余弦函数原型为:
double sin(double); double cos(double);
它们是接口一样的同类型函数。以下例子用函数参数调用这两个库函数。
【例3-26】使用函数名作为函数参数,调用库函数sin(x)和cos(x),计算指定范围内间隔为0.1的函数值之和。
#include<iostream> #include<cmath> using namespace std; typedef double fType(double); //定义函数类型 double func(fType*f,double l,double u) //f是函数指针参数 { double d,s=0.0; for(d = l; d <= u; d += 0.1) s += f(d); return s; } int main() { double sum; sum=func(sin,0.0,1.0); //库函数sin作为实参 cout << "the sum of sin from 0.0 to 1.0 is: " << sum << endl; sum=func(cos,0.0,1.0); //库函数cos作为实参 cout << "the sum of cos from 0.0 to 1.0 is: " << sum << endl; }
运行程序,显示结果为:
the sum of sin from 0.0 to 1.0 is: 5.01388 the sum of cos from 0.0 to 1.0 is: 9.17785