導入
開発中はデバッグ用の printf をたくさん入れたいけれど、完成版ではそれらを全部消したい……という場面はよくあります。行ごとコメントアウト/インするのは面倒です。#ifdef/#ifndef を使うと、「ある名前が定義されているかどうか」で、コードの一部をコンパイルに含めるかどうかを丸ごと切り替えられます。
説明
#include <stdio.h> は「そのファイルの内容をこの場所にそのまま貼り込む」というプリプロセッサ指令でした。同じように #ifdef 名前 は「名前 が #define されていれば、対応する #endif までの行をコードに含める」という指令です。反対に #ifndef 名前 は「名前 が #define されていなければ含める」という意味になります。
#include <stdio.h>
#define DEBUG
int main(void) {
int x = 10;
#ifdef DEBUG
printf("[DEBUG] x = %d\n", x);
#endif
printf("結果: %d\n", x * 2);
return 0;
}
#define DEBUG の行があるので DEBUG は定義済みです。すると #ifdef DEBUG から #endif までの printf がコンパイルに含まれ、実行結果には [DEBUG] x = 10 が出力されます。もし先頭の #define DEBUG を消すと、DEBUG は定義されていないので、その printf はコンパイルの時点で丸ごと存在しないのと同じになり、実行時に一切出てきません。
flowchart TB
d{"DEBUG は #define 済み?"}
d -->|"はい (#ifdef)"| a["[DEBUG]の行をコードに含める"]
d -->|"いいえ"| b["[DEBUG]の行はコンパイル対象から除外"]
#else を組み合わせると、「定義されているとき/されていないとき」の両方を書けます。
#include <stdio.h>
#define MODE_DEBUG
int main(void) {
#ifdef MODE_DEBUG
printf("デバッグモード\n");
#else
printf("リリースモード\n");
#endif
return 0;
}
if/else と似ていますが、大きな違いがあります。if/else は実行時にどちらを実行するか決まるのに対し、#ifdef/#else はコンパイルされる前にどちらのコードを含めるかが決まり、含まれなかった側は実行ファイルに一切残りません。
やってみよう
#define DEBUG の行をコメントアウトして、デバッグ出力が消えることを確かめましょう。MODE_DEBUG の例も、#define の行を消して #else 側が実行されることを確認してください。
演習
#define SHOW_EXTRA を使って、SHOW_EXTRA が定義されているときだけ 追加情報\n を表示し、常に 基本情報\n を表示するプログラムを書いてください(SHOW_EXTRA を定義した状態で実行すると両方表示されます)。
ヒント1を見る
先頭に #define SHOW_EXTRA を書きます。
ヒント2を見る
#ifdef SHOW_EXTRA から #endif の間に printf("追加情報\n"); を置きます。