新・闘わないプログラマ No.78

C言語入門書


今年も新入社員研修の季節がやってきました。もういい加減トシなので、研修の講師は引退したいところなのですが、なぜか今年もやる羽目になってしまいました。今年の私の担当はC言語だけなので、去年に比べれば楽になりましたけど。
ところが一つだけ問題がありまして、それは何かと言いますと、研修で使うテキストを強制的に指定されそうになっていることなんですね。なんか、数年前に買ったC言語のテキスト本がいっぱい倉庫に眠っているのが見つかったそうで、せっかくあるんだからそれを使え、とのこと。そりゃあまあ、出来がよければ使っていもいいのですけど、でもやっぱり「プログラミング言語C 第2版」(B.W.カーニハン/D.M.リッチー著、石田晴久訳、共立出版1989年発行、以下「K&R2」)が一番だと思うんですけどね。

とりあえず、ざっと目を通してみようと、件の本をぱらぱらとめくってみたのですが……あの、ほんっっっとうにこんな本使うの、という出来でした。ちなみに、「入門ソフトウェアシリーズ(1) C言語」(河西朝雄著、ナツメ社1995年発行、以下「河西本」と呼びます)という本です。
あまりにひどい出来なので、今回はこれをネタにしようなどと考えています ←いや、本当は、どれだけひどい本かレポートを書いて、これをテキストにするのを止めるように働きかけるために調べたんですけどね。
というわけなんで、今回はプログラミング言語、特にC言語とか知らない人にはさっぱりわからない内容かも知れません、ごめんなさい。

この河西本、「はしがき」のところにこんなことが書かれています(2ページ)。

ANSI(米国標準規格協会)は、Cの言語仕様および標準関数の仕様を1988年に定めました。この規格に従ったCをANSI Cと呼びます。本書は、ANSI Cに基いて説明してあります。

と、ANSI C準拠であることを高らかに宣言しているんですけどね、その実態はというと(22〜23ページ)……

ANSI Cの仕様に従った書き方で、1.2(14ページ)のプログラムを書くと次のようになります。
(中略)
つまり

    #include <stdio.h>
    void main(void)
    {
        [プログラム本体]
    }

というのがANSI Cの書式に従った書き方で、関数の型、引数の型を示すプロトタイプ宣言を意識したものになっています。
voidという特殊な型やプロトタイプ宣言については第6章で詳しく説明します。本書ではANSI Cの書式に従った書き方を採用しています。
なお、UNIXなどではmain関数がOSに対し戻り値を返すことを要求しているので、

    #include <stdio.h>
    int main(void)
    {
         ・
         ・
        return 0;
    }

と書きます。ただしUNIXのシステムによってはvoidをサポートしていないものもあるので、その場合は単にmain()とします。

いやあ、おいしい。だって、ここまでまるっきり嘘を書いていると、こちらも突っ込み甲斐があるというものです :-p
ANSI Cでは、main関数の型はintです、これは誰がなんと言うが、そう規格で決まっているんだから、そうなんです。だから「本書ではANSI Cの書式に従った書き方を採用して」いるのだったら「void main」は無いんじゃない? 別に「UNIXなどではmain関数がOSに対し戻り値を返すことを要求している」かどうかは、この際無関係。「voidをサポートしていないもの」って、単にANSI C準拠じゃない、ってだけでしょ? それに、関数の定義のときに、関数の型を省略した場合はintと見なされる、というのもどうやら知らないような書き方だし。
さらに同じ23ページで、

関数のプロトタイプに関する厳しいチェックを行わないなら、stdio.hを取り込まなくても構いませんが、getchar/putcharなどのマクロ、EOFなどの記号定数を使用する場合は必ず取り込まなければなりません。

だからさあ、嘘だってば。関数の型がint以外の場合には、その関数を使うところより上で関数の宣言が必ず必要でしょ? printfなんかだったら型がintだから無くても動くけど(でも、最近のコンパイラなら大概warningが出ると思う)
さらにさらに、61ページで、有名な「getcharの型が何故intか?」という問題に関連して、

