函數指針 (*(void (*)( ) )0)( ) 解析

概述

   在很多情況下,尤其是讀別人所寫代碼的時候,對 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.  函數調用中 "(* 指針變量名 )" 的兩邊的括號不可少,其中的 * 不應該理解爲求值運算,在此處它只是一種表示符號。   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章