C++中如何定義指向函數指針的指針?
打個比方已經定義了一個函數指針p:
int (*p)(int);那麼請問如何定義指向拍的指針,即如題所述指向函數指針的指針?
/* Readable!!! */
typedef int func(int);
typedef func* func_ptr;
typedef func_ptr* func_ptr_ptr;
func_ptr_ptr p;
在實踐中,請大家最好按照 @dyntkj 的回答去定義。
我來說個道理,知道這個道理後,我對C語言中定義類型這件小事立馬就一通百通了。
這個道理就是
定義的樣子,和使用的時候的樣子是一樣一樣的。
舉個最簡單的例子。
int a;
這定義了一個整型。
int *p;
這定義了一個指向整數的指針。那麼這個指針怎麼變成整數呢?很簡單,只需要 *p 就可以了。我們驚奇的發現,定義時和使用時的形式是一模一樣的。這不是個巧合,是語言設計者有意為之的。
int *p 的意思就是讓 *p 為整形。那麼 p 自然是指向整數的指針了。
再來看題主的例子。
int (*p)(int);
這個p是什麼我們不知道,但我們知道 (*p)(int) 是個整數,所以, *p 是個 返回整數的函數。而p就是指向這種函數的指針啦。
那麼如何定義函數指針的指針呢?簡單點說,就是加個 * 就行了。
假如不幸的是,我們一開始加錯了地方,變成了:
int *(*p)(int);
我們來用我們的腦袋檢驗一下(你需要熟悉運算符優先順序(這裡又有一個口訣:小括弧的運算級最高)): *(*p)(int) 是個整數,所以 (*p)(int) 是個整數的指針。 所以 *p 是個函數,返回一個整數指針,所以p是個函數指針。
那麼我們,將*挪一下地方。
int (**p)(int);
再來檢驗一下:(**p)(int)是個整數。 **p 是個函數,返回一個整數。於是 *p 是個函數的指針。 p 是個函數指針的指針。
完美。
-------------- 一看就知道我是分割線 --------------------
有的時候,有的人(就是我小時候),會直接這樣定義函數的指針的指針:
int **p(int);
他還振振有詞:這很顯然呀。
我們來看看他錯在哪裡:
小括弧的優先順序最高,當你寫下這個語句的時候: **p(int)
實際上是先執行 p(int) 的,所以,這個定義就是 **p(int) 是個整數,所以 *p(int) 就是整數的指針,而p(int) 就是指針的指針,所以p就是函數的指針(實際上最後一步是個語法糖。不過我們這裡就不追究啦)
漲姿勢的都點贊吧。
=======
更新:原來《C陷阱與缺陷》第一章講的就是這個!int (**p)(int);
我來回答一下吧,乾脆把問題擴大得更廣泛、通用一點,解釋下C++里的聲明(用的是C語言的BNF,足夠解決題主的疑問和說明這些難懂的語法了),特別說明合成類型的說明。以下討論的對C和C++一樣適用。(僅限這些討論中C++和C可以通用,加上這句是防止某些人詰難。)
如果在任何一個點你看不下去了,請直接跳到最後一段。
c語言的聲明的語法是這樣的(看不懂無視就可以)——&
| &
| &
&
&
| &
&
| { &
| { &
& (不了解的話可以搜索一下BNF,後面盡量寫得不需要這方面的知識) 都可以。
| &
int typedef const auto a;
令題主頭痛的東西在這裡:
}? & ::= * {& }? )&
&
&
| ( &
| &
| &
| &
然後你就會發現,
)&
| ( &
| &
| &
| &
int *a[2];和int *a(int);里,[2]和(int)結合的比*緊,相當於int *(a[2]); 和 int (*a(int)); (被捉了bug,這裡可以加括弧,原句:「雖然實際上這裡是不能加括弧的」)。
以上講的都是語法層面上的。從語義上講,語法里結合的越緊密的部分,在類型里的層次越靠上。比如說int*a[2]; , [2]結合的更緊,所以就是個array with 2 elements of ... type,中文說就是「XX類型的有兩個元素的數組」,然後去掉了[2]來看剩下的是int *a; ,結合最緊的是指針,所以就是array with 2 elements of pointer to ... type,對應中文「XX類型指針的有兩個元素的數組」,最後把int放進去,就得到最後的結果了——array with 2 elements of pointer to int type,「int類型指針的有兩個元素的數組」。
類型可以看成是一個樹形結構。如果你看得懂這句話,上面那些就很好理解了。
對於題主的問題,指向函數指針的指針,我們把上面的過程反過來想,我們的類型是個指針,所以*和標識符p最緊密,也就是(*p),然後他指向的是個函數指針,也就是指向函數的指針(雖然嚴格來說函數指針不是指針),再貼一層*,也就是(*(*p)),最後他是個函數,加上參數列表(看上面的語法定義你會發現參數的類型語法也是一個類型聲明(沒有分號),得到((*(*p))(int)),最後返回類型是int,我們得到int ((*(*p))(int)),添加上分號就是完整的聲明語句了(後面還可以賦上初值)。
以上這些對於其他複合類型也是一樣可以分析的。個人的一點吐槽,C語言的類型聲明為什麼不做成類似C++模板語法那樣呢?Array&
template&
array&
研究這個東西,除了寫編譯器之類的,還真是無聊啊——和回字有幾種寫法差不多。
看了這麼多你肯定有點暈,我也和你一樣暈——所以珍愛生命,多用using(typedef),多寫些代碼(比如class(struct)包裝)來代替單純的複合類型。class給了類型有意義的名字,讓編譯器可以用類型檢查出來更多不好人工查出的錯誤,也讓你節約了這種方面的腦力——這種事情上真的沒必要費腦。用C語言的時候你只有上述的方法可以用,C++還有上面所述的小技巧,還有auto類型推斷,還有std::function,多了解點新鮮工具都是有好處的。
(PS. typedef 的類型作為聲明語句最左邊的類型說明符的時候,相當於把類型整個放在類型架構的最下面,和int放在最左邊一樣)
參考資料:The syntax of C in Backus-Naur formint f(int i){ cout &<&< i &<&< endl; return 0; }
int main(){
int (*p)(int) = f;
decltype(p)* p2 = p;
(*p2)(1024);
system("pause");
return 0;
}
用一次C++11的特性吧。
#include&#include&
*定義std::function與實現類似函數指針功能
**/typedef std::function& std::cout &<&< (*ptf)(a,b)&<&
《C專家編程》...應該是這本吧...
c++11大法好呀
void test()
{ cout &<&< "testing" &<&< endl;}auto test_ptr = test;auto test_ptr_ptr = test_ptr;在c++11下,可以這麼偷懶獲得類型:
1 #include &
2
3 using namespace std;
4
5 int func(int a)
6 {
7 return a + 1;
8 }
9
10 int main(int argc , char *argv[])
11 {
12 decltype(func) *func_ptr = func; //或者 auto func_ptr = func;
14
15 cout &<&< func_ptr(2015) &<&< endl;
16 }
using func_ptr_ptr = std::add_pointer&
推薦閱讀:
※如何有效的練習並且提升寫代碼的能力?
※初始化、顯式初始化、隱式初始化。這幾個區別是什麼?
※在使用lib時,代碼都被鏈接到exe中去了嗎?
※學習C++,應該循序漸進的看哪些書?