林晴比古著「改訂新C言語入門」について

 こ(れら)の本は、僕自身は入門者にお薦めしない本です。

 ただ、むちゃくちゃヒドイというわけでもなくて、わかり易いところは わかり易いですし、この本で入門されて上達されている方も多数おられる ようです。ある意味、杞憂なのかもしれません。  危なっかしく見えるけれど教え方の上手いということでしょう。

 ただ、入門者にわかりやすい(かつ売れている)からこそ、見過ごせない 誤記や考え方を見るにつけ不安になるのですね。

 で、以下の文を書いたのですが、時間が経って見ると、いろいろ助長な ところや自分の好みや、この本(林氏)に対する悪感情のようなものが出過ぎている ようでマズイなあと言う気もしています。もっとすっきりかけなきゃ...

 理想としては、この本で入門された方に腹立てられずに読んでもらえる ような書き方ができればいいのだけど。

 と思いつつも、書きなおすパワーがないので、とりあえず、このままですが。 気に障ったらすみません。

2000-05-14


「改訂新C言語入門」

  ビギナー編 ¥1900−
  シニア編 ¥2500−
  スーパービギナー編 ¥1600−
  林晴比古 著
  ソフトバンク

 まず、こ(れら)の本は入門者にお薦めしない本です。
(薦めるならば入門書紹介にかいたような本を薦めます)

 著者は古くからコンピュータ関係の入門書や一般解説書などを多く手がけられる ライターの方のようで世間的には評判がいいようですが、 (本によっては)実作業にあたる者からは不評なことがあり、 これらの本もそういったモノに思えます。
 まったくの論外ではなく、 それどころか入門者によっては(入り口として)非常にわかりやすい本の場合も あるでしょう。著者は優しい人にも思えます。 なかなか上達できない人に、それでいいよ、と囁くような……

 「改訂 新C言語入門」は、「新C言語入門」(1991初版)の改訂版(1998改訂1版)で、ビギナー編、シニア編、スーパービギナー編と三冊出ているのですが、 内容的にはかなりの部分重複しており、シニア編を ベースにビギナー/スーパービギナー編は項目を選んで重要なことを先に 学習するのを狙ったようです。 が、項目の選び方/端折り方には首を捻りたくなるようなところがあり、 それぞれの本だけでは単体の入門書として 不味い面が増していますし、 ビギナー編のまえがきを読むとシニア編とペアで一組のようなことが書かれているとは いえ、本の題名からは2冊で一人前だとは気付きにくいです。 またスーパービギナー編を用意したならば内容的にあまりに中途半端なビギナー編は 全く不要に思います。

 が、この本は、山ほどあるC入門書のなかでは入門しやすい部類かもしれません。
この本で入門された方/されている方はたくさん居られるでしょうし、 問題なく仕事をこなされている方もたくさん居られると思います。 入門できた方にとっては、いい本でしょう。
 そうでなければ(改訂前の版についてでしょうが) 本のオビに40万部突破と書かれるくらいに売れる本にはならないと思います。
 実際、目を通してみると、入門者へうまくアプローチとしているなあ、とか、 やさしく丁寧に教えてはるなあ、と思うところも結構あります。 うまく感性があえば痒いところに手が届く的なはまり方があるかもしれません。レイアウトも見やすいほうでしょう。

 ですが一方、入門しやすそうな個所があるからといってすべてにおいてそうではなく、入門者に細心の気配りをされているとは思えない 勘違いや混乱しかねない書き方もままあり、 場合によっては著者自身がANSI-C を確認されていないようで不味い記述もあります.  実用のプログラムを組むことや大規模や複数人での作業を考えれば 教えないか注意を促すようなことを無頓着に記述されたり選んだりされています。

 以下に、(購入した)ビギナー編について、気になったことを書き出してみます( スーパービギナー編シニア編に関しては、本屋でぱらぱらと見た範囲では大筋は 似たようなものに見えましたが、 細かくはやはり違いそうなので以下の通りでないものもあるかもしれません。また、改訂前の版は人に見せてもらっただけですが多少違いがあるようで当てはまらないものもあります)。

