第14回
ヘッダファイルとプリプロセッサ指令

デバッグとプリプロセッサ指令

プリプロセッサ指令をうまく使うと、printf関数を使ったデバッグを効率的に行えます。

記号定数“DEBUG”

前回、printf関数を使ったデバッグの方法を紹介しました。リスト3のように、処理の途中で調べたい変数の値をprintf文で表示する方法です。

この方法は手軽で便利ですが、バグがすべて取れて最終版をコンパイルする前に、デバッグのためのprintf文を削除またはコメントアウト ※3 しなければなりません。1~2箇所ならともかく、たくさんあると大変な作業になります。

そこで#ifdefプリプロセッサ指令を使い、記号定数“DEBUG”が定義されていればデバッグ用のprintf文をコンパイルし、定義されていなければその部分をコンパイルしない──という形にしておけば、作業は非常に楽になります。

ソースの一部をコメント記号/* */で囲んで無効にすること
リスト3:カウンタ変数iの値を調べるためにprintf文を挿入した九九プログラム
#include <stdio.h>

int	main(void)
{
  int i;
  int num;

  printf("Input Base number : ");
  scanf("%d", &num);
  for (i=0; i<19; i++) {
    printf("DEBUG : i = %d\n", i); -------------- ★
    printf("%d\n", num * i);
  }
  return (0);
}

コマンドラインで記号定数を定義

リスト4は、リスト3に記述したデバッグ用のprintf文(★マークの箇所)を記号定数“DEBUG”が定義されているときだけコンパイルするようにしたソースです。ソースの冒頭で
#define DEBUG 1
のように記号定数“DEBUG”を定義しても構いませんが、コマンドライン版の多くのコンパイラには、コマンドラインオプションで記号定数を定義する機能があります。LSI-Cでは-Dオプションを使い
LCC -oC:\clang\exe\kuku.exe -DDEBUG C:\clang\source\kuku.c
のようにします。この場合DEBUGは1に定義されるため、ソースの先頭で#define指令を記述する必要はありません。

LinuxなどUNIX系OSの標準C処理系であるGCC(GNU C Compiler)でも、同じく-Dオプションで記号定数を定義できます。

Visual C++のコマンドライン版コンパイラCLでは、/Dオプションを使用して同じことができます。

リスト4:識別子“DEBUG”が定義されているときだけデバッグ用のprintf文をコンパイルするようにした九九プログラム
#include <stdio.h>

int	main(void)
{
  int i;
  int num;

  printf("Input Base number : ");
  scanf("%d", &num);
  for (i=0; i<19; i++) {
#idfef DEBUG
    printf("DEBUG : i = %d\n", i); -------------- ★
#endif
    printf("%d\n", num * i);
  }
  return (0);
}

printfデバッグの工夫

プログラムがたくさんの文字列を出力して表示がスクロールアップするような仕様だと、単にprintf文で変数の値を表示するだけでは、変数の値が確認できないことがあります。

そのような場合には、以下のようにprintf文に続けてgetchar関数を記述し、[Enter]キーを押すまで処理を一時停止するようにするとよいでしょう。
#ifdef DEBUG
  printf("変数 num = %d\n", num);
  (void)getchar();
#endif

あとがき

hiropの『ちょっと気になる専門用語』~《記号定数》

本文で『記号定数』という用語が登場しました。プロプロセッサ指令#defineを用いて値に割り当てた別名のことです。この値は定数です。

定数とはプログラムの実行中に変化しない値を意味し、ソースでは数値なら「100」のように、文字列なら""で囲んで"Hello"のように、直接その値を記述することができます。

しかし、そのようにすると入力ミスで同じであるべき値が異なるものになってしまう場合があります。そこで#defineプリプロセッサ指令を使い
#define __MAX 100
といったように値に名前を付け、入力ミスを防ぐことになります。

このように、ある値に付けた別名をCでは『記号定数』と呼びます。記号化された定数という意味です。