本文來自網站 ieng9.ucsd.edu (http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html) 加以重新排版.

呃... 它搬家了, 新網址如後: cseweb.ucsd.edu (http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html)

本文開始:


The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them.

First, symbols. Read

  • * as "pointer to" - always on the left side
  • [] as "array of" - always on the right side
  • () as "function returning" - always on the right side

as you encounter them in the declaration.

"右-左" 法則是一套用於解讀 C 語言宣告式的完整的正規法則. 它在撰寫 C 語言的宣告式上, 也同樣有用.

首先是符號. 當你在解讀宣告式時遇到下列的符號時, 它們的讀法是註一:

  • * (只會在左邊出現) * 讀作 "指標 (其/此指標)指向的是..."
  • [] (只會在右邊出現) [] 讀作 "陣列 (其/此陣列的)元素是..."
  • () (只會在右邊出現) () 讀作 "函式 (其/此函式的)回傳值是..."

註一: 我們中文比較習慣的是說法是 指向...的指標, 元素內容為...的陣列, 回傳值為...的函式, 但是這樣的語法會使得我們相對的容易把修飾對象搞混, 尤其是語法中的符號數量很多的時候. 因此在這裡我比照英文的次序翻譯, 最後再進一步的轉換成我們比較習慣的說法. 同時, 請注意後面的 ... 修飾的對象就是它前面的 指標, 陣列或函式.

STEP 1

Find the identifier. This is your starting point. Then say to yourself, "identifier is". You've started your declaration.

STEP 2

Look at the symbols on the right of the identifier. If, say, you find "()" there, then you know that this is the declaration for a function. So you would then have "identifier is function returning". Or if you found a "[]" there, you would say "identifier is array of". Continue right until you run out of symbols *OR* hit a *right* parenthesis ")". (If you hit a left parenthesis, that's the beginning of a () symbol, even if there is stuff in between the parentheses. More on that below.)

STEP 3

Look at the symbols to the left of the identifier. If it is not one of our symbols above (say, something like "int"), just say it. Otherwise, translate it into English using that table above. Keep going left until you run out of symbols *OR* hit a *left* parenthesis "(".

Now repeat steps 2 and 3 until you've formed your declaration. Here are some examples:

步驟一
先要找到辨識字 (變數名或者函式名). 這是你的起點. 然後你把它讀作 "某某某是...".

步驟二
再來看辨識字右手邊的符號. 例如: 你找到的是 "()", 那你知道這是一個函式的宣告式. 因此你把它讀成 "某某某是函式, 其回傳值是...". 又或者你找到的是 "[]", 則要把它讀成 "某某某是陣列, 其元素是...". 如此繼續向右推進, 直到沒有符號了, 或者你遇到一個單獨的右括號 ")". (如果你遇到一個左括號, 那它只是 "()" 的開頭, 即便是左右括號中有其他的東西. 關於這個部份後面會再詳細說明)

步驟三
接著看辨識字左手邊的符號. 如果它不是我們上面列的那三種符號 (例如像是 "int") 那就直接讀出來. 否則 (是那三種符號的情況) 就依照上表列的把它轉譯出來. 如此繼續向左推進, 直到沒有符號了, 或者你遇到一個單獨的左括號 "(".

重複步驟二和步驟三, 直到讀完整個宣告式. 下面來看幾個例子:

int *p[];

1) Find identifier.
int *p[];
     ^
"p is"
p 是...
2) Move right until out of symbols or left parenthesis hit.
int *p[];
      ^^
"p is array of"
p 是陣列, 其元素是...
3) Can't move right anymore (out of symbols), so move left and find:
int *p[];
    ^
"p is array of pointer to"
p 是陣列, 其元素是指標, 其指向的是...
4) Keep going left and find:
int *p[];
^^^
"p is array of pointer to int".
p 是陣列, 其元素是指標, 其指向的是 int註二.

註二: 重新順一下, 轉成我們平常慣用的中文說法: p 是一個陣列, 此陣列的元素是指向 (int) 的指標. (抱歉, 功力不佳, 一對一翻真的沒辦法翻得非常順.)

