とらりもんHOME  Index  Search  Changes  Login

C言語入門8. デバッグのやりかた

バグとは

 プログラミングとは難しいもので, ちょっとしたプログラムでも, 組んですぐに完璧に動くのは稀である。その主な理由は,

  • われわれ人間は間違いをおかす動物だから。
  • C言語は特に間違いに対して容赦ない言語だから。

である。実際, 我々が組むC言語プログラムは, たいてい, 間違いのためにうまくコンパイルできなかったり, たとえコンパイルはできても, 望んだようには動かなかったりするものである。

 そのような間違いのことを, コンピュータの業界ではバグ(bug)と呼ぶ。「バグ」という語の原義は「虫」である。機械の中に, 我々の邪魔をする害虫がひそんでいる, というイメージである(その虫を作り出したのは我々自身なのだが)。そしてその虫を退治すること, つまりプログラムの中の間違いを修正する作業のことを, コンピュータの業界ではデバッグ(debug)と呼ぶ。

 バグの原因は様々だが, おおまかに言って以下のようなものがある:

  • コマンドなどのスペルミス。例:printfをprontfと書いてしまう。
  • まぎらわしいアルファベット記号の打ち間違い。
  • 例えばl(小文字のエル)と1(数字のイチ)とI(大文字のアイ)の間違いや, 0(数字の零)とO(大文字のオー)の間違い。

英語が不得意な人(特にスペリングが怪しい人)は, この手のバグをよく作る。悔しいがコンピュータの言語のほとんどは英語をベースにしている。英語の基礎学力はこういうところにも効いてくるのだ。

  • 必要な手続きの欠落。例:宣言せずに変数を使う。for文の"{"を閉じるための"}"を書き忘れる。

このあたりは, 単に文法に関する間違いなので, コンパイルのときにエラーメッセージで指摘されるから, 直すのも簡単である。しかし, 文法が正しくても,

  • 許されない処理。例:配列変数xを, int x[10];と宣言したのにx[10]にアクセスする(x[0]からx[9]までしか許されない)。
  • 不適切な変数型。例:小数計算させたいのに整数変数で行う。
  • 根本的に処理の考え方(アルゴリズム)が間違っている。
  • 必要なライブラリを読み込んでいない。(#include <math.h>や#include <stdlib.h>を宣言していない等)

などに起因するバグもある。これらは, コンパイルの時点で見つからないことが多く, 実際に実行してはじめて, "segmentation fault error"というエラーメッセージを受けたり, あるいは間違った結果や, いつまでたっても終わらぬ処理(無限ループという)になってはじめてバグに気づく。

デバッグのテクニック

エラーメッセージをよく読み, 理解する。

 エラーメッセージには, エラー(バグ)に関する情報が詰まっている。エラーメッセージをよく読み, その意味を理解し, 何が原因なのかをよく考えることが重要である。 viの行番号指定を活用する。

 エラーメッセージには, エラーが発生位置の行番号が表示されることが多い。ソースコードのその部分を修正するには, viのコマンドモードで,

:5

のようにコロンに続けて行番号(この例では5行め)を入れてENTERを押せば, その行へすぐにジャンプできる。

manを活用する。

 C言語の関数の使いかたは, 以下のように, UNIXのコマンドと同様に, manコマンドで調べることができる。

$ man scanf 

怪しい部分の前でexitするか, 怪しい部分をコメントアウトして, 怪しい箇所を絞り込む。

 segmentation faultなどのエラーは問題箇所がすぐにはわからない。このような場合, あやしい箇所の前にexit(0);という関数を入れて実行してみよう。この関数は, その箇所でプログラムを強制的に終了する。したがって, exit(0);を入れたらエラーが出ずに終了する, という場合は, 問題箇所はexit(0);よりもあとの行に存在することになる。これを利用して, すこしづつexit(0);をうしろにずらしながら試してみよう。あるときエラーが再発するだろう。その部分に問題箇所が潜んでいる可能性が高い。

 また, あやしい箇所を, //などでコメントアウトするのも有用である。コメントアウトすることによってエラーが出なくなったら, その箇所がやはりあやしい。 変数の値を表示してみる。

 エラーは出ないのだが結果はおかしい, というような場合は, 内部の処理が思うように行っていない。その場合は, いくつかの変数に着目して, その変数の内容を, 処理の途中で, printfなどで画面に表示してチェックしてみよう。

入力データや設定を単純化して、原因を切り分ける。

 結果が望んだようにならない場合は、できる限り単純な入力・単純な設定にして試験するとよい。単純な入力・単純な設定なら、本来どういう結果になるべきかが容易にわかるため、実際の結果と正しい(本来あるべき)結果を比べることで不具合の発生原因を推測しやすい。

エラーメッセージをネットで検索する。

 見慣れないエラーメッセージが出てきて, しかもその意味がわからなければ, そのメッセージをGoogleなどに放りこんで検索をかけてみよう。どのような場合にそのようなエラーが出るのかという情報が得られるかもしれないし, 君と全く同じバグに遭遇して解決した人の経験が出ているかもしれない。

「良いソースコード」を書く。

 ソースコードは, レポートや作文と同様に, 「知的作品」であり, 書き方に巧拙が存在する。うまく書かれたソースコードは読みやすく, 下手なソースコードは読みにくい。読みやすいソースコードは, シンプルで明瞭でわかりやすいので, バグが発生しにくいし, デバッグもやりやすい。

 たとえ自分ひとりが使うプログラムでも, 客観的に他人の目でみたときにわかりやすくなっているかどうかを意識して書くことが, 良いソースコードをかくための第一歩である。それに, 君自身がどうしてもデバグできないときに, 友人や先生に助けを求める場合, 君のソースコードが汚ければ, あまり助けてもらえないかもしれない。

 良いソースコードを書くためのポイントは, 以下のとおりである:

  • シンプルに書く。
  • わかりやすいコメントをつける。
  • 記法に一貫性のあるルールを確立し, それを守る。
  • バージョン管理する。
  • デフォルトに頼らず, 明示的な定義を心がける。
  • マジックナンバーを埋め込むな(参考1, 参考2)。
  • 他人の良いソースコードから学ぶ。

 実は, 良いソースコードを書く技術は, 良い論文や良いレポートを書く技術や, 良いプレゼンテーションをする技術と, かなりの部分, 共通するところがある。自分が表現したいことを深く考えて把握し, 思い切ってシンプルにする, という頭の働きが, いちばん大事なのである。

学生さんがよくやらかす, バク以前のミス

 以下のような単純ミスは、バグではない。なので、いくらソースコードを眺めて悩んでも解決できないため、かえって学生さんを苦しめることが多い。

  • ソースコードを変更修正後, 保存しないでコンパイルしてしまう(変更修正が反映されないので、いつまでたってもうまくいかない)。
  • コンパイル後の実行ファイルとは違う実行ファイルを実行してしまう。
  • 誤った結果のファイルに追記モードで出力するため、いつまでもファイルの先頭に誤った結果が残ってしまう。
  • コンパイル時に必要なオプション(特にライブラリへのリンク指示)を忘れる。例えばmath.hを使っているのに, gccで-lmオプションを付け忘れる、とか。

<C言語入門に戻る>

Last modified:2013/03/10 20:11:09
Keyword(s):
References:[C言語入門]