| 概述
在很多情況下,尤其是讀別人所寫代碼的時候,對 C語言聲明的理解能力變得非常重要,而C語言本身的凝練簡約也使得C語言的聲明常常會令人感到非常困惑,因此,在這裡我用一篇的內容來集中闡述一下這個問題。
問題:聲明與函數
有一段程式儲存在起始地址為 0的一段記憶體上,如果我們想要調用這段程式,請問該如何去做?
答案
答案是 (*(void (*)( ) )0)( )。看起來確實令人頭大,那好,讓我們知難而上,從兩個不同的途徑來詳細分析這個問題。
答案分析:從尾到頭
首先,最基本的函式宣告: void function (paramList);
最基本的函數調用: function(paramList);
鑒於問題中的函數沒有參數,函數調用可簡化為 function();
其次,根據問題描述,可以知道 0是這個函數的入口地址,也就是說,0是一個函數的指標。使用函數指標的函式宣告形式是:void (*pFunction)(),相應的調用形式是: (*pFunction)(),則問題中的函數調用可以寫作:(*0)( )。
第三,大家知道,函數指標變數不能是一個常數,因此上式中的 0必須要被轉化為函數指標。
我們先來研究一下,對於使用函數指標的函數:比如 void (*pFunction)( ),函數指標變數的原型是什嗎?這個問題很簡單,pFunction函數指標原型是( void (*)( ) ),即去掉變數名,清晰起見,整個加上()號。
所以將 0強制轉換為一個傳回值為void,參數為空白的函數指標如下:( void (*)( ) )。
OK,結合2)和3)的分析,結果出來了,那就是:(*(void (*)( ) )0)( ) 。
答案分析:從頭到尾理解答案
(void (*)( )) ,是一個傳回值為void,參數為空白的函數指標原型。
(void (*)( ))0,把0轉變成一個傳回值為void,參數為空白的函數指標,指標指向的地址為0.
*(void (*)( ))0,前面加上*表示整個是一個傳回值為void的函數的名字
(*(void (*)( ))0)( ),這當然就是一個函數了。
我們可以使用 typedef清晰聲明如下:
typedef void (*pFun)( );
這樣定義之後,pFun就是一個傳回型別為void無參數的函數指標變數了。
這樣函數變為 (*(pFun)0 )( );
問題:三個聲明的分析
對聲明進行分析,最根本的方法還是類比替換法,從那些最基本的聲明上進行類比,簡化,從而進行理解,下面通過分析三個例子,來具體闡述如何使用這種方法。
# 1:int* (*a[5])(int, char*);
首先看到標識符名 a,"[]"優先順序大於"*",a與"[5]"先結合。所以a是一個數組,這個數組有5個元素,每一個元素都是一個指標,指標指向"(int, char*)",很明顯,指向的是一個函數,這個函數參數是"int, char*",傳回值是"int*"。OK,結束了一個。:)
# 2:void (*b[10]) (void (*)());
b是一個數組,這個數組有10個元素,每一個元素都是一個指標,指標指向一個函數,函數參數是"void (*)()"【注10】,傳回值是"void"。完畢!
注意:這個參數又是一個指標,指向一個函數,函數參數為空白,傳回值是 "void"。
# 3. doube(*)() (*pa)[9];
pa是一個指標,指標指向一個數組,這個數組有9個元素,每一個元素都是"doube(*)()"(也即一個函數指標,指向一個函數,這個函數的參數為空白,傳回值是"double")。
C語言中的函數指標
函數在記憶體中有一個物理位置,而這個位置是可以賦給一個指標的。一零點函數的地址就是該函數的進入點。因此,函數指標可被用來調用一個函數。函數的地址是用不帶任何括弧或參數的函數名來得到的。(這很類似於數組地址的得到方法,即,在只有數組名而無下標是就得到數組地址。)
怎樣說明一個函數指標變數呢 ?
為了說明一個變數 fn_pointer 的類型是"傳回值為 int 的函數指標", 你可以使用下面的說明語句:
int (*fn_pointer) ();
為了讓編譯器能正確地解釋這句語句, *fn_pointer 必須用括弧圍起來。若漏了這對括弧, 則:
int *fn_pointer ();
的意思完全不同了。fn_pointer 將是一個函數名, 其傳回值為 int 類型的指標。
函數指標變數
在C語言中規定,一個函數總是佔用一段連續的記憶體區, 而函數名就是該函數所佔記憶體區的首地址。 我們可以把函數的這個首地址 ( 或稱入口地址 ) 賦予一個指標變數, 使該指標變數指向該函數。然後通過指標變數就可以找到並調用這個函數。 我們把這種指向函數的指標變數稱為 " 函數指標變數 " 。
函數指標變數定義的一般形式為:
類型說明符 (* 指標變數名 )();
其中 " 類型說明符 " 表示被指函數的傳回值的類型。 "(* 指標變數名 )" 表示 "*" 後面的變數是定義的指標變數。 最後的空括弧表示指標變數所指的是一個函數。
例如: int (*pf)();
表示 pf 是一個指向函數入口的指標變數,該函數的傳回值 ( 函數值 ) 是整型。
下面通過例子來說明用指標形式實現對函數調用的方法。
int max(int a,int b){
if(a>b)return a;
else return b;
}
main(){
int max(int a,int b);
int(*pmax)();
int x,y,z;
pmax=max;
printf("input two numbers:/n");
scanf("%d%d",&x,&y);
z=(*pmax)(x,y);
printf("maxmum=%d",z);
}
從上述程式可以看出用,函數指標變數形式調用函數的步驟如下:
1. 先定義函數指標變數,如後一程式中第 9 行 int (*pmax)(); 定義 pmax 為函數指標變數。
2. 把被調函數的入口地址 ( 函數名 ) 賦予該函數指標變數,如程式中第 11 行 pmax=max;
3. 用函數指標變數形式調用函數,如程式第 14 行 z=(*pmax)(x,y); 調用函數的一般形式為: (* 指標變數名 ) ( 實參表 ) 使用函數指標變數還應注意以下兩點:
a. 函數指標變數不能進行算術運算,這是與數組指標變數不同的。數組指標變數加減一個整數可使指標移動指向後面或前面的數組元素,而函數指標的移動是毫無意義的。
b. 函數調用中 "(* 指標變數名 )" 的兩邊的括弧不可少,其中的 * 不應該理解為求值運算,在此處它只是一種表示符號。 |