混沌の廃墟にて -77-

激辛テクノロジ批評(2)

1989-08-24 (最終更新: 1996-02-07)

[↑一覧] [新着] [ホームページ]


ところで、この記事では「…君」という言い方になっているが、どうもこれは 好きではない。「…さん」の方がよいと思うのだが、これは個人的な趣味の問題 だから、この程度でやめておこう。

まず、一言強調しておきたい。どんなポリシーがあろうと、バグはバグである。 致命的なものはなおさら、たとえアルゴリズムが何だろうと、コードが節約でき ようと、そんなものを生産性の議論の場に持ってくるのがおかしい。破壊的なプ ログラムはユーザーをいらいらさせるだけでなく、会社を倒産に追い込む可能性 だってあるのである。

A君の回答で、まず一見して驚いたのは、これである。

>       b[100] = '¥0';
char b[100]; と宣言した配列は要素が 0 から 99 までの 100 個であることは、 それこそ初歩の初歩。b[100] にこんなもの代入したら何が起きるかわからないし、 実際何も起きないように見えるかもしれないが、ちゃんとメモリ管理しているOS だと、異常アドレスへのアクセスのようなエラーが発生してプログラムが停止す るかもしれない。こういうのは、私は致命的なバグと呼んでいる。

ちなみに、strcpy() が for のループ内部にある理由がいまいちわからないが、 これは外に出すべきだろう。上のバグで評価が0点になってしまったので、減点 のしようがなかった。

そして、Myers 氏が、「ほかよりは間違いなくベター」と評しているD君の解 答だが、確かに、これは「文字列を逆さまにしている」が…、普通、文字列を逆 さまにするとは、次のようなことを言うのではないか。つまり

    S T R I N G (終わり)
を、
    G N I R T S (終わり)
にする処理のことだ。説明のために、文字列終了コードを(終わり)と書き、 文字と文字の間は、見やすいように空白を入れたが、そこは説明と思って雑に理 解してほしい。で、D君のプログラムだとどうなるかと言うと、
    (終わり) G N I R T S
のように逆さまにして、しかも丁寧なことに(終わり)を指すポインタを返す のである。つまり、この関数は、常に(終わり)が先頭になるような文字列への ポインタを、呼び出し側に返してしまうのだ。こんな事でよいのか?

早い話が、

>   down = a + strlen(a);
は誤りで、
>   down = a + strlen(a) - 1;
とするべきである。これならベターだろう。(*)

さて、D君の解答は、確かにA君の解答のような危険性はないかもしれないが、 常に誤った結果となる事実は重大である(常にというのは言い過ぎで、実際は文 字列の長さが 0 なら正しい結果になるのだが)。まだA君の解答なら、「もしか したら何かの間違いで動くかもしれない」というだけ良いかもしれない…という 事はA君の解答が悪すぎるので流石にないのだが、全く使い物にならない関数を 納品しようとした責任は別にして、私が責めたいのは「テストをどうしてしなか ったのだ」という所にある。(*)

こんなバグは、簡単なテストでアッという間に発見できるのである。事実、私 はA君およびD君の解答のプログラムのバグは、一瞥しただけで発見しているの である。テストするまでもなく、見ただけでわかる人がいるようなバグをテスト もせず放置しておくという態度が許せないのだ。そのようなプログラマーがいる から、真面目なプログラマーの社会的地位がなかなか向上しないのだ。:-)

そこで、D君はテストを怠った上に使えない関数を作ったという意味で、0点 という採点にした。

    *
さて、B君はなかなかいいセンスだと思う。わざわざポインタでなく配列を使 うあたり、最近のコンパイラが賢いので下手にポインタを使うよりは配列のまま の方がパフォーマンスが上がる可能性もあるという事実を考慮すると、おおいに 弁解の余地もある。しかも、テストする姿勢がとにかくよい。私が 10 点入れて あげたのも、テストにかなり時間をかけるという態度を評価したからだ。