○BASICの影響

 まず本屋で手にとって真っ先に気になったのは、 裏表紙の見開に書かれた2のn乗数の一覧表です。 表表紙や裏表紙の見開きに文字コード表や2,8,10,16進数対応表がついていて、 こういうのはいいなあと思うのですが、2のn乗数の表現として、
 2^1
のように、BASICでの乗数表現と用いており (ひょっとしたら一般的な表現として通用するのかもしれませんが)、 C言語では ^ は排他的論理和という別の演算に用いる記号なので 初心者を混乱させる要因の一つになると思います(改訂前の版には無かったです)。

 本編に入ると、入り口の説明で BASICやFORTRAN,PASCAL といった他言語での事情などがよく引き合いに出されます。 これは一長一短で、全くの初心者を混乱させる要因になるかもしれませんし、 自分で考えて必要な情報を選ぶという行為が有意とすれば 目に見えて不要な情報は捨てやすくて適度なのかもしれないという気もします。 もちろん、あらかじめBASIC等の素養のある方なら気にならないかもしれません。

 書名は覚えていないのですが、林晴比古氏の昔の著作物に、BASICプログラマのためのC言語入門、のような本があり、 新C言語入門は、その流れを持っているようで、 その名残が今でもあるとも言えそうです。
 改訂前の初版が1991年で、当時としてもBASIC経験済みの入門者よりも 全くの入門者のほうが多かったと思いますが、 80年代前半のパソコンブームでBASICを入門された方や(よくは知らないけれど) 企業でのBASICの使われ具合によっては、当時の状況でもBASICからの入門という 需要は結構あったのだろうと想像できます。が、1998年の改訂時のBASIC事情を 思えば言及しないように改訂するほうが正解のような気はするのですが (ひょっとしたら、Visual-BASIC経験者の入門てのが念頭にあるのかも:-)

 他にBASICを引きずっていて、多少まずい、と思われることがらとして、 時間待ちをする方法として、空ループを紹介されています。
 実は、空文の説明や変数の桁あふれの例として用いられているので 主目的として紹介されているわけでないのですが、 時間稼ぎの方法として空ループを用いるのは、現状ではまず実用できない例です。
 現在のCコンパイラ(の最適化処理)では 単純な空ループでは動作として何もしないと見なされマシン語自体が生成されず、 全く時間稼ぎできません。 最適化処理を行わないよう指定すれば生成されるでしょうが、 その場合でもの待ち時間は、動作環境(CPUの種類や速度)やコンパイラに 大きく依存します(同じプログラムでも0.5秒なるか30秒なるかは不明ということ)。

 あと好みの問題と思われるものでしょうが for文の使用例で、
  for (i = 0; i < 10; i++)
よりも
  for (i = 0; i <= 9; i++)

  for (i = 1; i <= 10; i++)
のように <= を用いる表現を多用されていたり、1オリジンだったりします。
 いずれでも間違いというわけでないのですが、 境界条件にまつわるバグは多くできるだけ使用する表現を統一することで 勘違いを減らす工夫をしたほうがいいですし、 その場合、C言語では最初の例のように 0 から初めループ回数と比較できるよう <= でなく < を用いる書き方が一般的と思います。
 Cでは配列の添え字が 0から始まる(0オリジンの)ためでもあるし、この場合、 配列のサイズとして用いた値そのままをループ回数として記述に用いられます。 たとえば

  #define TST_NUM 10
  int tst[TST_NUM];

として

  for (i = 0; i < TST_NUM; i++) {
    tst[i] = i*7+2;
  }

のようにです。<= を使うと TST_NUM - 1 のように式になってしまいます。  式を書くよりも単項ですむならばそのほうが安全ですし、 ラベルでなく定数値であっても(規模がでかくなり確認や修正の手間のことを思うと) 同じ意味を表すならば同じ値を用いたほうが 9と書かずに 10-1のように書いたほうがわかりやすいことは多いですし、 -1をする手間を思えば < のみで済ますのがよいでしょう (逆に i < 10 でなく i <= 9 のような習慣をつけてしまうと 複数箇所に現れる定数値を定数ラベルにするというのが面倒なので 定数ラベルを利用しないで済ます率が高くなるような気もします)。

 あえて入門者に事情を説明するかどうかは別として、 入門書を真似るところから始まる入門者の数を思えば、 なるべく利用価値の高い表現を示してほしいものです。


