首先看幾個簡單的例子
int f; //一個整型變量
int *f; //一個指向整型的指針
不過,請看第 2 個聲明是如何工作的:它把表達式 *f 聲明為一個整數。根據這個事實,你肯定能推斷出 f 是個指向整型的指針。C 聲明的這種解釋方法可以通過下面的聲明得到驗證。
int* f,g;
它並沒有聲明兩個指針。盡管它們之間存在空白,但星號是作用於 f 的,只有 f 是一個指針。 g 只是一個普通的整型變量。
下面的例子,你曾見過:
int f();
它把 f 聲明為一個函數,它的返回值是一個整數。舊式風格的聲明對函數的參數不提供任何信息。它只聲明了 f 的返回類型。現在我將使用這種舊式風格,這樣例子看上去簡單一些,後面我將再回到完整的原型形式。
下面是一個例子:
int *f(); // f 是一個函數,它返回一個指向 int 類型的指針
要想推斷出它的含義,必須確定表達式 *f() 是如何進行求值的。首先執行的是函數調用操作符(), 因為它的優先級高於間接訪問操作符。
接下來的一個聲明更為有趣:
int (*f)(); // f 是一個指向函數的指針,該函數返回一個 int 類型的對象
確定括號的含義是分析這個聲明的一個重要步驟。第 2 對兒括號是函數調用操作符,但第 1 對兒括號只是起到聚組的作用。它迫使間接訪問在函數調用之前進行,使 f 成為一個函數指針,它所指向的函數返回一個整型值。
“函數指針”? 是的,程序中每個函數都位於內存中的某個位置,所以存在指向那個位置的指針是完全可能的。
現在下面的這個聲明應該是比較容易懂了:
int *(*f)();
它和前一個聲明基本相同, f 也是一個函數指針,它所指向的函數返回一個指向 int 類型的指針。必須對其進行間接訪問操作才能得到一個整型值。
現在讓我們把數組也考慮進去。
int f[];
這個聲明表示 f 是個整型數組,數組的長度暫時忽略,因為我們現在關心的是它的類型,而不是它的長度
【注】如果它們的鏈接屬性是external或者是作用函數的參數,即使它們聲明時未注明長度,也仍然是合法的。
下面的聲明又如何呢?
int *f[];
這個聲明又出現了兩個操作符。下標的優先級更高,所以 f 是一個數組,它的元素類型是指向整型的指針
下面的這個例子隱藏著一個圈套。不管怎樣,讓我們先推斷它的含義。
int f()[];
f 是一個函數,返回一個整型數組。這裡的圈套在於這個聲明是非法的————函數只能返回標量值,不能返回數組。
還有一個例子,頗費思量:
int f[]();
現在 f 似乎是一個數組,它的元素是返回值為整型的函數。但是,這個聲明也是非法的,因為數組元素必須具有相同的長度,但不同的函數顯然可能具有不同的長度。
但是,下面的這個聲明是合法的:
int (*f[])();
你必須找到所以的操作符,然後按照正確的次序執行它們。括號內的表達式 *f 首先進行求值,所以 f 是一個元素為某種類型的指針的數組。 表達式 () 是函數調用操作符,所以 f 肯定是一個數組,數組元素的類型是函數指針,它所指向的函數返回整型值。
如果你弄明白了上面最後一個聲明,下面這個應該是比較容易的了:
int *(*f[])();
它和上面那個聲明的唯一區別就是多了一個間接訪問操作符,所以這個聲明創建了一個指針數組,指針指向返回整型指針的函數。
新式風格的例子:
int (*f)(int,float);
int *(*g[])(int,float);
前者把 f 聲明為一個函數指針,它所指向的函數接受兩個參數,分別是一個整型和浮點型,並返回一個整型。後者把 g 聲明為一個指針數組,它所指向的函數接受兩個參數,分別是整型和浮點型,並返回整型指針。
【提示】如果你使用的是UNIX系統,並能訪問Inte.net,你可以獲得一個名叫 cdecl 的程序,它可以在 C 語言的聲明和聲明語義之間進行轉換。它可以解釋一個現存的 C 聲明:
cdecl> explain int (*(*f)())[10];
declare f as pointer to function returning pointer to array 10 of int
或者給你一個聲明語義:
cdecl> declare x as pointer to array 10 of pointer to function returning int
int (*(*x)[10])()
cdecl 的源代碼可以從 comp.sources.unix.newsgroup 存檔文件第 14 卷中獲得。