Another example:

int *(*func())();

1) Find identifier.
int *(*func())();
       ^^^^
"func is"
func 是...
2) Move right.
int *(*func())();
           ^^
"func is function returning"
func 是函式, 其回傳值是...
3) Can't move right anymore because of the right parenthesis, so move left.
int *(*func())();
      ^
"func is function returning pointer to"
func 是函式, 其回傳值是指標, 其指向的是...
4) Can't move left anymore because of the left parenthesis, so keep going right.
int *(*func())();
              ^^
"func is function returning pointer to function returning"
func 是函式, 其回傳值是指標, 其指向的是函式, 其回傳值是...
5) Can't move right anymore because we're out of symbols, so go left.
int *(*func())();
    ^
"func is function returning pointer to function returning pointer to"
func 是函式, 其回傳值是指標, 其指向的是函式, 其回傳值是指標, 其指向的是...
6) And finally, keep going left, because there's nothing left on the right.
int *(*func())();
^^^
"func is function returning pointer to function returning pointer to int".
func 是函式, 其回傳值是指標, 其指向的是函式, 其回傳值是指標, 其指向的是 int註三.

註三: 轉成我們平常慣用的中文說法是: func 是一個函式, 此函式的回傳值是指向函式的指標, 而該函式的回傳值是指向 int 的指標.

As you can see, this rule can be quite useful. You can also use it to sanity check yourself while you are creating declarations, and to give you a hint about where to put the next symbol and whether parentheses are required.

如同你見到的, 此一法則相當有用. (即便是) 在你撰寫宣告的時候, 你自己也可以用它來檢查完整性, 提示你下一組符號該放在哪裡? 和是不是需要附加上一組括號?

Some declarations look much more complicated than they are due to array sizes and argument lists in prototype form. If you see "[3]", that's read as "array (size 3) of...". If you see "(char *,int)" that's read as "function expecting (char *,int) and returning...". Here's a fun one:

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

I won't go through each of the steps to decipher this one.

Ok. It's:

"fun_one is pointer to function expecting (char *,double) and returning pointer to array (size 9) of array (size 20) of int."

有些宣告式看上去比我們前面說的要複雜許多, 原因是他們附加了陣列的元素個數或者宣告函式原型的參數表列. 如果你看到了 [3], 它應該讀成 陣列 (含有 3 個元素), 其元素是... 如果你看到了 (char *, int), 它應該讀作函式, 其傳入的參數是 (char *, int), 其回傳值是... 下面是 fun one 的例子: (雙關語, 一個有趣的例子)

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

這個例子我就不再逐步進行解讀了.

OK. 結果是:

fun_one 是指標, 其指向的是函式, 其參數是 (char *, double), 其回傳值是指標, 其指向的是陣列 (含有 9 個元素), 其元素是陣列 (含有 20 個元素), 其元素是 int註四.

註四: 轉成我們平常慣用的中文說法是: fun_one 是一個指向函式的指標, 此函式的傳入參數是 (char *, double), 回傳值是指向陣列的指標, 而該陣列含有 9 個元素, 元素的內容是 20 個 int 的陣列.

As you can see, it's not as complicated if you get rid of the array sizes and argument lists:

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

You can decipher it that way, and then put in the array sizes and argument lists later.

如同下一行所展示的, 如果你先甩掉陣列的元素個數以及函式的參數表列, 就沒那麼的複雜:

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

你可以先這樣解讀完宣告式, 之後再將陣列的元素個數以及函式的參數表列加回去.

Some final words:

It is quite possible to make illegal declarations using this rule, so some knowledge of what's legal in C is necessary. For instance, if the above had been:

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

it would have been "fun_one is pointer to function returning array of array of pointer to int". Since a function cannot return an array, but only a pointer to an array, that declaration is illegal.

Illegal combinations include:

  • []() - cannot have an array of functions
  • ()() - cannot have a function that returns a function
  • ()[] - cannot have a function that returns an array

In all the above cases, you would need a set of parens to bind a * symbol on the left between these () and [] right-side symbols in order for the declaration to be legal.

最後

