C指針聲明解讀之左右法則

C語言全部複雜的指針聲明,都是由各類聲明嵌套構成的。如何解讀複雜指針聲明呢?右左法則是一個既著名又經常使用的方法。不過,右左法則其實並非C標準裏面的內容,它是從C標準的聲明規定中概括出來的方法。C標準的聲明規則,是用來解決如何建立聲明的,而右左法則是用來解決如何辯識一個聲明的,二者能夠說是相反的。右左法則的英文原文是這樣說的: 

         The   right-left   rule:   Start   reading   the   declaration   from   the   innermost   parentheses,   go   right,   and   then   go   left.   When   you   encounter   parentheses,   the   direction   should   be   reversed.   Once   everything   in   the   parentheses   has   been   parsed,   jump   out   of   it.   Continue   till   the   whole   declaration   has   been   parsed.  


這段英文的翻譯以下: 

         右左法則:首先從最裏面的圓括號看起,而後往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號裏面全部的東西,就跳出圓括號。重複這個過程直到整個聲明解析完畢。 

筆者要對這個法則進行一個小小的修正,應該是從未定義的標識符開始閱讀,而不是從括號讀起,之因此是未定義的標識符,是由於一個聲明裏面可能有多個標識符,但未定義的標識符只會有一個。 

如今經過一些例子來討論右左法則的應用,先從最簡單的開始,逐步加深: 

int   (*func)(int   *p); 

首先找到那個未定義的標識符,就是func,它的外面有一對圓括號,並且左邊是一個*號,這說明func是一個指針,而後跳出這個圓括號,先看右邊,也是一個圓括號,這說明(*func)是一個函數,而func是一個指向這類函數的指針,就是一個函數指針,這類函數具備int*類型的形參,返回值類型是int。 

int   (*func)(int   *p,   int   (*f)(int*)); 

func被一對括號包含,且左邊有一個*號,說明func是一個指針,跳出括號,右邊也有個括號,那麼func是一個指向函數的指針,這類函數具備int   *和int   (*)(int*)這樣的形參,返回值爲int類型。再來看一看func的形參int   (*f)(int*),相似前面的解釋,f也是一個函數指針,指向的函數具備int*類型的形參,返回值爲int。 

int   (*func[5])(int   *p); 

func右邊是一個[]運算符,說明func是一個具備5個元素的數組,func的左邊有一個*,說明func的元素是指針,要注意這裏的*不是修飾func的,而是修飾func[5]的,緣由是[]運算符優先級比*高,func先跟[]結合,所以*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明func數組的元素是函數類型的指針,它所指向的函數具備int*類型的形參,返回值類型爲int。 


int   (*(*func)[5])(int   *p); 

func被一個圓括號包含,左邊又有一個*,那麼func是一個指針,跳出括號,右邊是一個[]運算符號,說明func是一個指向數組的指針,如今往左看,左邊有一個*號,說明這個數組的元素是指針,再跳出括號,右邊又有一個括號,說明這個數組的元素是指向函數的指針。總結一下,就是:func是一個指向數組的指針,這個數組的元素是函數指針,這些指針指向具備int*形參,返回值爲int類型的函數。 

int   (*(*func)(int   *p))[5]; 

func是一個函數指針,這類函數具備int*類型的形參,返回值是指向數組的指針,所指向的數組的元素是具備5個int元素的數組。 

要注意有些複雜指針聲明是非法的,例如: 

int   func(void)   [5]; 

func是一個返回值爲具備5個int元素的數組的函數。但C語言的函數返回值不能爲數組,這是由於若是容許函數返回值爲數組,那麼接收這個數組的內容的東西,也必須是一個數組,但C語言的數組名是一個右值,它不能做爲左值來接收另外一個數組,所以函數返回值不能爲數組。 

int   func[5](void); 

func是一個具備5個元素的數組,這個數組的元素都是函數。這也是非法的,由於數組的元素除了類型必須同樣外,每一個元素所佔用的內存空間也必須相同,顯然函數是沒法達到這個要求的,即便函數的類型同樣,但函數所佔用的空間一般是不相同的。 

做爲練習,下面列幾個複雜指針聲明給讀者本身來解析,答案放在第十章裏。 

int   (*(*func)[5][6])[7][8]; 

int   (*(*(*func)(int   *))[5])(int   *); 

int   (*(*func[7][8][9])(int*))[5]; 

         實際當中,須要聲明一個複雜指針時,若是把整個聲明寫成上面所示的形式,對程序可讀性是一大損害。應該用typedef來對聲明逐層分解,加強可讀性,例如對於聲明: 

int   (*(*func)(int   *p))[5]; 

能夠這樣分解: 

typedef     int   (*PARA)[5]; 
typedef   PARA   (*func)(int   *); 

這樣就容易看得多了數組

 

typedef void (*comm_callback)(char*, int, void*); 是什麼意思?

定義一個函數指針類型comm_callback
這個函數類型是這樣:返回值void 參數:char*,int,void*
能夠直接使用comm_callback定義一個函數指針
如:comm_callback pFun;函數

 

類型定義:以一個或多個類型標識符來命名一個類型表達式。
類型表達式:由數據類型名和類型修飾符(如:*,[],&,())所構成的式子。
例如:int * f(double); 這就是用類型表達式  int * (double) 定義類型標識符 f;
同理:typedef void (CALLBACK* LINEDDAPROC)(int, int, LPARAM); 命名了一個類型標識符名叫LINEDDAPROC,而它的類型表達式就是void (CALLBACK*)(int, int, LPARAM)spa

相關文章
相關標籤/搜索