引言
我們都知道,數組名就是指向數組第一個元素的常量指針。同理,對于一個函數而言,函數名也是指向函數第一條指令的常量指針。而編譯器要做的就是在程式編譯之後,為每個函數配置設定一個首位址,即該函數第一條指令的位址。一般情況下,我們可以用一個指針來儲存這個位址,而這個指針就是函數指針,該指針可以看作是它指向函數的别名,是以我們可以用該指針來調用這個函數。
函數指針的聲明方法
type (*func)(type &,type &)
該語句聲明了一個指針func,它 指向了一個函數,這個函數帶有了2個type型參數并傳回一個type的值。
p.s. type類型可以被看成是int啊或者是floast等C++的類型。
注意事項
一個指向函數的指針必須確定該函數被定義且配置設定了記憶體,否則它 将指向一個空位址,這個可是大忌!
特别注意 第一個括号的位置。如果我們不寫括号,如下:
type *func(type ,type)
這就不是一個指向函數的指針了,而是聲明了一個函數,該函數傳回一個type類型的指針
函數指針應用示例
我們以這樣一個程式為例:該程式的功能是計算三角形的矩形的面積。其中,三角形的長和高,矩形的長和寬由使用者自己輸入,并且我們提供一個交換函數,用來交換使用者輸入的長和高(寬)。
#include "stdafx.h"
#include <iostream>
using namespace std;
double triangle_area(double &x, double &y);
double rectangle_area(double &x, double &y);
double swap_value(double &x, double &y);
double set_value(double &x, double &y);
double print_area(double &x, double &y);
double triangle_area(double &x, double &y)
{
return x*y*0.5;
}
double rectangle_area(double &x, double &y)
{
return x*y;
}
double swap_value(double &x, double &y)
{
double temp;
temp = x;
x = y;
y = temp;
return 0.0;
}
double print_area(double &x, double &y)
{
cout << "執行函數後:\n";
cout << "x=" << x << " y=" << y << endl;
return 0.0;
}
double set_value(double &x, double &y)
{
cout << "自定義長寬(高)為:\n";
cout << "長為:";
cin >> x;
cout << "寬或者高為:";
cin >> y;
return 0.0;
}
int _tmain(int argc, _TCHAR* argv[])
{
bool quit = false;
double a = 2;
double b = 3;
char choice;
while (quit == false)
{
cout << "=========================" << endl;
cout << "退出(q);設定長、寬或高(1);三角形面積(2);矩形面積(3);交換長寬或高(4)."<<endl;
cin >> choice;
switch (choice)
{
case 'q':
quit = true;
break;
case '1':
set_value(a, b);
print_area(a, b);
break;
case '2':
print_area(a, b);
cout << "三角形的面積為:\t" << triangle_area(a, b) << endl;
break;
case '3':
print_area(a, b);
cout << "矩形的面積為:\t" << rectangle_area(a, b) << endl;
break;
case '4':
swap_value(a, b);
print_area(a, b);
break;
default:
cout << "請按規矩出牌!" << endl;
//break;
}
}
return 0;
}
在這個例子中,我們采用普通函數的方法,來輸出三角形和矩形的值,輸出如下:

