西灣筆記

擷英採華,以備不需!

【譯】C語言「右左規則」

• C

這篇筆記、翻譯自博文:C Right-Left Rule,作者Rick Ord。尊重他人勞動果實,轉載請註明!

「右左」規則是解讀C宣告十分實用的規則。創建宣告時它也同樣有用。

首先,符號。在宣告中,視
  *   為「指向的指標」   通常在左邊
  []  為「的陣列」     通常在右邊
  ()  為「返回的函式」   通常在右邊

第一步
----------
找到標識符。這是初始點。然後妳知道,這個宣告是:「標識符是」。

第二步
----------
查看標識符右邊的符號,如果是「()」,那就表示這是一個函式宣告:「標識符是返回的函式」。抑或是「[]」,則是「標識符是的陣列」。繼續查看右邊,直到沒有符號或者遇到一個圓括號「)」。(如果遇到左圓括號,那是符號()的起始,即使括號間還有東西。更多以下詳解。)

第三步
----------
查看標識符左邊的符號。如果不是上面說的符號(比如「int」),直接代到句子裏面。否則,使用上述表格轉譯成文字。繼續查看左邊,直到沒有符號或者遇到圓括號「(」。

接下來重複第二步和第三步,直到宣告解讀完畢。以下是一些範例:

    int *p[];

1)找到標識符。
                       int *p[]
                            ^
    「p是  」

2)向右邊移動,直到沒有符號或者遇到右圓括號。
                       int *p[]
                             ^^
    「p是  的陣列」

3)不能再向右邊移動了(沒有符號),因而向左邊移動並得到:
                       int *p[]
                           ^
    「p是指向  的指標的陣列」

4)繼續向左邊移動並得到:
                       int *p[]
                       ^^^
    「p是指向int的指標的陣列」
    (或者「p是一個陣列,其元素型別為指向int的指標」)

另一個範例:

    int *(*func())();

1)找到標識符。
                       int *(*func())();
                              ^^^^
    「func是  」

2)向右邊移動。
                       int *(*func())();
                                  ^^
    「func是返回  的函式」

3)遇到右圓括號,不能再向右邊移動了,因而向左邊移動:
                       int *(*func())();
                             ^
    「func是返回指向  的指標的函式」

4)遇到左圓括號,不能再向左邊移動了,因而繼續向右邊移動:
                       int *(*func())();
                                     ^^
    「func是返回指向返回  的函式的指標的函式」

5)沒有符號,不能再向右邊移動了,因而轉向左邊:
                       int *(*func())();
                           ^
    「func是返回指向返回指向  的指標的函式的指標的函式」

6)最後,因為右邊沒有了,繼續向左邊移動。
                       int *(*func())();
                       ^^^
    「func是返回指向返回指向int的指標的函式的指標的函式」

正如妳所看到的,這個規則相當有用。妳也可以在創建宣告時用它來做完整性檢查,或提示下一個符號的位置以及括號是否必要。

有些宣告因原型中陣列長度和參數列表而看起來比實際複雜得多。如「[3]」,意為「__的(長度為3的)陣列。又如「(char *,int)」,意為「參數為(char *,int),返回__的函式」。來一個比較有趣的:

                int (*(*fun_one)(char *,double))[9][20];

這裡就不詳細羅列解讀過程,直接揭曉答案:

「fun_one是指向參數為(char *,double)並返回指向int陣列(長度為20)的陣列(長度為9)
的指標的函式的指標。」

正如妳所看到的,去掉陣列長度和參數列表後,其實並不復雜:

                int (*(*fun_one)())[][];

妳可以先這樣解讀,然後再添上陣列長度和參數列表。

結語:

使用這個規則很有可能做出非法宣告,因而關於什麼在C中是合法的基本常識是必要的。例如,如果把上述語句寫作:

                int *((*fun_one)())[][];

這樣就解讀為「fun_one是指向返回指向int的指標的陣列的陣列的函式的指標」。因為函式不能返回陣列,而是指向陣列的指標,因而這樣宣告是非法的。

非法的組合還有:

[]() - 不存在函數陣列
()() - 不存在返回函式的函式
()[] - 不存在返回陣列的函式

在以上所有情況下,為了使宣告合法,妳需要用一對括號去結合左邊*符號和右邊()和[]間的符號。

以下是一些合法和非法的例子:

int i;                  int的變數
int *p;                 int的指標(指向int的指標)
int a[];                int的陣列
int f();                返回int的函式
int **pp;               指向int的指標的指標
int (*pa)[];            指向int的陣列的指標
int (*pf)();            指向返回int的函式的指標
int *ap[];              int的指標的陣列(指向int的指標的陣列)
int aa[][];             int的陣列的陣列
int af[]();             返回int的函式的陣列(非法)
int *fp();              返回int的指標的函式
int fa()[];             返回int陣列的函式(非法)
int ff()();             返回返回int的函式的函式(非法)
int ***ppp;             指向指向int的指標的指標的指標
int (**ppa)[];          指向指向int的陣列的指標的指標
int (**ppf)();          指向指向返回int的函式的指標的指標
int *(*pap)[];          指向int的指標的陣列的指標
int (*paa)[][];         指向int的陣列的陣列的指標
int (*paf)[]();         指向返回int的函式的陣列的指標(非法)
int *(*pfp)();          指向返回int指標的函式的指標
int (*pfa)()[];         指向返回int的陣列的函式的指標(非法)
int (*pff)()();         指向返回返回int的函式的函式的指標(非法)
int **app[];            指向int指標的指標的陣列
int (*apa[])[];         指向int的陣列的指標的陣列
int (*apf[])();         指向返回int的函式的指標的陣列
int *aap[][];           int指標的陣列的陣列
int aaa[][][];          int的陣列的陣列的陣列
int aaf[][]();          返回int的函式的陣列的陣列(非法)
int *afp[]();           返回int指標的函式的陣列(非法)
int afa[]()[];          返回int的陣列的函式的陣列(非法)
int aff[]()();          返回返回int的函式的函式的陣列(非法)
int **fpp();            返回指向int指標的指標的函式
int (*fpa())[];         返回指向int的陣列的指標的函式
int (*fpf())();         返回指向返回int的函式的指標的函式
int *fap()[];           返回int指標的陣列的函式(非法)
int faa()[][];          返回int的陣列的陣列的函式(非法)
int faf()[]();          返回返回int的函式的陣列的函式(非法)
int *ffp()();           返回返回int指標的函式的函式(非法)
comments powered by Disqus