1. 程式人生 > >C/C++函式指標(typedef簡化定義)

C/C++函式指標(typedef簡化定義)

學習要點:
        1,函式地址的一般定義和typedef簡化定義;
        2,函式地址的獲取;
        3,A函式地址作為B函式引數的傳遞;
    函式存放在記憶體的程式碼區域內,它們同樣有地址.如果我們有一個int test(int a)的函式,那麼,它的地址就是函式的名字,這一點如同
陣列一樣,陣列的名字就是陣列的起始地址。
    定義一個指向函式的指標用如下的形式,以上面的test()為例:
    int (*fp)(int a);//這裡就定義了一個指向函式(這個函式的引數僅僅為一個int型別)的指標
    一般定義方式:
    data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);
    函式指標不能絕對不能指向不同型別,或者是帶不同形參的函式,這點尤其注意.
    在定義函式指標的時候我們很容易犯如下的錯誤:
    int *fp(int a);//這裡是錯誤的,因為按照結合性和優先順序來看就是先和()結合,然後變成了一個返回整形指標的函數了,而不是函式指標.
    下面我們來看一個具體的例子:
    #include 
<iostream>
    #include 
<string>usingnamespace std;

    
int test(int a);
 
    
void main(int argc,char* argv[])   
    {
        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>usingnamespace std;
 
    
int test(
int a);
 
    
void main(int argc,char* argv[])   
    {
        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>usingnamespace std;   
   
    
int test(int);   
 
    
int test2(int (*ra)(int),int);
 
    
void main(int argc,char* argv[])     
    {   
        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>usingnamespace std;
 
    
void t1(){cout<<"test1";}
    
void t2(){cout<<"test2";}
    
void t3(){cout<<"test3";}
    
void main(int argc,char* argv[])     
    {
        
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]()這樣地址帶括號的操作,而函式指標不同它是一個例外,函式指標只所以這麼叫它就是因為它是指向函式指向記憶體的程式碼區的指標
,它被系統授予允許與()括號操作的權利,進行間接的函式呼叫,既然函式指標允許這麼操作,那麼被定義成函式指標的陣列就一定是可以一樣的操作的。

    對上一例子改動:
//a.c    #include <iostream>
    #include 
<string>usingnamespace std;

    
void t1(){cout<<"test1\n";}
    
void t2(){cout<<"test2\n";}
    
void t3(){cout<<"test3\n";}

    
int main(int argc,char* argv[])
    {
            
void* a[3];
            a[
0]=(void*)t1;
            a[
1]=(void*)t2;
            a[
2]=(void*)t3;
            printf(
"t1=0x%x\n",*t1);
            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();
            
return0;
    }

    編譯:
    [[email protected] ]# g++ a.c -o a
    a.c: In function `int main(int, char**)':
    a.c:16: warning: the address of `void t1()', will always be `true'
    [[email protected] ]# ./a
    t1=0x804881c
    比較t1()的記憶體地址和陣列a[0]所儲存的地址是否一致1|0x804881c
    test1