1. 程式人生 > >C語言學習之指標的右左法則

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);
sizeof( func ) = 4, 它是一個函式指標,在32位系統中一個指標佔4位元組。

首先找到那個未定義的識別符號,就是func,它的外面有一對圓括號,而且左邊是一個*號,這說明func是一個指

針,然後跳出這個圓括號,先看右邊,也是一個圓括號,這說明(*func)是一個函式,而func是一個指向這類

函式的指標,就是一個函式指標,這類函式具有int*型別的形參,返回值 型別是 int。

int (*func)( int *p, int (*f)(int*) );
sizeof( func ) = 4

func被一對括號包含,且左邊有一個*號,說明func是一個指標,跳出括號,右邊也有個括號,那麼func是一

個指向函式的指標,這類函式具有int *和int (*)(int*)這樣的形參,返回值為int型別。再來看一看func的形

參int (*f)(int*),類似前面的解釋,f也是一個函式指標,指向的函式具有int*型別的形參,返回值為int。

int (*func[5])(int *p);
sizeof( func ) = 20   它是一個數組,有五個元素,每個元素都是指標,所以總共佔了 5x4=20 B的空間。
這裡有點複雜,用typedef分解看的比較清楚:
typedef int f(int *p);   這是一個函式;
typedef f    *pf;           這是函式 指標;
typedef pf   apf[5];     函式指標 型別的 陣列;
也就是 它是一個 含有 五個 函式指標 元素 的陣列

func 右邊是一個[]運算子,說明func是一個具有5個元素的陣列,func的左邊有一個*,說明func的元素是指標

,要注意這裡的*不是修飾func的, 而是修飾func[5]的,原因是[]運算子優先順序比*高,func先跟[]結合,因

此*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明func陣列的元素是函式型別的指標,它

所指向的函式具有int*型別的形參,返回值型別為int。


int ( * (*func)[5] )(int *p);
sizeof( func ) = 4 它是指標,所以只佔四個位元組
typedef int f(int *p);   這是一個函式;
typedef f    *pf;           這是函式 指標;
typedef pf   apf[5];     這是一個函式指標 陣列
typedef apf *papf;    它是指標

func 被一個圓括號包含,左邊又有一個*,那麼func是一個指標,跳出括號,右邊是一個[]運算子號,說明

func是一個指向陣列的指標,現在往左看,左邊有一個*號,說明這個陣列的元素是指標,再跳出括號,右邊

又有一個括號,說明這個陣列的元素是指向函式的指標。總結一下,就是:func是一個指向陣列的指 針,這個

陣列的元素是函式指標,這些指標指向具有int*形參,返回值為int型別的函式。

int ( *(*func)(int *p) )[5];
sizeof( func ) = 4      它是指標

typedef int    a[5];      首先它是一個數組,
typedef a      *pa;        它是一個指向陣列的指標,指向陣列的指標的表示: int   (*a)[5]
typedef pa fpa( int *p);   函式的返回值為pa,而pa是一個數組指標
typedef fpa *pfpa;    它只是個指標

func是一個函式指標,這類函式具有int*型別的形參,返回值是指向陣列的指標,所指向的陣列的元素是具有5

個int元素的陣列。

注意:

int *( *p )[5] 和 int (* (*p) )[5]在用typedef分解時先後順序是不同的:
int *( *p )[5]
typedef   int *p;     首先它是一個指標
typedef   p ap[5];   一個數組的5個元素型別也是指標   即指標修飾的是陣列的元素
typedef   ap   *pap; 最後它是一個指向陣列的指標

int (* (*p) )[5]  
typedef   int a[5];   首先它是一個數組
typedef   a    * pa;   它是一個指向陣列的指標
typedef   pa *ppa; 它是一個指向陣列指標的指標

要注意有些複雜指標宣告是非法的,例如:

int func(void) [5];

func是一個返回值為具有5個int元素的陣列的函式。但C語言的函式返回值不能為陣列,這是因為如果允許函式

返回值為陣列,那麼接收這個陣列的內容的東西,也必須是一個數組,但c語言的陣列名是一個右值,它不能作

為左值來接收另一個數組,因此函式返回值不能為陣列。

int func[5](void);

func是一個具有5個元素的陣列,這個陣列的元素都是函式。這也是非法的,因為陣列的元素除了型別必須一樣

外,每個元素所佔用的記憶體空間也必須相同,顯然函式是無法達到這個要求的,即使函式的型別一樣,但函式

所佔用的空間通常是不相同的。

附:

1、 指標的型別
     從語法的角度看,你只要把指標宣告語句裡的指標名字去掉,剩下的部分就是這個指標的型別。這是指標

本身所具有的型別。讓我們看看例一中各個指標的型別: 
(1)int *ptr; //指標的型別是int * 
(2)char *ptr; //指標的型別是char * 
(3)int **ptr; //指標的型別是 int ** 
(4)int (*ptr)[3]; //指標的型別是 int(*)[3] 
(5)int *(*ptr)[4]; //指標的型別是 int *(*)[4] 
怎麼樣?找出指標的型別的方法是不是很簡單?

2、指標所指向的型別
     當你通過指標來訪問指標所指向的記憶體區時,指標所指向的型別決定了編譯器將把那片記憶體區裡的內容當

做什麼來看待。 
     從語法上看,你只須把指標宣告語句中的指標名字和名字左邊的指標宣告符 *去掉,剩下的就是指標所指

向的型別。例如: 
(1)int *ptr; //指標所指向的型別是int 
(2)char *ptr; //指標所指向的的型別是char 
(3)int **ptr; //指標所指向的的型別是 int * 
(4)int (*ptr)[3]; //指標所指向的的型別是 int()[3] 
(5)int *(*ptr)[4]; //指標所指向的的型別是 int *()[4] 
     在指標的算術運算中,指標所指向的型別有很大的作用。 
     指標的型別(即指標本身的型別)和指標所指向的型別是兩個概念。當你對C越來越熟悉時,你會發現,把

與指標攪和在一起的“型別”這個概念分成“指標的型別 ”和“指標所指向的型別”兩個概念,是精通指標的

關鍵點之一。我看了不少書,發現有些寫得差的書中,就把指標的這兩個概念攪在一起了,所以看起書來前後

矛盾,越看越糊塗。

轉載自:http://blog.csdn.net/sdwuyulunbi/article/details/6645106