デバッグの仕方事始め:ログとか出力してみたり

もっとも概念的にわかりやすいのは printf() デバッグ と言われる、必要なところに必要そうな情報を埋め込むことで、プログラムの挙動を観察する手法です( ´ ▽ ` )ノ

それだけで何もかも上手く行く様な便利な方法では無いのですが、ログファイルに書き出す場合などの観点を養うためにも、覚えておく必要のある技術ではあります。

前回のコードに少し色々と追加してみました( ´ ▽ ` )ノ

#include <stdio.h>
#include <stdlib.h>

void
anyAction (int aNumber)
{
    printf ("anyAction: %d\n", aNumber);
    return ;
}

void
sample (int aNumber, int aModulus)
{
    int surplus = aNumber % aModulus;
    if (surplus = 0) {
         anyAction (aNumber);
    }
    return ;
}

int
main (int argc, char *argv[])
{
    int i;
    for (i=1; i<argc; ++i) {
        int num = atoi (argv[i]);
        sample (num, 3);
    }
    return 0;
}

これで実際に動かしてみることが出来るので…

 

とりあえずmakeして実行してみます( ´ ▽ ` )ノ

$ cc sample.c -o sample
$ ./sample 1 2 3
$

あれ、表示されない(゜¬゜)

 

このままでは致し方も無いので、とりあえずprintf()を埋め込んで確認してみましょう。

こうしてprintf()を埋め込むから「printf()デバッグ」と言われる訳なのですがw

#include <stdio.h>
#include <stdlib.h>

void
anyAction (int aNumber)
{
    printf ("DBG %s:%d anyAction(%d)\n", __FILE__, __LINE__, aNumber);
    printf ("anyAction: %d\n", aNumber);
    return ;
}

void
sample (int aNumber, int aModulus)
{
    int surplus = aNumber % aModulus;
    printf ("DBG %s:%d sample(%d, %d)\n", __FILE__, __LINE__, aNumber, aModulus);
    printf ("DBG %s:%d surplus = %d\n", __FILE__, __LINE__, surplus
);
    if (surplus = 0) {
        anyAction (aNumber);
    }
    return ;
}

int
main (int argc, char *argv[])
{
    int i;
    for (i=1; i<argc; ++i) {
        int num = atoi (argv[i]);
        printf ("DBG %s:%d argv[%d] = %s, num = %d\n", __FILE__, __LINE__, i, argv[i], num);
        sample (num, 3);
    }
    return 0;
}

そして実行してみる…

$ cc sample.c -o sample
$ ./sample  3
DBG sample.c:31 argv[1] = 3, num = 3
DBG sample.c:16 sample(3, 3)
DBG sample.c:17 surplus = 0
$

17行目のsurplusの値は期待通りで、概ね想定している通りの動作のはずなのに、anyActionが実行された時に表示されるはずのメッセージがない…(゜¬゜)

 

ここまでくれば18行目の判定がおかしいというのには流石に気づけるのではないかなと( ´ ▽ ` )ノ

 

今回追加した情報は、出力を見て分かりやすいよう先頭に”DBG”の文字を書き出し、その後にファイル名と行数を表示し、それから必要なメッセージを出力する形です。

# ベタッと書いてて見づらいので、実際はマクロにしてみたり、コード上でも見やすくなるように工夫したいところ!

因みにこれらの情報はprintf()デバッグをする場合だけでなく、ログファイルなどを書き出す場合も同様に重要な情報になります。

 

“DBG”はまぁ、通常の出力との差が分かるようにしたいだけなのでどうでもいいですw

ファイル名と行数は、特に幾つものモジュールで構成された——別に大規模というわけでなく、今回書いたようなサンプルコード的な物でなければ大抵そう——プログラムを確認する時に、どこで問題が起きているかを確認するために必要です。

 

前回のWarningの時にコンパイラが出す警告文にもこの情報(ファイル名と行番号)が含まれていました(^_^;)

それを見て最初に確認するべき箇所のアタリをつけ、その前後を確認するというのは、どちらの場合でも同じなわけです。

# 大抵は出力されている場所よりも前に問題があるので、その辺りの流れを確認する意識付けを( ´ ▽ ` )ノ

 

出力するメッセージ内容はその時に確認の手助けになるような情報を表示する必要があります。

分岐の指針となる情報や計算結果など、変数の内容表示が主になるのかな…

ただし、とにかく表示させまくりすぎて逆に見るのが嫌になるほど大量に表示させたり、逆に見ても動作が類推出来なくなるほど少なすぎたりすると意味がなくなるので、その辺りの粒度については見極めが必要です(^_^;)

 

また、今回の流れでは問題が起きてからprintf()を追加していますが、可能ならば予め出力を入れておく——ログ機能などを持たせておいたり、それもダメなら事前に表示しやすい構成で書く事を意識する——様にしていれば、自然とログを見る目も養われてくるはず__ \ノ’∀ン ヒャッホウ