用這個法則有非常大的可能會製造出一些不合 C 的語法的宣告式. 因此, 具備一些關於什麼是合乎 C 的語法規則的知識是必要的. 譬如把上面的例子改成:

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

把它解讀出來會是 "fun_one 是一個指標, 其指向的是函式, 其回傳值是陣列, 其元素是陣列, 其元素是指標, 其指向的是 int"註五. 由於函式只能回傳一個指向陣列的指標, 而無法回傳一整個陣列, 因此這個宣告式是不合語法的.

不合語法的組合包括:

  • []() - 沒有這種東西: 陣列, 其元素內容是函式的陣列.
  • ()() - 沒有這種東西: 函式, 其回傳值是函式.
  • ()[] - 沒有這種東西: 函式, 其回傳值是陣列.

以上的例子, 如果你想要把它轉換成合於語法的宣告, 你需要用一組括號來括住: 一個在左邊的 * (指標符號) 與 ()[] 之間的右邊符號註六註七.

註五: 轉成我們平常慣用的中文說法是: fun_one 是一個指向函式的指標, 此函式的回傳值是一個陣列, 此一陣列的元素內容是另一個陣列, 該陣列的元素是指向 int 的指標.

註六: 到底是在練什麼肖話! 換句話說, 重來一次: 就是在前面補一個左括號*, 然後這一組 ()[] 符號之間插入一個右括號. 例如:
af[]() --> (*apf[])()
ff()() --> (*fpf())()
fa()[] --> (*fpa())[]

註七: 由於函式運算 () 的優先權是最高的, 同時我們只能用指標來處理函式, 因此宣告式中的 () 前面一般會有一組括號來覆蓋 () 的優先權. 陣列下標運算 [] 也有類似的狀況.

Here are some legal and illegal examples:

Declare Description OK/NA
int i; an int OK
int *p; an int pointer (ptr to an int) OK
int a[]; an array of ints OK
int f(); a function returning an int OK
int **pp; a pointer to an int pointer (ptr to a ptr to an int) OK
int (*pa)[]; a pointer to an array of ints OK
int (*pf)(); a pointer to a function returning an int OK
int *ap[]; an array of int pointers (array of ptrs to ints) OK
int aa[][]; an array of arrays of ints OK
int af[](); an array of functions returning an int NA
int *fp(); a function returning an int pointer OK
int fa()[]; a function returning an array of ints NA
int ff()(); a function returning a function returning an int NA
int ***ppp; a pointer to a pointer to an int pointer OK
int (**ppa)[]; a pointer to a pointer to an array of ints OK
int (**ppf)(); a pointer to a pointer to a function returning an int OK
int *(*pap)[]; a pointer to an array of int pointers OK
int (*paa)[][]; a pointer to an array of arrays of ints OK
int (*paf)[](); a pointer to a an array of functions returning an int NA
int *(*pfp)(); a pointer to a function returning an int pointer OK
int (*pfa)()[]; a pointer to a function returning an array of ints NA
int (*pff)()(); a pointer to a function returning a function returning an int NA
int **app[]; an array of pointers to int pointers OK
int (*apa[])[]; an array of pointers to arrays of ints OK
int (*apf[])(); an array of pointers to functions returning an int OK
int *aap[][]; an array of arrays of int pointers OK
int aaa[][][]; an array of arrays of arrays of ints OK
int aaf[][](); an array of arrays of functions returning an int NA
int *afp[](); an array of functions returning int pointers NA
int afa[]()[]; an array of functions returning an array of ints NA
int aff[]()(); an array of functions returning functions returning an int NA
int **fpp(); a function returning a pointer to an int pointer OK
int (*fpa())[]; a function returning a pointer to an array of ints OK
int (*fpf())(); a function returning a pointer to a function returning an int OK
int *fap()[]; a function returning an array of int pointers NA
int faa()[][]; a function returning an array of arrays of ints NA
int faf()[](); a function returning an array of functions returning an int NA
int *ffp()(); a function returning a function returning an int pointer NA

    MagicJackTing 發表在 痞客邦 留言(5) 人氣()