指向函数的指针
#include<iostream> using namespace std; int max(int x,int y) { return x>y?x:y; } int main() { int (*p)(int,int); p = max; cout<<(*p)(4,5)<<endl; cout<<max(4,5)<<endl; }
int (*p)( int,int ); 用来定义 p 是一个指向函数的指针变量,该函数有两个整形参数,函数值为整形。注意 *p 两侧的括号不可省略,表示 p 先与 * 结合,是指针变量,然后再与后面的 ( ) 结合,表示此指针变量指向函数,这个函数值 (即函数的返回值) 是整形的。如果写成 int *p ( int,int ) ,由于( )的优先级高于 *,它就成了声明一个函数P( 这个函数的返回值是指向整形变量的指针)。
赋值语句 p = max ; 作用是将函数 max 的入口地址赋给指针变量p。和数组名代表数组首元素地址类似,函数名代表该函数的入口地址。这时 p 就是指向函数 max 的指针变量,此时 p 和 max都指向函数开头,调用 *p 就是调用 max 函数。但是p作为指向函数的指针变量,它只能指向函数入口处而不可能指向函数中间的某一处指令处,因此不能用 *(p + 1)来表示指向下一条指令。
注意:
(1) 指向函数的指针变量的一般定义形式为:
数据类型 (*指针变量名)(函数参数列表)
这里数据类型就是函数返回值的类型
(2) int (* p) ( int,int ); 它只是定义一个指向函数的指针变量 p, 它不是固定指向哪一个函数的,而只是表示定义这样一个类型的变量,它是专门用来存放函数的入口地址的。在程序中把哪一函数(该函数的值应该是整形的,且有两个整形参数)的地址赋给它,他就指向哪一个函数。在一个函数中,一个函数指针变量可以先后指向同类型的不同函数。
(3) p = max; 在给函数指针变量赋值时,只需给出函数名而不必给出函数参数,因为是将函数的入口地址赋给 p ,而不涉及 实参和形参的结合问题,不能写成 p = max(a,b);
(4) c = (*p)(a,b) 在函数调用时,只需将( *p ) 代替函数名即可,后面实参依旧。
(5) 对于指向函数的指针变量,像 p++ ,p+n.....是无意义的。
(二) 用指向函数的指针作为函数参数
函数指针变量通常的用途之一就是把指针作为参数传递到其他函数。
函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量,也可以是指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
void sub ( int ( *x1) (int), int (*x2) (int,int) )
{
int a,b,i,j;
a = (*x1)(i); /* 调用 f1 函数 */
b = (*x2)(i)(j); /* 调用 f2 函数 */
}
如果实参为两个 函数名 f1 和 f2. 在函数首部定义x1、x2为函数指针变量,x1指向的函数有一个整形形参,x2指向的函数有两个形参。i 和 j 是函数f1 和 f2所要的参数。函数sub的形参 x1、x2(指针变量)在函数 sub 未被调用时并不占用内存单元,也不指向任何函数。在sub被调用时,把实参函数 f1 和 f2的入口地址传给形式指针变量 x1 和 x2.
既然在 sub 函数中要调用 f1 和 f2 函数,为什么不直接调用f1 和 f2而要用函数指针变量呢? 确实,如果只是用到f1 和 f2 函数,完全可以在sub函数中直接调用f1 和 f2,而不必设指针变量 x1 和 x2。 但是,如果在每次调用sub时,调用的函数不是固定的,下次是f3 和 f4,再是f5 和 f6...这时用指针变量就比较方便了。
http://blog.csdn.net/wuliming_sc/article/details/3855682
指向函数的指针
假定我们被要求提供一个如下形式的排序函数:
sort( start, end, compare );
start 和end 是指向字符串数组中元素的指针。函数sort()对于start 和end 之间的数组元素进行排序。compare 定义了比较数组中两个字符串的比较操作。
该怎样实现compare 呢?我们或许想按字典顺序排序数组内的字符串,或许想按长度排序它们,以便将最短的字符串放在前面,而长的放在后面。解决这种需求的一种策略是将第三个参数compare 设为函数指针,并由它指定要使用的比较函数。
为简化sort()的用法而又不限制它的灵活性,我们可能希望指定一个缺省的比较函数,以用于大多数的情况。让我们假设最常见的以字典序排列字符串的情况,缺省实参将指定一个比较操作,它用到了字符串的compare()函数。我们将考虑怎样用函数指针来实现我们的sort()函数。
指向函数的指针的类型
怎样声明指向函数的指针呢?用函数指针作为实参的参数会是什么样呢?下面是函数lexicoCompare()的定义,它按字典序比较两个字符串:
#include <string>
int lexicoCompare( const string &s1, const string &s2 ) {
return s1.compare(s2);
}
如果字符串s1 和s2 中的所有字符都相等,则lexicoCompare()返回0;否则,如果第一个参数表示的字符串小于第二个参数表示的字符串,则返回一个负数;如果大于,则返回一个正数。
函数名不是其类型的一部分,函数的类型只由它的返回值和参数表决定。指向lexicoCompare()的指针必须指向与lexicoCompare()相同类型的函数(带有相同的返回类型和相同的参数表)。让我们试一下:
int *pf( const string &, const string & ); // 喔! 差一点
这几乎是正确的。问题是编译器把该语句解释成名为pf 的函数的声明,它有两个参数,并且返回一个int*型的指针。参数表是正确的,但是返回值不是我们所希望的。解引用操作符* 应与返回类型关联,所以在这种情况下,是与类型名int 关联,而不是pf。要想让解引用操作符与pf 关联,括号是必需的:
int (*pf)( const string &, const string & ); // ok: 正确
这个语句声明了pf 是一个指向函数的指针,该函数有两个参数和int 型的返回值。即指向函数的指针,它与lexicoCompare()的类型相同。下列函数与lexicoCompare()类型相同,都可以用pf 来指向:
int sizeCompare( const string &, const string & );
但是,calc()和gcd()与前面两个函数的类型不同,不能用Pf 来指:
int calc( int , int );
int gcd( int , int );
可以如下定义pfi,它能够指向这两个函数:
int (*pfi)( int, int );
初始化和赋值
我们知道,不带下标操作符的数组名会被解释成指向首元素的指针。当一个函数名没有被调用操作符修饰时,会被解释成指向该类型函数的指针。例如,表达式
lexicoCompare;
被解释成类型:
int (*)( const string &, const string & );
的指针。
将取地址操作符作用在函数名上也能产生指向该函数类型的指针。因此,lexicoCompare和&lexioCompare 类型相同。指向函数的指针可如下被初始化:
int (*pfi)( const string &, const string & ) = lexicoCompare;
int (*pfi2)( const string &, const string & ) = &lexicoCompare;
指向函数的指针可以如下被赋值:
pfi = lexicoCompare;
pfi2 = pfi;
只有当赋值操作符左边指针的参数表和返回类型与右边函数或指针的参数表和返回类型完全匹配时,初始化和赋值才是正确的。如果不匹配,则将产生编译错误消息。在指向函数类型的指针之间不存在隐式类型转换。例如:
int calc( int, int );
int (*pfi2s)( const string &, const string & ) = 0;
int (*pfi2i)( int, int ) = 0;
int main() {
pfi2i = calc; // ok
pfi2s = calc; // 错误: 类型不匹配
pfi2s = pfi2i; // 错误: 类型不匹配
return 0;
}
函数指针可以用0 来初始化或赋值,以表示该指针不指向任何函数。
调用
指向函数的指针可以被用来调用它所指向的函数。调用函数时,不需要解引用操作符。无论是用函数名直接调用函数,还是用指针间接调用函数,两者的写法是一样的。例如:
#include <iostream>
int min( int*, int );
int (*pf)( int*, int ) = min;
const int iaSize = 5;
int ia[ iaSize ] = { 7, 4, 9, 2, 5 };
int main() {
cout << "Direct call: min: "
<< min( ia, iaSize ) << endl;
cout << "Indirect call: min: "
<< pf( ia, iaSize ) << endl;
return 0;
}
int min( int* ia, int sz ) {
int minVal = ia[ 0 ];
for ( int ix = 1; ix < sz; ++ix )
if ( minVal > ia[ ix ] )
minVal = ia[ ix ];
return minVal;
}
调用
pf( ia, iaSize );
也可以用显式的指针符号写出:
(*pf)( ia, iaSize );
这两种形式产生相同的结果,但是第二种形式让读者更清楚该调用是通过函数指针执行的。
当然,如果函数指针的值为0,则两个调用都将导致运行时刻错误。只有已经被初始化或赋值的指针(引用到一个函数)才可以被安全地用来调用一个函数。
函数指针的数组
我们可以声明一个函数指针的数组。例如:
int (*testCases[10])();
将testCases 声明为一个拥有10 个元素的数组。每个元素都是一个指向函数的函数指针,该函数没有参数,返回类型为int。
像数组testCases 这样的声明非常难读,因为很难分析出函数类型与声明的哪部分相关。在这种情况下,使用typedef 名字可以使声明更为易读。例如:
// typedefs 使声明更易读
typedef int (*PFV)(); // 定义函数类型指针的typedef
PFV testCases[10];
testCases 的这个声明与前面的等价。
由testCases 的一个元素引用的函数调用如下:
const int size = 10;
PFV testCases[size];
int testResults[size];
void runtests() {
for ( int i = 0; i < size; ++i )
//调用一个数组元素
testResults[ i ] = testCases[ i ]();
}
函数指针的数组可以用一个初始化列表来初始化,该表中每个初始值都代表了一个与数组元素类型相同的函数。例如:
int lexicoCompare( const string &, const string & );
int sizeCompare( const string &, const string & );
typedef int ( *PFI2S )( const string &, const string & );
PFI2S compareFuncs[2] = {
lexicoCompare,
sizeCompare
};
我们也可以声明指向compareFuncs 的指针。这种指针的类型是“指向函数指针数组的指针”。声明如下:
PFI2S (*pfCompare)[2] = &compareFuncs;
声明可以分解为:
(*pfCompare)
解引用操作符* 把pfCompare 声明为指针,后面的[2]表示pfCompare 是指向两个元素数组的指针:
(*pfCompare)[2]
typedef PFI2S 表示数组元素的类型,它是指向函数的指针,该函数返回int,有两个const string&型的参数。数组元素的类型与表达式&lexicoCompare 的类型相同,也与compareFuncs的第一个元素的类型相同。此外,它还可以通过下列语句之一获得:
compareFuncs[ 0 ];
(*pfCompare)[ 0 ];
要通过pfCompare 调用lexicoCompare,程序员可用下列语句之一:
// 两个等价的调用
pfCompare[ 0 ]( string1, string2 ); // 编写
((*pfCompare)[ 0 ])( string1, string2 ); // 显式
参数和返回类型
现在我们回头看一下本节开始提出的问题,在那里给出的任务要求我们写一个排序函数,怎样用函数指针写这个函数呢?因为函数参数可以是函数指针,所以我们把表示所用比较操作的函数指针作为参数传递给排序函数:
int sort( string*, string*,
int (*)( const string &, const string & ) );
我们再次用typedef 名字使sort()的声明更易读:
// typedef 使 sort() 的声明更易读
typedef int ( *PFI2S )( const string &, const string & );
int sort( string*, string*, PFI2S );
因为在多数情况下使用的函数是lexicoCompare(),所以我们让它成为缺省的函数指针参数:
// 提供缺省参数作为第三个参数
int lexicoCompare( const string &, const string & );
int sort( string*, string*, PFI2S = lexicoCompare );
sort()函数的定义可能像这样:
void sort( string *s1, string *s2, PFI2S compare = lexicoCompare )
{
// 递归的停止条件
if ( s1 < s2 ) {
string elem = *s1;
string *low = s1;
string *high = s2 + 1;
for (;;) {
while ( compare( *++low, elem ) < 0 && low < s2) ;
while ( compare( elem, *--high ) < 0 && high > s1) ;
if ( low < high )
low->swap(*high);
else break;
} // end, for(;;)
s1->swap(*high);
sort( s1, high - 1, compare );
sort( high + 1, s2, compare );
} // end, if ( s1 < s2 )
}
sort()是C.A.R.Hoare 的快速排序算法的一个实现。让我们详细查看该函数的定义。该函数对s1 和s2 之间的数组元素进行排序。sort()是一个递归函数,它将自己逐步地应用在较小的子数组上。停止条件是当s1 指向与s2 相同的元素时或指向s2 所指元素之后的元素(第5行)。
elem(第6 行)被称作分割元素。所有按字典序小于elem 的元素部会被移到elem 的左边,而所有大于的都被移到右边。现在,数组被分成若干个子数组,sort()被递归地应用在它们之上。
for(;;)循环的目的是完成分割。在循环的每次迭代中,low 首先被向前移动到第一个大于等于elem 的数组元素的索引上。类似地,high 一直被递减,直到移动到小于等于elem 的数组最右元素的索引上。如果low 不再小于high,则表示元素已经分隔完毕,循环结束。否则,这两个元素被交换,下一次迭代开始。虽然数组已经被分隔,但elem 仍然是数组的第一个元素。在sort()被应用到两个子数组之前,第19 行的swap()把elem 放到它在数组中最终正确的位置上。
数组元素的比较通过调用compare 指向的函数来完成。swap()字符串操作被调用,以便交换数组元素所指的字符串。
下面main()的实现用到了我们的排序函数:
#include <iostream>
#include <string>
// 这些通常应该在头文件中
int lexicoCompare( const string &, const string & );
int sizeCompare( const string &, const string & );
typedef int (*PFI)( const string &, const string & );
void sort( string *, string *, PFI=lexicoCompare );
string as[10] = { "a", "light", "drizzle", "was", "falling","when", "they", "left", "the", "museum" };
int main() {
// 调用 sort(), 使用缺省实参作比较操作
sort( as, as + sizeof(as)/sizeof(as[0]) - 1 );
// 显示排序之后的数组的结果
for ( int i = 0; i < sizeof(as)/sizeof(as[0]); ++i )
cout << as[ i ].c_str() << "/n/t";
}
编译并执行程序,生成下列输出:
"a"
"drizzle"
"falling"
"left"
"light"
"museum"
"the"
"they"
"was"
"when"
函数参数的类型不能是函数类型,函数类型的参数将被自动转换成该函数类型的指针。例如:
// typedef 表示一个函数类型
typedef int functype( const string &, const string & );
void sort( string *, string *, functype );
编译器把sort()当作已经声明为:
void sort( string *, string *, int (*)( const string &, const string & ) );
上面这两个sort()的声明是等价的。
注意,除了用作参数类型之外,函数指针也可以被用作函数返回值的类型。例如:
int (*ff( int ))( int*, int );
该声明将ff()声明为一个函数,它有一个int 型的参数,返回一个指向函数的指针,类型为:
int (*) ( int*, int );
同样,使用typedef 名字可以使声明更容易读懂。例如,下面的typedef PF 使得我们能更容易地分解出ff()的返回类型是函数指针:
// typedef 使声明更易读
typedef int (*PF)( int*, int );
PF ff( int );
函数不能声明返回一个函数类型。如果是,则产生编译错误。例如,函数ff()不能如下声明:
// typedef 表示一个函数类型
typedef int func( int*, int );
func ff( int ); // 错误: ff()的返同类型为函数类型