王玉山
(广东外语外贸大学,广州 510440)
一个难于理解的C++函数指针问题
王玉山
(广东外语外贸大学,广州 510440)
摘要:指针是C、C++程序设计语言中一个比较复杂、难于掌握的概念。但又是任何一个C、C++程序设计者必须过的一道门槛。本文只就其中比较难难于理解的函数指针的一个例子加以阐述。希望对于C、C++程序设初学者有所帮助。
关键词:C++程序设计;C++函数指针;
指针常量与指针变量统称为指针。有关变量(类对象)、数组、函数、字符串常量的地址值都是地址常量,它们不能被赋值,只能做右值;可以被赋值的地址变量是指针变量,它们可以做左值也可以作右值。
函数名是函数代码段的入口地址,是地址常量,只能做右值;指向函数的指针变量是用来存放函数的入口地址的,可以做左值也可以做右值。
象普通变量赋值一样,函数指针变量的赋值要求右值和左值必须类型一致。什么是函数指针的类型呢?就是在定义函数指针时,我们把指针名去掉,剩下的部分就是函数指针的类型。
int (*return_fun(char op))(int, int){……}。去掉return_fun(char op)后留下部分为:int (*)(int, int),即为函数return_fun(char op)返回的类型,是一个返回值为整形,有2个整形参数的函数。它的形参是字符变量char op。
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;}
int sub(int a,int b)
{
return a-b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return b?a/b:-1;
}
下面定义的return_fun(char op),即为返回指向函数的指针函数,该函数参数是char op。
int (*return_fun(char op))(int, int)
{
switch (op)
{
case '+':return add;//返回函数的地址,即函数名。
case '-':return sub;
case '*':return mul;
case '/':return div;
default:
return NULL;
}
}
//s_fp为指向函数的指针变量,指向有2个整形参数,返回值为整形的函数。 这里指向函数的指针变量 s_fp的右值是:return_ fun(op),return_fun(op)是一个函数调用、返回一个函数指针常量,即四个函数名add、sub、mul、div之一。
//下面的语句根据上一步得到的函数的地址调用相应函数,并返回运算结果。
if (s_fp)return s_fp(a, b);
else return -1;
}
int main(int argc, char** argv){
int a = 800, b = 200;
cout<<a<<"+"<<b<<"="<<calc(a,'+', b )<<endl;
cout<<a<<"-"<<b<<"="<<calc(a, '-', b)<<endl;
cout<<a<<"*"<<b<<"="<<calc(a, '*',b)<<endl;
cout<<a<<"/"<<b<<"="<<calc(a,'/', b )<<endl;
return 0;
}
以上程序在DevC++5.4.2版本下通过试验。
指向函数的指针,定义的时候为了保证类型一致,少犯错误。可以把函数定义的头部搬到定义的地方,在后面加上分号,把原来的函数名换为合法的标示符。如有函数:int add(int a, int b){return a + b;}。我们就把这个函数头部的函数名用Psum代替、省掉参数名、将标示符左侧加*号,必须将*号与标示符加括号进行绑定得到:int (* Psum )(int , int);此时Psum就是可以指向int add(int a, int b){return a + b;}函数的指针变量了。注意这个用于绑定的括号绝对不能省掉,如果省掉Psum就不是变量了!成了这样:int * Psum (int , int);这就成了返回整形指针的函数原形声明了。Psum也成了常量。
象普通变量一样,指向函数的指针变量也必须先定义后使用。
前面函数头部为:int (*return_fun(char op))(int, int)的例子中,是一个比较复杂的应用,该代码段定义的函数为return_fun(char op),而函数return_fun(char op))的返回类型是int (*return_fun(char op))(int,int)中把return_fun(char op)去掉后留下的部分:int (*)(int, int),这是一个指向函数的指针类型。在本例中最右侧的括号(int, int)是绝对不能省掉的,因为它是确定return_fun(char op)返回类型的关键。
再看以下代码段:void (*setF1(void (*f)()))(){……}。我们该如何理解?当然要从setF1为起点,右边的括号有几个,这看起来让人有点头晕。这里要特别注意圆括号的运算级别和结合性,我们知道圆括号的优先级是1级,左结合性。而这里的*号是间接访问运算符,优先级是1级。所以应该先有setF1(void (*f)()),它是一个整体,setF1()的参数是void (*f)(),是一个指向函数的指针参数,在void (*setF1(void (*f)()))()中,把setF1(void (*f)())去掉后留下的部分是void (*)()就是函数setF1的返回类型。
参考文献:
[1]王珊珊等著.C++程序设计教程(第2版),北京:机械工业出版社,2011年1月第 2 版,ISBN978-7-111-33022-6.
[2]谭浩强著.C程序设计,第三版,北京, 清华大学出版社,2005年 7月第3 版,ISBN 7-302-10853- 6/TP·7217.
[3]史上最牛逼的 C++指针 http://wenku.baidu.com/link?url=Mw5lETdR_tZo2-6zbkNmj7pZPb7OiiYPzwrvO0epmv6SAX14UFJ Yq9u3xt5gPwOlM8u1ZKlscq2U-SfUk6GshQooN59vaVJxJAo8YxLDWuy.