C言語のポインタ宣言の読み方をうまく教えてくれる記事があったので紹介します。ぼくはC言語は素人ですが、今まで見たものの中で一番わかりやすいです。
例えば
char *(*(**foo[][8])())[];
などがすっきり読めてしまいます。
以下、僕の理解を深めるためにも日本語でまとめます。
演算子の優先順位
配列を表す[]と関数を表す()は、ポインタを表す*よりも優先順位が高くなります。したがって、以下のルールが成立します。
- まず変数名から始めます
- foo is …
- 型で終わります
- foo is … init
- 真ん中を埋めるのはより難しいのですが、以下のルールでまとめることができます
- 「行けるだけ右に行って、仕方ないときは左に行く」
簡単な例
long **foo[7];
- まず変数名から出発して、型で終わらせます。
foo is … long - 次にfooの右の[7]に移動
foo is array of 7 … long - 次にfooの左となりのポインタ演算子*
foo is array of 7 pointer to … long - 最後に一番左のポインタ演算子*
foo is array of 7 pointer to pointer to long
これで終了。
ちょっとめちゃくちゃな例
char *(*(**foo [][8])())[];
- 最初は変数名から出発して、型まで
char *(*(**foo [][8])())[];
foo is … char - 変数名の右の[]
char *(*(**foo [][8])())[];
foo is array of … char - さらに右に行って[8]
char *(*(**foo [][8])())[];
foo is array of array of 8 … char - )にぶつかったので、変数名の左のポインタ演算子*を処理
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to … char - さらにその左のポインタ演算子*へ
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to pointer to … char - ()の中の処理が終わったので、右に移動。そこには関数を表す()があります
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to pointer to function returning … char - ここで)にぶつかるので、左に戻ってポインタ演算子*を処理
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to pointer to function returning pointer to … char - 左に行くと(にぶつかるので、この()は処理が終了。次に右に行って[]を処理
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to pointer to function returning pointer to array of … char - これで右端まで行ったので、左に戻って一番左のポインタ演算子*を処理
char *(*(**foo [][8])())[];
foo is array of array of 8 pointer to pointer to function returning pointer to array of pointer to char
同じことを日本語でできると最高なのですが、今はいい表現が思いつきません。何かいいのが思いついたらまた紹介します。