Q:プログラムのエラーにはコンパイルエラーと実行時エラーがあると聞きました。実行時エラーとはどんなエラーなのですか。
A:プログラミングの際に文法的な誤りがなければ、ビルドによって実行可能なプログラムができます。ただし、このプログラムは実行可能というだけで、実行後、適切な結果が得られるかどうかは全く保障されていません。プログラムを実行したときに発生するエラーを総称して実行時エラーと呼んでいます(図1)。
実行時エラーが発生すると、システムが停止したり、応答しなくなってしまうことも多いので、注意が必要です。システムはそのまま動き続けていて、特定のステップだけ実行を飛ばしたり計算値が不正になったりするエラーもあり、発見しにくい分だけ難しいエラーともいえます。
Q:実行時エラーが起こったかどうか、どうやって知るのですか。
A:ビルド時のエラー(コンパイルエラー、リンクエラー)の場合、ビルドが中断し、エラーメッセージが表示されます。エラー発生が分かりやすく、どんなエラーが発生したかを知ることもできます。ところが、実行時エラーの場合は症状もさまざまで、エラーメッセージが出ないことが多く、とても対処が難しいものです。
まず、実行時エラーが発生したときにメッセージが出るのはどんな場合かを説明しておきましょう。
コンピュータでプログラムを実行するとき、大別して2つの方法があります。1つは、OSのように上位で動いている管理プログラムに依頼して、プログラムを実行してもらう方法です。パソコンでもスマートフォンでも、ユーザーがアプリケーションを選択して実行するようなシステムでは、基本的にこの方法を採っています。インタプリタ言語やスクリプト言語、Excelのマクロ言語(VBA)、RDB(リレーショナルデータベース)管理システムのSQL言語などは、OSの下で動いている実行環境(言語プログラム)がプログラムの実行を管理しています。
この場合は、実行時エラーが発生したときに、OSや実行環境が必要な処置を行ってシステムの動作を維持するとともに、エラーメッセージを出します(図2)。一般に実行時エラーと呼ばれているものです。
ただし、どんなエラーでも検出できるわけではありません。マイクロプロセッサのエラー検出機能や、OSのエラー検出機能、実行環境のエラー検出機能で検出できたものだけです。
インタプリタなどの実行環境では、ビルド時と同様に、エラー修正を目的としたメッセージを出します。OSは「エラーが発生したのでプログラムを終了しました」という報告を出すだけなので、エラー修正には役立たないことが多いでしょう。
一方、マイコンのプログラム開発では、一般にはOSも実行環境もなく、マイコンのフラッシュメモリに書き込んだプログラムがそのまま実行されます。この場合は、実行時エラーが発生しても、システムが暴走してしまうだけで、誰もエラーメッセージを出してくれません(図3)。ほとんどのマイコンは、不正な処理の後、割り込みを発生する機能をもっていません。
システムが暴走すると、大抵の場合、プログラムが応答しなくなり、止まってしまったように見えます。そのため、実行時エラーとして認識されにくいのです。暴走の主要な原因の1つと言えます。
実行プログラムをいきなりマイコンボードに書き込んで実行すると、実行時エラーが発生しても、その原因や解決方法は全く分かりません。そこで、通常はデバッグ作業を行ってエラーを検出し、原因を特定します。
デバッグ時には、デバッグ用のソフトウェアツール(これをデバッガと呼ぶ)の管理下でプログラムを実行します。デバッガはプログラムの正常な実行を制御し、実行状態を記録するツールです。デバッガを使うと、プログラムをステップ単位(1行単位)で実行したり、任意の場所(ブレークポイント)で停止して実行結果を確認できます。テキサス・インスツルメンツ(TI)のCCS(Code Composer Studio)のような高機能の統合開発環境では、デバッガもとても使いやすく、豊富な機能を持っています。
ただし、多くのデバッガは、実行時エラーを検出したりメッセージを出したりする機能を備えていません。システムが暴走する前の正常な状態と、暴走が始まるきっかけの命令を調べて、エラーの発生原因を自分で見つける必要があります。
デバッグ時に全ての条件で動作をテストできるわけでもありません。デバッグでエラーが出なくなっても、本番の実行を続けているうちに、特定の条件がそろったときにだけまれに発生するエラーというのもありますから、注意が必要です。
Q:実行時エラーにはどんな種類があるのですか。
A:代表的な実行時エラーを幾つか紹介しましょう。
計算をしているとき、例えば「12÷0」のようにゼロで割り算をすると、値が未定義(無限大)になってしまい、その後の計算ができなくなってしまいます。このときに発生するのがゼロ除算エラーです。除数が0の場合だけでなく、除数がとても小さくて割り算の結果が大きくなりすぎ、桁あふれ(演算オーバフロー)してしまうような場合もあります。
もちろん、0で割るプログラムをわざと書くようなことはしないでしょう。大抵の場合、変数xで割るプログラムがあり、その前の計算や代入の結果、xの値がゼロになってしまったときに発生します。大規模、複雑なプログラムでは、プログラム上では割り算をしているつもりはなくても、関数の中などで割り算をしている場合があります。
ゼロ除算エラーで割り込みを発生しないマイコンが多いため、このエラーを防ぐために、プログラム側であらかじめ除数を検査してゼロでないかを確認する方法も用いられています。
プログラムはシステムのメモリ(RAM)領域を確保して、その範囲にデータを書き込んだり、読み出したりします。あらかじめ確保した領域以外に誤ってデータを書き込むと、そこに保存された大切なデータを壊してしまう恐れがあります。これをバッファオーバフローエラーと呼びます。読み出す場合にデータを壊すことはありませんが、読み込んだ値は想定したものとは異なるため、正常動作とはいえません。
C言語の場合、例えば長さ20の配列を定義すると、それに必要な分だけのメモリが確保されます。プログラムの中で、変数xを用いて配列を参照するように命令を記述した場合、xの値が代入や計算によって21や22になっても文法的にはエラーになりません。しかし、実行するとバッファオーバフローになります。さらに、C言語ではポインタ変数でメモリをアクセスする場合も多く、ポインタの値が適切な範囲を超えるとバッファオーバフローになります。
あらかじめxの値が適正な範囲かどうか調べてからアクセスするという方法が、バッファオーバフローの防止に有効です。
これは一般的な実行時エラーとは少し違います。forループの終了条件やwhileループの継続条件が適切に設定されていないために、ループから脱出できなくなってしまうものです。
OSなどの管理下で動作するプログラムは、基本的に仕事が終ったら終了してOSに戻ります。しかし、単独で動作する組み込み機器のプログラムは、機器の電源を切らない限り無限ループを意図的に続け、スイッチなどの入力待ちでループから脱出するのが普通です。そのため、次のような無限ループの構文をよく使います。
while(1) { ……処理内容…… }
無限ループそのものが悪いというのではなくて、意図せずに無限ループになってしまうのが問題です。
文法上の制約が少なくプログラミングの自由度が高いというのがC言語のそもそものコンセプトであり、コンパイラでは基本的に実行時エラーのチェックを行っていません。実行結果はプログラマの責任であり、注意してデバッグを行うことが必要です。
【関連リンク】
※CapTIvate、MSP430およびMSP432はTexas Instruments Incorporatedの商標です。その他すべての商標および登録商標はそれぞれの所有者に帰属します。
Copyright © ITmedia, Inc. All Rights Reserved.
提供:日本テキサス・インスツルメンツ株式会社
アイティメディア営業企画/制作:EDN Japan 編集部/掲載内容有効期限:2016年3月31日
宮崎 仁のQ&Aでよく分かるマイコン基礎の基礎:
第24回 割り込みっていろいろあるのですか?どんな種類があるか教えてください。
今回は、外部割り込み、内部割り込み、ソフトウェア割り込みについて詳しく解説しています
●全文を読む
電源IC選択のヒント集
電源IC 使用時の注意点をわかりやすく説明しているほか、使用時に発生する可能性のあるさまざまなトラブルとその対処法についても紹介しています。ぜひご利用ください。ダウンロードには myTI アカウントが必要です。
●Part 1をダウンロード
●Part 2をダウンロード
アナログ回路設計式一覧ポケット・ガイド
日本語版 PDF
英語版で高い評価を受けてきたポケット・ガイドの日本語版が完成しました。基板レベルやシステム・レベルの回路設計でよく使われるアナログ設計式を紹介しています。ダウンロードには myTI アカウントが必要です。
●ダウンロード