ここで変数cの型をchar型でなく、int型にしているのには意味があります。K&Rの仕様のCではchar型は符号なし数として扱われ0〜255の値をとりますから、EOFの値である-1を判別できません。そこでchar型でなくint型として宣言しているのです。
ANSI Cのchar型は符号付きなので-1を認識できますから、int c;とせずchar c;としても良いわけですが、K&Rの仕様のCとの互換性を持たせるため、int c;としておくのが良いでしょう。

どうしてこういうおいしい表現がいっぱい出てくるんだろう。「ANSI Cのchar型は符号付き」なんていう怪しい知識、いったいどこから仕入れてきたの??
K&R2の「付録A 参照マニュアル」(238ページ)にこう書いてあるんだけど……

文字以外の値もchar変数に格納してよいが、値として許される範囲、とくにその値が符号付きかどうかは、処理系に依存する。

ね、疑問の余地無しでしょ?
それに、「ANSI Cのchar型は符号付きなので-1を認識できますから、int c;とせずchar c;としても良いわけですが」っていいわけ無いじゃない。仮にある処理系でcharが符号付きだったとしても、全く駄目です。これじゃあ、ファイルを最後まで読まないうちにEOFになっちゃう可能性があります。全然だめ、0点です。
この人、結局なんでgetcharがint型なのかさっぱり分かっていないようですね。だから「K&Rの仕様のCとの互換性を持たせるため、int c;としておくのが良い」なんて苦し紛れに言っているだけような気がします。なんで互換性を保たなければいけないのでしょうか??
他にもこんなのも見つかりました(70ページ)。

Cでは、配列の宣言時に{}の中にデータを書いておけば、自動的に、データを初期化してくれるという便利な機能があります。そのときは配列を宣言するときにstaticという指定をしなければなりません。

あれえ、この本、ANSI C準拠じゃなかったっけ? ANSI Cなら、staticじゃ無くたって初期化してくれるんだけど。そりゃあまあ、staticじゃない配列の初期化をどんな場合でも薦めるか、という問題はありますけど、それはまた別な話。
なんか、この調子だといくらでも出てくるなあ。最後にもう一つだけ(「関数の定義と引数」101ページ)

対応する実引数と仮引数は同じ名前を付ける必要はありませんが型は必ず一致していなければなりません。

この人、プロトタイプ宣言について解っているのでしょうか? そりゃまあ、私だって実引数と仮引数の型については一致していることが望ましい場合が多いとは思いますよ。だけどねえ、「必ず一致していなければなりません」はないでしょ。

まだ、おいしい「ポインタ」の説明をしているところ(沢山間違いがあるだろうと推測できる :-p)を見ていないのですが、それでもまだまだ間違いやら、変な表現やらがあるのですから、ちょっとカンベンしてほしい、などと思ってしまうわけです。
こういう本の著者って「間違いを書いてしまって恥ずかしい」とか、そういう気持ちって無いんでしょうか?? いや、「じゃあ、お前が入門書書いて見ろよ」などと言われたら、ちょっと自信が無かったりしますけど、でも少なくともこんな初歩的な誤り(それも著者の勘違いからくる誤り)だけは犯さない自信だけはあります……って、そんなのはあたりまえのことですねよね、プロとしては。

とはいうものの、世の中に氾濫しているひどいコード(ソースプログラム)を見るにつけ、そして、そういうひどいコードを書いた方が、プログラムの行数が増えて、デバッグの時間が増えて、トラブルが頻発して、それだけ仕事が多くなって、みんなが幸せ、という事実もあるわけで、それはそれでいい、という話もあったりするのも事実だったりするわけですけどね。
でも、私は断固あのようなクズ本をテキストにすることだけは拒否するつもりです。

なんか、今回は揚げ足取りに終始したような気もします。
「揚げ足取りばかりしてないで、どんな入門書がいいのか、挙げてみろよ」と言われるとつらいところではあります、入門書ってほとんど見たことがないもので。でも、「悪い入門書」のチェックポイントくらいは挙げられるかな、と思いますので、これで許して下さい。

ううむ、あと何があったかな。そうだ!


1999.6.28追記
このページの内容に関連して、(ぱ)さんという方からのメールを掲載いたしました。

[前へ] [次へ]

[Home] [戻る]


mailto:lepton@amy.hi-ho.ne.jp