ショートカット
ファシリテーター × あり方
コーディングの向こう側
Hello, ANOTHER world!
オブジェクト指向のはなし
プログラミングのはなし
C言語実力診断クイズ
eSkillBooks
プログラミングのはなし

バグと実行時エラー

次のコードは間違っています。どこが間違っているのかわかるでしょうか?

SAMPLE
CODE
int *pi = (int *)malloc( sizeof(int) );
*pi = 100;

おそらく、パソコンでのプログラミングに慣れた人ならすぐにピンと来たのではないでしょうか。答えは「malloc() のエラーチェックをしていない」です。malloc() は必ず成功するわけではなく、メモリ確保に失敗すると 0(NULL) を返します。メモリは限られたシステムリソースなので、失敗する場合があって当然ですね。このように実行時の状態によって発生するかもしれないエラーのことを「実行時エラー」といいます

しかし仮想メモリが働いている環境では malloc() によるエラーは少なくなります。とくに仮想メモリがあって、なおかつ実際の実装メモリサイズが大きい場合はほとんどエラーを返さなくなります。重要なシステムを UNIX で構築している場合がこれにあたります。そのため、「UNIX では malloc() のエラーチェックは不要だ」という前提でコーディングすることが多くなっているようです。これはとんでもないことです。

実行時エラーは必ずチェックすべきです。起こる可能性があるエラーを見逃しているとしたら、実際にそのエラーが起こったときにはバグとして表面化してしまいます。運が良ければオペレータが異常に気付いてくれるでしょうが、知らないうちにデータが壊れてしまうかもしれません。重要なシステムでは、最悪の場合、人命に関わる事故を招きます。

では、上のコードを修正して見ましょう。次のようなエラーチェックは正しいでしょうか?

SAMPLE
CODE
int *pi = (int *)malloc( sizeof(int) );
ASSERT( pi != 0 );
*pi = 100;

前のトピックで説明した ASSERT を使っています。結論から言うと、実行時エラーのチェックに ASSERT は使えません。実行時エラーはリリース後にチェックされなければ意味がありません。ASSERT はリリース用コンパイル時に無視されてしまうので、この目的には使えないのです。

そこで、次のようにします。

SAMPLE
CODE
int *pi = (int *)malloc( sizeof(int) );
if ( pi != 0 ) {
    // 通常処理
    *pi = 100;
}
else {
    // エラー処理
    ;
}

コーディング量も増えるし、すこしめんどくさいですね。それでもやるしかありません。実行時エラーは必ずチェックしなければならないのです。実際、まともなプログラムは半分以上がこのようなエラー処理なのです。覚悟をきめて下さい!

では、さいごに実行時エラーチェックの例をいくつか挙げておきます。

  • malloc() のエラーチェック
  • ファイルのオープン・出力エラーチェック
  • 通信エラーチェック
  • データの破損チェック
  • オペレータの操作エラーのチェック

(「プログラミングのはなし」は1998年1月から1999年1月にかけて作成されたコンテンツです。)