C指針聲明解讀之左右法則
2008-06-03 08:46
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.
這段英文的翻譯如下:
右左法則:首先從最裡面的圓括號內未定義的標識符開始閱讀看起,然後往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號裡面所有的東西,就跳出圓括號。重復這個過程直到整個聲明解析完畢。
總之對聲明進行分析,最根本的方法還是按優先級和結合性來類比替換,從那些最基本的聲明進行類比,簡化,從而進行理解。下面分析幾個例子,來具體闡述如何使用這種方法。
#1:int* (*a[5])(int, char*);
首先看到標識符名a,"[]"優先級大於"*",a與"[5]"先結合。所以a是一個數組,這個數組有5個元素,每一個元素都是一個指針,指針指向"int* (int, char*)",很明顯,指向的是一個函數,這個函數參數是"int, char*",返回值是"int*"。OK,結束了一個。:)
#2:void (*b[10]) (void (*)());
b是一個數組,這個數組有10個元素,每一個元素都是一個指針,指針指向一個函數,函數參數是"void (*)()"【注:這也是一個函數指針, 參數為空,返回為void】,返回值是"void"。完畢!
#3:int(*)() (*c)[9];
c是一個指針,指針指向一個數組,這個數組有9個元素,每一個元素都是"int(*)()"(也即一個函數指針,指向一個函數,這個函數的參數為空,返回值是int型)。
#4:int (*(*d)[5])(int *);
(*d)------指針;
(*d)[5]------這個指針指向一個數組;
*(*d)[5]------這個數組中每個元素都是指針類型;
int (int *)------ 什麼類型的指針?這個類型的。
#5:int (*(*e)(int *))[5];
*e-----向右遇到括號,向左遇到*,說明e是個指針,啥指針呢?
(*e)(int *)------跳出括號向右遇到(int *),說明這個指針是個函數指針,形參為int*, 返回值為何?且聽下回分解:);
*(*e)(int *)------返回值為何?向右遇到括號,再向左,喔,遇到*了,那就是返回了一個指針了。啥指針呢? 同樣地,下回分解;
(*(*e)(int *))[5]-------向右遇到[],說明那是個指向數組的指針,是啥數組呢?不急,慢慢來;
int (*(*e)(int *))[5]-------向左遇到int,喔,明白了,就是個簡單的整型數組。OVER
當然實際當中,當需要聲明一個復雜指針時,如果把整個聲明寫成上面所示的形式,對程序可讀性將是一個巨大損害。誰要是寫出這樣BT的指針聲明,那就真是丟rp了,估計會被罵死!。
還是用typedef來對聲明逐層分解替換下吧,增強可讀性。
例如對於上面的聲明:int (*(*func)(int *))[5]; 可以這樣分解:
typedef int (*pArr)[5];
typedef pArr (*func)(int *);
這樣就容易讀得多了啊!
再看看這個啥意思? typedef int (* (* (*FUNC)(int *) )[5] )(int *); ---- 暈了吧。
其實typedef int (* (* (*FUNC)(int *) )[5] )(int *);
等價與下面的:)
typedef int (*PF)(int *);
typedef PF (*PARRAY)[5];
typedef PARRAY (*FUNC)(int *);
(*(void (*)())0)();------->這個呢?
按左右法則:
(void (*)()) -----是一個返回值為void,參數為空的函數指針原型。
(void (*)())0-----把0強轉成一個返回值為void,參數為空的函數指針,指針指向的地址為0.
*(void (*)())0-----前面加上*表示整個是一個返回值為void的函數的名字
(*(void (*)())0)()------這當然就是一個函數調用了。
再typedef化簡下:
typedef void (*pf)();
(*(pf)0)();
作者“programmer”