で、本当はもっと点をあげたかったのだが…ここで恥を暴露すると、私はA君 とD君のプログラムは確かに一瞥して致命的な障害のあることを発見したが、B 君のプログラムのバグは、しばらく考えてみるまで気付かなかったのである。

つまり、B君の解答は、長さが偶数の文字列を引数に与えると、真ん中の2文 字だけは入れかわらないのだ。これは、ループの所の次の式が原因。

>       j <= len / 2
これは、私はあっさり j < len / 2 と直せばよい(実際それでよいのだが)と 安易に考えて両方トレースしてみたのだが、そこで初めて <= だと真ん中が入れ かわらないことに気付いたのである。原理を解説するのも何だが、蛇足というこ とで念のため。
        AB
このような文字列を引数に与えると、長さが 2 だから、for のループは
    for (j = 0; j <= 1; j++, i--)
となって、
        j が 0      i が 1
        j が 1      i が 0
の2回、ループの内部を実行することになる。これでは、最初に折角入れかえ た文字が、次の回で元に戻ってしまうので、結局戻す文字列も AB のままであ る。Myers 氏の評には、「B君のプログラムだと、1文字だけの文字列でもその 1文字を自分に上書きする。バグとまではいえないが、きれいでもない」とある が、私なら、これはバグだと断言する。

それにしても、<= を < に直せばOKなのに、生産性0とは厳しい評価だと言 われそうだから、理由を説明しよう。B君は、A君やD君と違って、徹底的に テストしたのだ。私がチーフ・プログラマーなら、徹底的にテストした上でこ のようなバグを残したままにするようなプログラマーは即座にクビにする。テス トしていなければ、もう少し高い点数をあげたかったのだが、しょうがない。

    *
さて、ここで妙だと思ったのは、Myers 氏ともあろうものが、こんな単純なミ スをぼろぼろするものであろうか、ということ。生産性向上に対する批評、考察 は非常に高く評価したいにもかかわらず、生産性を考えるための例題と解答例が これでは、説得力がまるでない。誤植だろうか? D君の解答で、-1 がたまたま 誤植でなくなったりするだろうか、それに、b[99] を b[100] にしてしまうよう な誤植があり得るだろうか。B君の解答は、<= が < になっていれば、Myers 氏 自身が「きれいでもない」と評した現象が消滅してしまうので、少なくともこれ だけは誤植でありえない。

そこで思うのだが、これは我々プログラマーに対する Myers 氏からの挑戦なの ではなかろうか。わざとバグをそれぞれ含ませておき、それに気付かずに「うん うん、成程ねぇ」と納得しているプログラマーの姿を想像して、ニヤニヤしてい るのかもしれない。だとすれば、いかに日本のソフトウェア技術がアメリカより 遅れること数百年と言えども、我々プログラマーはそう捨てたものでもない。少 なくとも、私はワナをよけて通ることができたようだから。

つまり、やっぱり何をしてよいかわからない仕様が与えられた時には、昼寝を するに限るのである。

    参考文献
        CQ出版社、インターフェース、1989 9 月号、pp.284-286
        辛口テクノロジ批評、ソフトウェア製造現場の教育性を高めよう
        Robert T. Myers
(本文中の引用はすべてこの記事からです。なお、プログラムの空白・インデ ント等は画面の余裕などを考慮して、意味が変わらない範囲で若干修正しました。 また、句読点は「、。」に変更させていただきました)

補足

>   down = a + strlen(a) - 1;
は、a[-1]が正しいオブジェクトでない場合に、strlen(a)が0のときup < downと いう比較がC言語の仕様上未定義であるという問題を引き起こす。

D君はテストをしなかったと本文に書いたが、元の記事では1時間テストしたこと になっている。1時間テストしてこの結果に気付かないというのはテストしていな いと同義であると解釈した。あるいは、テストして正しく動作しないことを確認 し、そのまま納品したのか?


    COMPUTING AT CHAOS RUINS -77-
    1989-08-24, NIFTY-Serve FPROG mes(4)-003
    FPROG SYSOP / SDI00344   フィンローダ
    (C) Phinloda 1989, 1996