C語言指標右左法則
對於一些複雜的宣告式,我們往往很難理解它代表的含義,
int (*(*(*foo)(int*))[5])(int*); /* complex and difficult to understand */
“右左法則“是前輩們總結出的一套分析規則,藉助它,可以準確快速地分析出上式的具體含義。
一:宣告式的組成
一個宣告式可以分為兩部分,“basic type”和“derived types”,如下,
對於“derived types”,它主要由以下幾部分組成,
foo
,識別符號。
*
,pointer to... ,指標符號。
[]
,array of... ,表示陣列的一對方括號。注意方括號內也可以有個數字,例如 [10]
,那麼它的含義就是"array of 10..."。
()
,function returning... ,表示函式名後面包住引數的一對括號。注意括號內也可以有引數,例如 (int*)
,那麼它的含義就是“function with parameter 'int*' returning...”。
二:右左法則
根據運算子優先順序,"array of" []
和"function returning" ()
的優先順序比"pointer to" *
高,因此定義的規則如下:
- 總是以識別符號為開頭,
foo is ...
- 總是以“basic type”為結尾,
foo is ... char
-
中間部分比較複雜,但有一個規律,
“go right when you can, go left when you must”
接下來我們就分別以一易一難兩個例子具體講下這個“右左法則”。
三:簡單的例子
long **foo[7];
我們一步一步地按照上面的“右左法則”來,
- long * * foo
[7]
以識別符號為開頭,以“basic type”為結尾,
foo is ... long
-
long* *
foo[7]根據“右左法則”的“go right when you can“,下一步是分析”array of 7...“
[7]
,foo is array of 7 ... long
-
long* *foo [7]已走到右邊的盡頭,故“go left when you must”,下一步分析指標符號
*
,foo is array of 7 pointer to ... long
-
long** foo [7]繼續“go left when you must”,依舊是一個指標,
foo is array of 7 pointer to pointer to long
-
long * * foo [7]全部分析完畢,完整的含義為: foo is array of 7 pointer to pointer to long ,翻譯為:foo是一個長度為7的陣列,這個陣列的元素是指向long型指標的指標。
四:複雜的例子
int (*(*(*foo)(int*))[5])(int*);
- int ( * ( * ( * foo
) ( int* ) ) [5] ) ( int* )
以識別符號為開頭,以“basic type”為結尾,
foo is ... int
-
int( * ( * ( *foo) ( int* ) ) [5] ) ( int* )
走到括號尾,故“go left when you must”,下一步分析指標符號
*
,foo is pointer to ... int
-
int( * ( *
( * foo )( int* )) [5] ) ( int* )
一對括號內的內容分析完,繼續“go right when you can“,下一步分析
(int*)
,foo is pointer to function with parameter 'int*' returning ... int
-
int( * ( *( * foo ) ( int* )) [5] ) ( int* )
走到括號尾,故“go left when you must”,下一步分析指標符號
*
,foo is pointer to function with parameter 'int*' returning pointer to ... int
-
int( *
( * ( * foo ) ( int* ) )[5]) ( int* )
此時可以“go right when you can“,下一步分析
[5]
,foo is pointer to function with parameter 'int*' returning pointer to array of 5 ... int
-
int( *( * ( * foo ) ( int* ) ) [5]) ( int* )
走到括號尾,故“go left when you must”,下一步分析指標符號,
foo is pointer to function with parameter 'int*' returning pointer to array of 5 pointer to ... int
-
int ( * ( * ( * foo ) ( int* ) ) [5] )( int* )此時可以“go right when you can“,下一步分析
(int*)
,foo is pointer to function with parameter 'int*' returning pointer to array of 5 pointer to function with parameter 'int*' returning int
-
int ( * ( * ( * foo ) ( int* ) ) [5] ) ( int* )全部分析完畢,完整的含義為: foo is pointer to function with parameter 'int*' returning pointer to array of 5 pointer to function with parameter 'int*' returning int ,翻譯為:foo是一個函式指標,這個函式的引數是“int*”,返回的是一個指向長度為5的陣列的陣列指標,這個陣列的元素也是函式指標,引數也是“int*”,返回的是“int”。
五:抽象宣告式(Abstract Declarators)
抽象宣告式(Abstract Declarators)通常用作資料型別轉換和作為 sizeof
的引數,與上面幾個宣告式不同的是,抽象宣告式缺少 識別符號 ,
int (*(*)())()
“右左法則”都是從識別符號開始分析的,那麼問題來了,抽象宣告式沒有識別符號,該從哪裡開始呢? 找到它應該在的位置 。
我們仔細地觀察,會發現,不管是什麼宣告式,識別符號滿足下面幾條規律:
* [] ()
根據這幾條規律,我們再來看看上面的抽象宣告式。根據規律1和規律3,可以鎖定大致範圍,
• ) •
其中“•”這個點就代表識別符號應該在的位置,再根據規律4,最終鎖定在左邊那個“•”的位置,那麼這個抽象宣告式我們可以看成這樣,
int (*(*foo)())()
如此,我們就可以很容易地知道它的含義了, foo is pointer to function returning pointer to function returning int 。
六:呼叫約定(Calling Conventions)
在Windows平臺上的Win32程式設計中,我們經常會碰到一些呼叫約定符號 __cdecl,__fastcall
和 __stdcall
等等,
extern int __cdecl main(int argc, char **argv); extern BOOL __stdcall DrvQueryDriverInfo(DWORD dwMode, PVOID pBuffer, DWORD cbBuf, PDWORD pcbNeeded);
這些呼叫約定符號是用來修飾函式的,所以若出現在宣告式中,我們應當將其置於“function returning” ()
之前,比如,
BOOL (__stdcall *foo)();
它的含義為: foo is pointer to __stdcall function returning BOOL 。