○名前関係

 名前の文字数制限については、些細なことかもしれませんが、 必ず31文字までしか認識しないというわけでなく、 ANSI-Cなら少なくとも31文字は認識しないと駄目というだけで コンパイラはそれ以上を認識してもよいです。
 もちろん移植性や安全性を思えば 31文字までの名前でプログラムを 組むというのはよい方針で、実際この方針の人は多いと思います。

 名前のつけ方に関しては、 小文字を主体に用いることを薦められているのはいいな、と思います。 が、できれば、マクロ名や定数名(場合によっては型名)に大文字のみの名前を使う、 という昔からの慣例も言及してほしかったところです。

 代わりに、大文字を用いるのは記号定数だけでその他は小文字のみで書くのが 慣例というのは、ちょっとズレているように思います。
 InputFile という名前のつけ方がCの表記慣例にない、と言いきるのは 現在の(たとえばWindows等の)状況からすれば、初心者を混乱させるだけでしょう。

 また _で始まる名前は通常の名前のように例に使っておられ、 標準化後のCではコンパイラやライブラリの作成者が専用に用いる名前用で 一般には _ で始まる名前を使わない決まりになったことには言及されていません (載っている_で始まる名前の例は、 C標準化前では一般的な方法の一つだったのですが)。


○ main

 mainの戻り値について、システムに返す値であることは補足されるのだけど、”システム側は、通常はこの戻ってきた値を捨てるだけだから、任意の戻り値でも何も問題ない”と言いきり int をつける場合のほうを”記述してもよい”のように紹介されてます。
 "main関数については、プロトタイプ宣言が不要"というような記述さえあります。

 結果的にシステム側がエラーコードを無視する状況は多いでしょうが、システム側がいつもエラーを捨てるわけでないですし(makeのように戻り値を活用するものもありますし)、C言語の仕様ではOS下のプログラムのmain()はint値を返えすのが決まりです。

(どうも著者は、コンパイラが mainを特別に扱うと考えられているのでしょうか……そのようなコンパイラはあるのかもしれませんが、人間にとっての決まりやスタートアップルーチンの都合で特殊な存在だけでコンパイラにとっては他と同様の関数でしかないです)。


○ 警告メッセージの扱い

 警告に対するスタンスも問題に思います。
 警告はよくない個所を教えてくれるものだけど反面「エラーはない」と保証しているから初心者はとりあえずこれを無視するのがコツ、みたいなことを書かれています。
 重要なプログラムを開発するようになったら、そのときは警告メッセージがでないように心がけてください、とか、一応フォローはされているのですが……前後からすると仕事でなければ警告を無視していてもよい、と考えておられるようにも見えます。

 しかし、大抵の警告は「エラーはない」ではなく「エラーだけど場合によっては大丈夫」くらいのものも多く、結局、多少苦労しようとなるべく警告のでないソースを書けるよう身につけることが上達への近道だと思います。

 警告を無視して、とにかく動かすというのが有効なのは、せいぜい数日とか数週間でしょうか。 むしろ、警告を無視する習慣をつけてしまうことのほうが問題と思います。

 警告を無視する習慣は、マルチステートメントの多用やgotoの乱用と同種の問題で、一度身に着けてしまうと、わかっていてもなかなか直すのが大変な悪癖だと思います。インデントを付ける習慣、(不要な)gotoを使わない習慣、と同様に、警告を無視しない習慣、は早期に身につけて貰いたいので、"無視するのがコツ"みたいなことを推奨するのは不味いなあ、と思うわけです。


○ 引数を持たない関数について

 void 型の引数指定に関する説明も多少不味いです。
