Hatena::ブログ(Diary)

プログラミング言語を作る日記

2012-01-19 Cの宣言は英語順で読もう、という話

[]Cの宣言は英語順で読もう、という話

Cでのポインタの読み方

上記のページ、現時点ではてなブックマークが1241ついています。同趣旨のことを私は1998年に以下のページに書きました。

配列とポインタの完全制覇

こちらのはてブ数は212…… きいいっ! 悔しい!!

などという話はさておき。

上記ページに関連してだと思うのですが、Twitterで@kinabaさんが以下のようにつぶやかれておりました。


『という方針のCの入門記事』ということであれば、WebではないのでURLは貼れませんが、たとえば柴田望洋先生の「秘伝 C言語問答 ポインタ編」*1には以下の記述があります(p.22)。

ここで、下のように考えると、int x;の部分が*ptrに相当すると考えられますね。

int   x  ;
int *ptr ;

――*ptrはint型変数であると言っているように解釈できますね。

で、実のところこれを意識した上で、私は「C言語 ポインタ完全制覇」にこう書きました(p.38)。

別の考え方として、

  int *hoge_p;

という宣言について、hoge_pがhogeを指しているとき、*hoge_pはhogeと同じように使えますから、

ほらほらhoge_pに*を付けると、int型の変数hogeと同じように使えるんだよね。この宣言はつまり、hoge_pに*を付けたものがint型だという意味なんだよ

という説明をする人もいます。

この考え方は、確かにそれなりに通用するのですが(たとえば配列でも同じようにいえる)、では、

  int *&hoge;

と書くと、int型の変数としてhogeが宣言できるのでしょうか?*2――やってみるとわかりますが、これはシンタックスエラーです。

それに、この考え方は、宣言の中にconstが割り込んでくると破綻しますし(式の中じゃconstは書けない)、関数へのポインタの宣言でも問題が発生します。

関数へのポインタの宣言でも問題が発生します』について補足します。上記のような説明は、『使う時の構文と宣言の構文が似るようにという方針で設計されてる』という前提に従うわけですから、たとえばint *a;という宣言があったとき、これを『使う時』に*aと書くとint型になるよね、ということを前提としています。では関数へのポインタのときにはどうか。int (*func)(void);という宣言があったとき、これを『使う時』、みなさん(*func)()のように書くかというと*3、たいていfunc()と書くのではないでしょうか。少なくともこのケースでは、「使う時」の書き方と宣言がずれています。

実のところ関数へのポインタを持ち出すまでもなく、int *a;と宣言した変数を使うときにはa[i]と書くことなどは普通にあるのですから、『使う時の構文と宣言の構文が似るようにという方針』がさして役に立つとは思えません。『使う時』というのは式の中で使うということでしょうが、その式の中では配列ポインタに読み替えられるとか、関数関数へのポインタに読み替えられるとか、関数定義の仮引数の宣言においてのみ最外周の[]ポインタを意味するとか、謎の規則がいっぱいある以上、結局、"a ptr to func returning ptr to func returning char"のような『コンパイラの内部表現っぽい表示』を経由しない限り、理解には至らないと私は思います。

最初に紹介したページのブクマコメントには、『実際にこんなコード書かれたらキレタ方ががいいと思う』とか『おとなしく typedef 使おうず』といったコメントがつきました。確かにsignal()みたいな関数はちょっと特殊な例かと思いますし、適切にtypedefを使ったり構造体をかませたりするほうがよいのは確かです。ただし、typedefは結局『コンパイラの内部表現っぽい表示』の一部に名前を付けてコピペする機能でしかないわけで、まずは宣言をきちんと読めていないと結局使いこなせないのではないでしょうか*4

さて。最後は宣伝しますよ。

「Cの宣言は英語で読め」とか、「配列は式の中ではポインタに読み変えられる」とか、「添字演算子[]は配列とは無関係だ」とか、「Cには多次元配列は存在しない」とか、役に立つ記述でいっぱいですよ!!

10年前の本ですが、今回「Cでのポインタの読み方」というページがホットエントリに上がってきたということは、たぶんまだこういう本の需要はあるのでしょう。そして上記ページとかこういう本とかに需要があるということ自体が、「結局のところコンパイラの内部表現っぽいレベルまで理解しないとCを使いこなせない」ということを意味しているのだと思います*5

*1:この本は何度も改版されています。「秘伝C言語問答 ポインタ編 第2版」→「図解C言語 ポインタの極意」→「詳解 C言語 ポインタ完全攻略」でいいのかな。ただし、私が持っているのは「新版 秘伝 C言語問答 ポインタ編」のみです。

*2:実はCを学び始めた頃、試したことがあります。私。

*3:書けますし動きますが

*4:そしてtypedefは構文上「記憶クラス指定子」なので、typedef自体を理解するのもたぶん結構難しい

*5:「ポインタ完全制覇」では、Cの型について連結リストの図で説明しています。まさにコンパイラの内部表現ですが、それが必要だと私は判断しました。