函數指針

“函數指針”是指向函數的指針變量,因而“函數指針”本身首先應是指針變量,只不過該指針變量指向函數。這正如用指針變量可指向整型變量、字符型、數組一樣,這裏是指向函數。如前所述,C在編譯時,每一個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其他類型變量一樣,在這些概念上一致的。函數指針有兩個用途:調用函數和做函數的參數。函數指針的說明方法爲:
數據類型標誌符 (*指針變量名)(參數);注:函數括號中的參數可有可無,視情況而定。
下面的程序說明了函數指針調用函數的方法:

#include<stdio.h>

int max(int x,int y){ return(x>y?x:y); }

void main()
{
int (*ptr)();
int a,b,c;
ptr=max;
scanf("%d,%d",&a,&b);
c=(*ptr)(a,b);
printf("a=%d,b=%d,max=%d",a,b,c);
}

ptr是指向函數的指針變量,所以可把函數max()賦給ptr作爲ptr的值,即把max()的入口地址賦給ptr,以後就可以用ptr來調用該函數,實際上ptr和max都指向同一個入口地址,不同就是ptr是一個指針變量,不像函數名稱那樣是死的,它可以指向任何函數,就看你像怎麼做了。在程序中把哪個函數的地址賦給它,它就指向哪個函數。而後用指針變量調用它,因此可以先後指向不同的函數,不過注意,指向函數的指針變量沒有++和--運算,用時要小心。

下面我們來看一個具體的例子:

#include <iostream
#include <string
using namespace std; 
 
int test(int a); 
 
void main(int argc,charargv[])   

    cout<<test<<endl;//顯示函數地址 
    int (*fp)(int a); 
    fp=test;//將函數test的地址賦給函數學指針fp 
    cout<<fp(5)<<"|"<<(*fp)(10)<<endl; 
//上面的輸出fp(5),這是標準c++的寫法,(*fp)(10)這是兼容c語言的標準寫法,兩種同意,但注意區分,避免寫的程序產生移植性問題! 
    cin.get(); 

 
int test(int a) 

    return a; 
}

  typedef定義可以簡化函數指針的定義,在定義一個的時候感覺不出來,但定義多了就知道方便了,上面的代碼改寫成如下的形式:

#include <iostream
#include <string
using namespace std; 
 
int test(int a); 
 
void main(int argc,charargv[])   

    cout<<test<<endl; 
    typedef int (*fp)(int a);//注意,這裏不是聲明函數指針,而是定義一個函數指針的類型,這個類型是自己定義的,類型名爲fp 
    fp fpi;//這裏利用自己定義的類型名fp定義了一個fpi的函數指針! 
    fpi=test; 
    cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl; 
    cin.get(); 

 
int test(int a) 

    return a; 
}

  函數指針同樣是可以作爲參數傳遞給函數的,下面我們看個例子,仔細閱讀你將會發現它的用處,稍加推理可以很方便我們進行一些複雜的編程工作。

//-------------------該例以上一個例子作爲基礎稍加了修改----------------------------- 
#include <iostream>   
#include <string>   
using namespace std;   
   
int test(int);   
 
int test2(int (*ra)(int),int); 
 
void main(int argc,charargv[])     
{   
    cout<<test<<endl; 
    typedef int (*fp)(int);   
    fp fpi; 
    fpi=test;//fpi賦予test 函數的內存地址 
 
    cout<<test2(fpi,1)<<endl;//這裏調用test2函數的時候,這裏把fpi所存儲的函數地址(test的函數地址)傳遞了給test2的第一個形參 
    cin.get(); 
}   
   
int test(int a) 
{   
    return a-1; 

 
int test2(int (*ra)(int),int b)//這裏定義了一個名字爲ra的函數指針 

    int c=ra(10)+b;//在調用之後,ra已經指向fpi所指向的函數地址即test函數 
    return c; 
}

  利用函數指針,我們可以構成指針數組,更明確點的說法是構成指向函數的指針數組,這麼說可能就容易理解的多了。

#include <iostream>   
#include <string>   
using namespace std; 
 
void t1(){cout<<"test1";} 
void t2(){cout<<"test2";} 
void t3(){cout<<"test3";} 
void main(int argc,charargv[])     

    void* a[]={t1,t2,t3}; 
    cout<<"比較t1()的內存地址和數組a[0]所存儲的地址是否一致"<<t1<<"|"<<a[0]<<endl; 
 
    cout<<a[0]();//錯誤!指針數組是不能利用數組下標操作調用函數的 
 
    typedef void (*fp)();//自定義一個函數指針類型 
    fp b[]={t1,t2,t3}; //利用自定義類型fp把b[]定義一個指向函數的指針數組 
    b[0]();//現在利用指向函數的指針數組進行下標操作就可以進行函數的間接調用了; 
    cin.get(); 
}

  仔細看上面的例子可能不用我多說大家也會知道是怎麼一會事情了,最後我們做一個重點小結,只要記住這一點,對於理解利用函數指針構成數組進行函數間接調用就很容易了!

void* a[]={t1,t2,t3};
cout<<"比較t1()的內存地址和數組a[0]所存儲的地址是否一致"<<t1<<"|"<<a[0]<<endl;

cout<<a[0]();//錯誤!指針數組是不能利用數組下標操作調用函數的

  上面的這一小段中的錯誤行,爲什麼不能這麼調用呢?

  前一篇教程我們已經說的很清楚了,不過在這裏我們還是複習一下概念,指針數組元素所保存的只是一個內存地址,既然只是個內存地址就不可能進行a[0]()這樣地址帶括號的操作,而函數指針不同它是一個例外,函數指針只所以這麼叫它就是因爲它是指向函數指向內存的代碼區的指針,它被系統授予允許與()括號操作的權利,進行間接的函數調用,既然函數指針允許這麼操作,那麼被定義成函數指針的數組就一定是可以一樣的操作的。

 
發佈了36 篇原創文章 · 獲贊 4 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章