結局は int foo(void) のように引数なしの場合は void を付けることを薦められているので、それはよいのですが、その説明がまずく、int foo() と int foo(void) と同じ意味、と受け取られかねない書き方です。
 そうだとすれば、わざわざ void を付けるメリットはなく付けないほうがいいと思う人のほうが多いでしょう。

 実際には、foo()の場合には過去との互換性のため foo(...) という宣言と同じ扱いで(古いプロトタイプ宣言のなかった時代のソースをそのままコンパイルできるように)、いくつ引数があってもよい(エラーにしない)という意味になり、引数のチェックを放棄することになるわけです。
 Cでは引数が無いことをチェックしたければ voidを書いて明示しなければならない仕様になっています(正直、C言語の仕様が悪い、といいたくなりますが……たちの悪いことに C++では int foo() で int foo(void) と同じ扱いになるのも混乱の元でしょう)

 本ではエラーチェックの違いに多少触れられてはいますが、もっとはっきり書いてほしいところです。


○ マクロの使用例

 #define begin { や #define loop for(;;) のような制御文をマクロで再定義できることを例に出されています。
 そう出来てしまうことを知ることは必要なように思いますが(使うことを推奨されているわけでないですが)、合わせて、それを実用することの問題点にも言及しないと不味いようにも思います。

 制御文をかってに再定義すると、その意味するところを知らない他人には、知らない他言語のソースをみるようなもので、手間が増えるばかりです。というかそのようなものはC言語でない他言語だと認識したほうがよいでしょう。

 ただでさえマクロの使用というのは難しいもので、そのメリットとデメリットを考えて注意深く使う必要があるものですが、文法(とくに制御文)に影響するようなマクロは勘違いを誘発しやすく、作った本人には便利でも、他人には迷惑な存在になることが多いです。


○ scanf, gets

 キーボードからのデータ入力として、scanf や gets が用いられています。
 標準関数を用いて説明する場合、特に序盤はこれらを用いて説明したほうが手っ取りはやいとも思いますし、それはそれでいいとも思います。ただし、これらの関数には致命的な欠点があるということを、またなるべくその回避方法もどこかで入門者に伝える必要はあるでしょう。この本に限らず入門でのscanf使用で懸念されることは、こららのもつ欠点が説明されずに済まされることで、入門者がせっかく覚えた関数のほうを使い続ける可能性が高いから、です。
 scanf(fscanf)の持つ問題としては、まず、改行を空白扱いにしてしまう、ということです。テキストを行単位で処理したい場合にいろいろ不都合がでる仕様です。
 scanfやgetsの致命的な問題としては、入力文字数を指定できないため、入力バッファを溢れてメモリ領域破壊を起こす危険性が、絶えずある、ということです。なので入力文字数の指定できる fgets を用いることになります。
(せっかく前書きのツカミとして gets の問題を引き合いに出しているのにその説明をしないのはもったいない)

 シニア編では、一応 scanf の問題点には触れられていますが、ビギナー編/スーパービギナー編では触れずじまいです。