下面,我們來看看如果采用函數指針,效果會是怎樣?由于我們在前面分析過了,函數指針就是一個指向函數的指針。那麼我們在調用函數的時候,就可以運用指針來調用這個函數。而且,衆所周知,指針可以作為一個函數的參數,那麼函數指針也不應該例外。這樣就好了,我們可以将函數的指針作為函數參數來調用。對于一個普通要調用指針的函數而言,聲明應該是這個樣子的:
type func(type*, type , type)
那麼由上面這句話就可以看出來,這個函數func的 第一個參數就是一個指向type類型的指針,而後面兩個參數就是兩個類型為type的形式參數。結合本文一開始所列的函數參數的聲明格式,那麼函數指針作為函數參數的一般形式就是:
type func(type(*p)(type &, type &),type &,type &);
該函數func有3個參數,第一個參數為type(*p)(type &, type &),這就是一個函數指針, 它指向一個帶有兩個type類型的參數并且傳回type值的函數,另外兩個參數都是type類型的引用。
這樣一來,我們就可以 利用函數指針把函數作為另外一個函數的參數調入到那個函數中使用了。對于上面這個例子,我故意把所有的函數都聲明為帶有兩個double類型的變量,且傳回值均為double。這樣做的原因隻是為了讓後面我們在利用函數指針調用的時候友善一些。因為我們隻需要将函數指針聲明成與之想比對的函數就行了。從上面這個例子看出,它麻煩就麻煩在每次輸出的時候都需要在case語句中進行操作,那麼我們能不能利用函數指針将其一并簡化到print函數中呢,請看下例:
#include "stdafx.h"
#include <iostream>
using namespace std;
double triangle_area(double &x, double &y);
double rectangle_area(double &x, double &y);
double swap_value(double &x, double &y);
double set_value(double &x, double &y);
/*double print_area(double &x, double &y);*/
//利用函數指針輸出面積
double print_area(double(*p)(double&, double&), double &x, double &y);
double triangle_area(double &x, double &y)
{
cout << "三角形的面積為:\t" << x*y / 0.5 << endl;
return 0.0;
}
double rectangle_area(double &x, double &y)
{
cout << "矩形的面積為:\t" << x*y << endl;
return 0.0;
}
double swap_value(double &x, double &y)
{
double temp;
temp = x;
x = y;
y = temp;
return 0.0;
}
double set_value(double &x, double &y)
{
cout << "自定義長寬(高)為:\n";
cout << "長為:";
cin >> x;
cout << "寬或高為:";
cin >> y;
return 0.0;
}
double print_area(double(*p)(double&, double&), double &x, double &y)
{
cout << "執行函數前:\n";
cout << "x=" << x << " y=" << y << endl;
p(x, y);
cout << "函數指針傳值後:\n";
cout << "x=" << x << " y=" << y << endl;
return 0.0;
}
int _tmain(int argc, _TCHAR* argv[])
{
bool quit = false;
double a = 2, b = 3;
char chioce;
double(*p)(double&, double&);
//聲明的p為一個函數指針,它指向的函數帶有兩個double型的參數并傳回double
while (quit==false)
{
cout << "=========================" << endl;
cout << "退出(q);設定長、寬或高(1);三角形面積(2);矩形面積(3);交換長寬或高(4)." << endl;
cin >> chioce;
switch (chioce)
{
case 'q':
quit = true;
break;
case '1':
p = set_value;
print_area(p, a, b);
break;
case '2':
p = triangle_area;
print_area(p, a, b);
break;
case '3':
p = rectangle_area;
print_area(p, a, b);
break;
case '4':
p = swap_value;
print_area(p, a, b);
break;
default:
cout << "請按規矩出牌!" << endl;
//break;
}
}
return 0;
}
采用了函數指針的方式,可以看到,在case語句中隻需要制定每個函數指針所指向的函數是什麼,那麼在print函數中,我們就可以調用這個函數了。輸出如下所示:
在該程式的第60行,我們就聲明了一個函數指針。可以看到,在程式的case語句中,我們隻需要把這個函數指針傳遞到print_area()函數裡面就可以了。這樣十分友善。而且我們可以看到,其實隻要在print_area()中,即程式的第49行加上對這個函數指針的調用,我們就可以利用指針所指向的函數了。這十分友善。
但是有幾個小知識點應該注意一下:
聲明函數指針時,其傳回值,參數個數,參數類型應該與需要它指向的函數保持一緻;否則,編譯器會報錯,無法從“***”轉換到“***”;
利用函數指針指向某個函數的時候,我們隻用,也隻能給出該函數的函數名,不能把參數一并給出了。比如說在上例中,如果我們把程式的第85行改成:
p=swap_value(a,b);
那麼編譯器會報錯:
error C2440: “=”: 無法從“double”轉換為“double (__cdecl *)(double &,double &)
這個錯誤的原因就是因為我們忘記了在文章一開頭所講的函數指針的一句話: 函數名也是指向函數第一條指令的常量指針。因為函數指針就是指向其函數的位址的,那麼我們就應該利用函數指針來指向函數名就可以了。
補充
如果你認為上面所訴的函數指針的聲明格式有點羅嗦,那麼我們也可以利用 typedef來簡化聲明和定義的操作。比如說在上例2的第60行,那麼長一串。我們完全可以在在程式一開始利用typedef來代替:
typedef double (*vp)(double &,double &);
這樣一來,我們就可以把程式的第60行簡化成:
vp p;
而且,我們在聲明和定義print_area()函數的時候,就可以程式的第12行和第45行換成:
//函數聲明
double print_area(vp,double &x,double &y);
//函數定義
double print_area(vp p, double &x,double &y)