天天看點

右左法則----複雜指針解析

 因為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的分解方法:

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

  func是一個指向數組的指針,這類數組的元素是一個具有5X6個int元素的二維數組,而這個二維數組的元素又是一個二維數組。

  typedef int (*PARA)[7][8];

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

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

  func是一個函數指針,這類函數的傳回值是一個指向數組的指針,所指向數組的元素也是函數指針,指向的函數具有int*形參,傳回值為int。

  typedef int (*PARA1)(int*);

  typedef PARA1 (*PARA2)[5];

  typedef PARA2 (*func)(int*);

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

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

  的數組。

  typedef int (*PARA1)[5];

  typedef PARA1 (*PARA2)(int*);

  typedef PARA2 func[7][8][9];

文章轉載自: http://c.chinaitlab.com/skill/803431.html