○ 実用プログラム

 実用プログラムという章があり、トイプログラムレベルの例がいくつか紹介されてるのですが、短くわかりやすいプログラムは例題として大事ですが、実用ということでは、もう少し長くてもより汎用的で、入門者が自分のわかるところからでも手を加えて改造できるような例も示されたほうがありがたいです。
 入門者の手間と得られるノウハウの効率を思えば、あと少しソースが長くても説明する事柄が増えても、そういう例も用意すべきだった、と思います(複数オプションや複数ファイル名指定可能なコマンドラインツールを一例くらい示しておいてほしいところ)。
 章扉の方針もありますしプログラムを複雑にしたくないのはあるでしょうが、その場合、各プログラムの持つ制限は、本文なりソース中コメントなりで、多少補足しておくべきとも思います。
 題材選びの不味さとも言えますが、Cソースの()の数確認ツールはフリーフォーマットでないし対状態もチェックしてないし'('や"("、/*(*/ にも考慮してませんし、プロトタイプ宣言生成ツールもフリーフォーマットに対応していないどころか行末にコメントがついてるだけで見つけられない(その割に、戻り型なしのmain定義を例外処理する)プログラム、です。 実のところCソースを対象とするツールなどは、いろいろな局面を想定し対応しようと思うと途方もなく困難な(難易度の高い)題材なのですが(#if の処理などを考慮しだすととくに)。
 C対象ツールの仕様の問題はいろいろなソースで実演してみれば結構気付けるでしょうが、1行最大255文字で必ず改行コードが読みこめていることを前提に処理している、というようなことは(どういうことか)説明があったほうが入門者にはよいように思います。


○ 巻末の主要関数一覧

 ビギナー編巻末の主要関数一覧は、リファレンスとして機能しません。
 著者が不要と思ったものに関しては戻り値を記述せずにすました簡易版のためで、主目的の紹介と、割り切るしかないです。
 他の人のソースを読んでるときにぶち当たるかもしれない記述 n = printf("%d\n",s);や strcat(strcpy(d,s),e); を調べることができないですし、選択されている関数も、 putchar,getchar や scanf は紹介しているけど、比較的よく使いそうな sprintf などは無いですし、fgets等の戻り値を書かないならばせめて ferror でエラー処理の方法を紹介してするなりはしてほしいところです。


と、まだまだ他にもいろいろ気になることはあるけど……

といったとこでしょうか。
 他の人にとっては重箱の隅つつきや好みの問題に思えるだろうし実際自分でもそう思える部分もあるけど……パラッと見るぶんには、そんなに悪くないんじゃない?、と思ったんですが、こまごまとした間違いや不親切の量も積もれば、だし、読んでいくうちに根本的なところでの方針に首を捻ってしまいます。
 前書きや章扉で語る方針などは悪くないと思える面もあり、入門者の立場に立った優しい言葉のようにも感じますが、結果的に初心者心理をついた甘い言葉に終わり、 また下手の言い訳にしかなっていないように思います。
 林氏の筆力により難しいこともわかりやすく教えてくれる、というのでなく、C言語のやさしい部分だけ選んで上手くややこしいトコを入門者に気付かれないように隠して「簡単でしょ」と囁いてるみたいです。
 まあ、そういう本だと宣言されているみたいですし、 せいぜい入門2,3日〜1,2週間くらいが使命の本で使い込まれるのは不本意なのかもしれませんし、 著者の想定する入門者の目標は、 とにかく十数行〜数十行くらいのプログラムが書けるようになること、で、 それ以上の規模に関するノウハウは後回し (あとあと苦労するような習慣を入門者が身につけるとしても)、 なのかもしれません。

 ですが、いくら前書きで、と書かれていようと、実際の入門書の使われ方からすれば、そうそう使い捨てにならないだろうし、 想定される到達点はどれくらいの読者の希望に添うかは不明です。 またビギナー/スーパービギナーだけでシニア編を購入せずに済ます人も多いと思うので "初心者を混乱させるばかりの細かすぎる知識はあえて説明しません" と いう方針だとしても、多少教えるのが面倒でも知らないとかえって混乱する事柄を 伝えずにすましているのはよくないと思います(注釈が1行程度あるだけでも違うもんです)。


 正直なところ、前書きで示される方針の入門方法は、せめて本のタイトルを上下編に するか、あるいは付録小冊子にするか、基本的には一冊の本の中で第1部、第2部にわけるなりして上手くまとめない限り成立しない方法だと思います。重要度別に色わけするなりして上達度にあわせて読み飛ばしやすいレイアウトにすれば一冊の本でも読み返しで段階を重ねるのは容易だと思いますし。 たとえ致命的な問題がなかったとしても、現状のように半端な状態の版が一人前のふりをしてしまうのは危ないです。


※蛇足
 柴田望洋氏の 「明解C言語入門」 (編のつかない古い方)の"はじめに"を読見返してみると、どうもこの本に対する返答のような気もします(ちなみに"はじめに"だけみると、なんだか厳しそうに思われてしまうかもしれないけれど、実際にはわやかりやすくあとあと困らないよう配慮された入門者にやさしい本です)

 あと、藤原博文氏のウェブページには、林晴比古氏の他の本に関する 書評 があります。

1999-07-07

[back]