新刊ピックアップ

ツールいらずのデバッグテクニック

この記事を読むのに必要な時間:およそ 1 分

バグのありか,わかりますか?

突然ですが,プログラマの皆さんにクイズです。リスト1は,2つの整数の最大公約数を求めるC言語の関数です。大きい方の数から小さい方の数を引くことを繰り返して最大公約数を求める「ユークリッドの互除法」と呼ばれるアルゴリズムを使っています。

ですが,リスト1のgcm関数は引数a,bで与えられた整数の最大公約数を求めることができません。プログラムのどこにバグがあるのかわかりますか?

リスト1 バグのあるユークリッドの互除法

int gcm(int a, int b)
{
     while (a !=b)
     {
           if (a > b)
           {
                a -= b;
           }
           else
           {
                b = a;
           }
      }
      
      return a;
}

リスト1は,その圧倒的にわかりやすい解説で多くのプログラマから支持を集める矢沢久雄氏の新刊プログラミングのセオリーの中で解説されているプログラムです。

本書の目的は,若手プログラマの方,プログラミングの腕をもっと磨きたい方に,先人プログラマが培ってきた知恵とテクニックを伝えることにあります。矢沢氏は本書の「はじめに」でこう記しています。

『プログラミングのセオリー』は,筆者がプログラミングする際に常に心がけているセオリー(定石)をまとめたものです。⁠中略)本書で紹介しているのは,これまでの20年間で見出した代表的なセオリーです。筆者が信じているセオリーの多くが,皆さんのプログラミングに磨きをかける一助となれば幸いです。

思いどおりに動かない。そんなときは……

話を元に戻しましょう。gcm関数の引数に,たとえば30と42を与えたとすると,リスト1の実行結果は30になります(ここではmain関数は省略します⁠⁠。処理のどこかに問題があり,変数の値が意図したとおりになっていないことが明らかです。プログラミングの最中には,よくある問題ではないでしょうか。

プログラムの動作が思いどおりにならないときに使えるセオリーが「printfデバッグ」です。printfデバッグでは,画面に変数の値を表示するデバッグ用のコードを追加しますリスト2⁠。

リスト2 printfデバッグを追加

#include <stdio.h>

int gcm(int a, int b)
{
     printf("a = %d, b = %d\n", a, b);     // DEBUG
     while (a !=b)
     {
           if (a > b)
           {
                a -= b;
           }
           else
           {
                b = a;
           }
           printf("a = %d, b = %d\n", a, b);     // DEBUG
      }
      
      return a;
}

リスト2の実行結果は次のようになります。

a = 30, b = 42
a = 30, b = 30

printfデバッグでプログラムを追う

printfデバッグの結果,引数には問題がないものの,while文による繰り返しが1回しか行われていないことがわかります。変数a,bの値が最大公約数と無関係の30になってしまっていることも,2つ目のprintfによって示されています。

どうでしょう? バグが見つかったでしょうか? 不具合の原因は,elseブロックの中にあるb=a;です。正しい処理は,b=b-a;,つまりbー=a;です。リスト2のバグを修正したうえでの実行結果は次のようになります。

a = 30, b = 42
a = 30, b = 12
a = 18, b = 12
a = 6, b = 12
a = 6, b = 6

手元にデバッガがない環境でプログラミングするときや,デバッガを使うほどではないけれど処理の流れや変数の値の変化を追いかけたいときがあるでしょう。そんなときにprintfデバッグは使い勝手のいいテクニックとして,皆さんのプログラミングをサポートしてくれます。