一、普通的函数指针
一旦函数被编译并载入计算机中执行,它就会占用一块内存,这块内存有一个地址,因此函数也有地址。
1. 定义一个函数指针
示例代码如下:
void (*funcPtr)();
这个例子将funcPtr定义为一个指向函数的指针,这个函数没有参数,返回值为空。
注:*funcPtr 两侧的括号是必须的,如果去掉括号,
void *funcPtr(); // 这表示funcPtr是一个函数,它返回类型为 void*
由于直接定义一个函数指针显得冗长,我们可以使用typedef对其进行简化,如下:
typedef void (*FuncPtrType)();
此时我们将FuncPtrType定义为了一个函数指针类型,
接下来我们可以使用这个类型来定义变量:
FuncPtrType funcPtr; // 这个funcPtr与void (funcPtr*)();中的含义是一样的。
许多C/C++的面试题都喜欢出一些关于指针的题目,比如:说出下列式子的含义,
2
3 float (*(*fp2)( int, int, float))( int);
4
5 typedef double (*(*(*fp3)())[ 10])();
6 fp3 a;
7
8 int (*(*fp4)[10])();
对于fp1:
我们从里向外一点一点分析,首先(*fp1)(int),这说明fp1是一个函数指针,它有一个int类型的参数;然后我们来找这个函数指针类型的返回值,注意到*(*fp1)(int),所以我们可以断定它的返回值是一个指针,指针指向什么呢?
我们可以看到最外层剩余的部分是void* [10],因此这个函数的返回值是一个指针,这个指针指向一个包含十个void*类型数据的数组。
综上:fp1是一个函数指针,它所指向的函数有一个int类型的参数,并且这个函数的返回值是一个指针,这个指针指向一个包含10个void*元素的数组。
对于fp2
就不再赘述了。fp2是一个函数指针,它所指向的函数有三个参数,参数类型分别为int,int,float;它的返回值是一个函数指针,这个函数指针所指向的函数具有一个int类型的参数,且返回类型为float。
对于fp3
fp3被定义为一个函数指针类型,这种函数指针所指向的函数的参数为空;它的返回值是一个指针,这个指针指向一个包含10元素的函数指针数组,这些函数指针所指向的函数的参数为空,返回值为double。
对于fp4
fp4是一个指针,这个指针指向一个包含10元素的函数指针数组,这些函数指针所指向的函数的参数为空,返回值为int。
《C陷阱与缺陷》中以下面一个例子对函数指针进行了讲解,如下:
(*(void(*)())0)();
如果能明白上边几个例子的含义,那么这个简直就是小case啊!
同学们,你们能说出它的含义么???
将0转换为一个指向参数列表为空,返回值类型为void的函数指针值(函数地址),并对这个指针值解引用(通过这个函数地址调用这个函数)
2. 使用函数指针
在使用函数指针之前,我们首先要弄明白什么是函数指针。
先看一段代码:、
2 cout<< " hello.\n ";
3 }
4
5 typedef void (*FuncPtr)();
6
7 int main(){
8 func();
9 (*func)(); // 输出:hello. 注意这一行
10
11 FuncPtr fp = func;
12 fp(); // 输出:hello.
13 fp = &func;
14 fp(); // 输出:hello.
15 (*fp)(); // 输出:hello.
16 }
当这段代码成功被执行的时候,我自己也有点迷糊了,产生这样一个疑问:
函数名到底是什么东东?
我最初的认识是函数名就是代表了一段代码。但是(*func)();这行代码却可以正确地运行,那函数名是一个函数指针?但是fp = &func;这一行代码也没有错啊,Oh, My Dog.
在《C陷阱与缺陷》中有这样一段描述:
fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSI C标准允许程序员将上式简写为fp(),但是一定要记住这种写法只是一种简写形式。
在表达式(*fp)()中,*fp两侧的括号非常重要,因为函数运算符()的优先级要高于单目运算符*。如果*fp没有括号,那么*fp实际上与*(fp())的含义完全一致,ANSI C把它写作*(*(fp)())的简写形式。
根据以上的描述,我们似乎可以得到这样一个结论:
1. func是一个函数指针
2. func()是(*func)()的一个简写形式
3. &func是&(*func)的简写形式
如果这样理解正确的话,那么上面那段代码就顺理成章了。
声明函数的引用:
- int f( double );
- int (* const pf)(double) = f; // pf是指向函数f()的常量指针
- int (&rf)(double) = f; // rf是函数f()的引用
从编码实践角度来看,指向函数的常量指针和函数的引用并无很大不同。除了一点,那就是指针可以显式地使用反引用语法,而对引用是不能使用显式反引用语法的,除非它被隐式转换成指向函数的指针 。
- a = pf( 12.3 ); // 直接用函数指针名调用函数
- a = (*pf)(12.3); // 使用反引用语法也是可以的
- a = rf( 12.3 ); // 通过引用调用函数
- a = f( 12.3 ); // 直接调用函数本身
- a = (*rf)(12.3); // 把引用(隐式)转换成指向函数的指针,再使用反引用语法
- a = (*f)(12.3); // 把函数本身(隐式)转换成指向函数的指针,再使用反引用语法
(注:此处六行代码主要想说明,C++的函数调用很灵活,无论是通过使用函数名本身、指向函数的指针还是函数的引用来调用函数,都既可以用名字本身,也可以使用反引用语法。尽管后两行在语法上其实是经过了一个隐式类型转换,因而会带来效率上的损失。)
这是《C++语言99个常见编程错误》错误5中的一段话。