函數的指針如何說明、賦值、調用


第一個例子說明指向函數的指針如何說明、賦值、調用。 

#include 
#define TESTDATE 100 

int func(int a)  /* func用於打印一個整數 */ 
{ 
    return printf("%d\n",a); 
} 


main() 
{ 
    int (*FunctionPionter)(int a); 
    FunctionPionter = func; 
    (*FunctionPionter)(TESTDATE); 
    return 0; 
} 

其中重點語句的含義如下: 
int (*FunctionPionter)(int a); 
FunctionPionter: 指向一個返回整數的函數的指針,這個指針有一個整數參數。 
FunctionPionter = func; 
將FunctionPionter指向函數func;其中函數必須已經定義,且函數和函數指針的說明的返回值必須一致。  
(*FunctionPionter)(TESTDATE); 
通過函數指針調用函數;因爲函數指針已經指向函數,所以用*取出函數指針的內容就爲函數本身。 

下面這個例子顯示如何將指向函數的指針傳遞給函數、作爲函數的返回類型。在這個例子中,有三個函數: 
hello:返回字符指針的函數,用來返回字符串“hello world!\n” 
RetFunc:返回一個指向函數的指針的函數,且返回指針所指的那個函數爲一個返回字符指針的函數。 
call:返回一個void *型的指針,且call有一個指向函數的指針的參數,且這個函數指針返回一個字符指針 

#include 
#define MAX 100 

main() 
{ 
    void *call(char *(*)()); 
    char *(*RtnFunc())();   

/* 上面兩個說明有些複雜 */ 

    printf("%s",call(RtnFunc())); 
    return 0; 
} 

char *hello() 
{ 
    return "Hello World!\n"; 
} 

char *(*RtnFunc())() 
{ 
    return hello; 
} 

void *call(char *(*func)()) 
{ 
    return (*func)(); 
} 

上面的例子中,main()無法直接調用hello函數,利用兩個函數分別返回hello和調用hello,實現了在main()中調用hello。雖然,似乎這個程序顯得多餘但卻很好的說明了如何把指向函數的指針傳遞給函數、作爲函數的返回。其中call函數利用了void *型指針的靈活機制,使得call的適用性大爲增加,這也正是指向函數的指針的優點之一。同樣的例子是《The C Programming Language Second Edition》中下面這個函數調用: 

qsort((void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : strcmp)); 

其中,使用了兩次強制類型轉換,其中第二甚至是利用指向函數的指針,將函數的類型進行了轉換。當然上面語句在某些編譯器上無法通過,因爲某些編譯器要求條件表達: 
表達式1 ? 表達式2 : 表達式3 
中表達式2與表達式3的類型相同。當然這樣的要求是不符合ANSI標準的。在ANSI標準中,如果表達式2與表達式3的類型不同,則結果的類型由類型轉換規則決定。當然,我們可以變同一下,先將兩個函數的類型進行強制轉換來達到目的:

qsort((void **) lineptr, 0, nlines-1, numeric ? (int (*)(void *, void *))numcmp : (int (*)(void *, void *))strcmp)); 

對於如何直接說明一個像RtnFunc一樣返回指向函數的指針的函數,我查閱了不少資料,都沒有找到答案,最後是自己硬着頭皮摸索出來的。由此,我也對C的複雜說明有了更深刻的體會,將在以後的技術日記中寫出來。當然在我看來,過多的、不合適的使用這些複雜說明,並不是一種好的編程風格,因爲它將使程序變得難以理解,同時也增加了出錯的可能性。 
一個比較好的折衷的方法是使用typedef來使程序的含義明朗。下面給出用typedef給寫上面那個程序的例子,其中定義個一個類型PtoFun,用typedef說明PtoFun是指向函數的指針類型,指針所指的函數返回一個字符指針,且沒有參數。 

#include 
#define MAX 100 


typedef char *(*PtoFun)(); 

main() 
{ 
    void *call(PtoFun); 
    PtoFun RtnFunc(); 

    printf("%s",call(RtnFunc())); 
    return 0; 
} 

char *hello() 
{ 
    return "Hello World!\n"; 
} 

PtoFun RtnFunc() 
{ 
    return hello; 
} 

void *call(PtoFun func) 
{ 
    return (*func)(); 
} 

改寫後的程序的可讀性大爲增加,給人一目瞭然的感覺。
 
 
===========================================================================
 
再看一個例子:
 
看下面的有關函數指針的賦值語句:

transition->method = (void (*)(void *, void *)) &ict_snd_invite;
transition->method = (void (*)(void *, void *)) ict_snd_invite;

其中的transition變量是transition_t *類型的,
typedef struct _transition_t
{
  state_t   state;
  type_t    type;
  void (*method) (void *, void *);
}transition_t;

ict_snd_invite()函數的定義爲:
void ict_snd_invite (transaction_t * ict, sipevent_t * evt);


這兩條賦值語句有什麼區別嗎?
transition->method = (void (*)(void *, void *)) &ict_snd_invite;
transition->method = (void (*)(void *, void *)) ict_snd_invite;
答案是沒有區別。
 
如果func是一個函數,那麼&func還是它自己,array也一樣。

函數名字和數組名字是常量,再取地址本來沒有意義。但編譯爲了迎合什麼(?),所以就允許取地址,取了也白取,還是自己

引用函數名就是隱式引用函數地址,所以加&和不加是一樣的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章