添字演算子
[]
は、参照演算子*
より優先順位が高い。
したがって、T *a[n]
は「T型ポインタを要素とする配列a
」と読むのが正しい。
最初、これが納得いかなかった。[n]
は最初にa
と結合するということは、つまりT *(a[n])
ということであるから、a
は「n
個の要素を持つ配列」というカタマリのオブジェクトを参照するポインタ型となる、ではないのか。これって逆なんじゃないの?、と。
そこで僕は、こんなアナロジーを考えた。
ウンコ味のカレー。
カレー味のウンコ。
ウンコとカレーの見た目が似ていると言っても、落ち着いて考えると、「ウンコ味のカレー」は、結局カレーである。「カレー味のウンコ」は、結局ウンコである。「どちらか選べと言われたら、俺はカレー味のウンコを食う」という人は、味がカレーだろうが「ウンコを食べる」のである。再考をお勧めしたい。言い換えると、こうなる。
ウンコ味のカレーA。 … Aはカレー(ウンコ味)。
カレー味のウンコB。 … Bはウンコ(カレー味)。
味はどうあれ、変数Aが最初に結合するのはカレーであり、変数Bが最初に結合するのはウンコである。この「結局、カレーなのか?ウンコなのか?」という観点を与えてくれるのが、C言語の型分類という概念である。
型は, その型分類(type category)によって特徴付ける。型分類とは, (この節の派生型の解釈の中で規定したように) 派生型を表現する際に最外側に示す型, 又は型が派生型を含まない場合, その型自身のいずれかとする。
(『プログラム言語C JIS X 3010-1993』「6.1.2.5 型」より引用)
「型分類とは、派生型を表現する際に最外側に示す型」という定義に注目したい。変数と直接に結合する型が、その変数の型分類(最外側の型)であると考えると、T *A[n]
において、変数A
の型分類は配列型(その要素型は「T型ポインタ」型)である。T (*B)[n]
において、変数B
の型分類はポインタ型(被参照型は「T型を要素型とする配列」型)である。
変数名と演算子の結合関係をカッコで明示すると
int *(A[n]);
int (*B)[n];
(A[n])
は配列型としか読みようが無いし、(*B)
もポインタ型としか読みようがない。同様に、配列型配列でも
int A[m][n];
添字演算子の結合規則は「左から右」である。上記のコード例における変数Aの型分類は「m
個の要素を持つ配列」型であり、その要素型は「int
型n
個の配列」型である1。これを逆にして「m
個の要素を持つ配列を要素とするn
個の配列」と読むのは誤りである。「りんごが5個入った袋が3袋あります」という文章を「5*3
」ではなく「3*5
」と立式する、と考えれば分かりやすいかもしれない。
『ポインタ完全制覇 』(前橋和弥著・技術評論社)では、英語の語順で解釈してみることを提案されていたけど、「型分類(最外側の型)は何か?」という観点から考えてみても、最初に結論を述べ、詳細情報を後に付随するという英語の語順が作用しているのは大きいと思う。
-
ちなみに、配列型配列とは「
T
型配列A
」のT
がたまたま配列型だった、というだけのことである。よく言われる「Cに多次元配列は存在しない」とは、このことを指す。 ↩