#ifdef / #ifndef / #if / #endif
| 対応: | C89(1989) |
|---|
条件付きコンパイルは、マクロの定義状態や値に応じてソースコードの特定部分をコンパイルするかしないかを制御します。複数のプラットフォーム対応やデバッグコードの切り替え、ヘッダファイルの二重読み込み防止などに使われます。
構文
// マクロが定義されている場合にコンパイルします。
#ifdef マクロ名
コード
#endif
// マクロが定義されていない場合にコンパイルします。
#ifndef マクロ名
コード
#endif
// #else と組み合わせた条件分岐です。
#ifdef マクロ名
マクロが定義されている場合のコード
#else
マクロが定義されていない場合のコード
#endif
// マクロの値で条件を評価します。
#if 定数式
コード
#elif 定数式
コード
#else
コード
#endif
// ヘッダガード(二重インクルード防止のイディオム)です。
#ifndef ヘッダ名_H
#define ヘッダ名_H
// ヘッダの内容
#endif
条件付きコンパイル命令一覧
| 命令 | 概要 |
|---|---|
| #ifdef | 指定したマクロが定義されていればブロックをコンパイルします。 |
| #ifndef | 指定したマクロが定義されていなければブロックをコンパイルします。ヘッダガードに使われます。 |
| #if | 定数式が0以外(真)ならブロックをコンパイルします。マクロの値で分岐できます。 |
| #elif | #if / #ifdef / #ifndef に続けて別の条件を追加します。 |
| #else | 直前の条件が偽のときのブロックを定義します。 |
| #endif | 条件ブロックを終了します。 |
| defined(マクロ名) | #if の中でマクロが定義済みかどうかを判定する演算子です。 |
サンプルコード
sample_ifdef_ifndef.c
コンパイル時に『-DDEBUG』を渡すとデバッグログが有効になります。
#include <stdio.h>
#define DEBUG
/* OS ごとにコンパイル分岐します。 */
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM "Windows"
#elif defined(__linux__)
#define PLATFORM "Linux"
#elif defined(__APPLE__)
#define PLATFORM "macOS"
#else
#define PLATFORM "Unknown"
#endif
#ifdef DEBUG
#define LOG(msg) printf("[DEBUG] %s\n", msg)
#else
#define LOG(msg)
#endif
int main(void) {
printf("プラットフォーム: %s\n", PLATFORM);
LOG("プログラムを開始します。");
int x = 42;
#ifdef DEBUG
printf("[DEBUG] x = %d\n", x);
#endif
#define VERSION 2
#if VERSION >= 2
printf("バージョン2以上の機能を使います。\n");
#elif VERSION == 1
printf("バージョン1の機能を使います。\n");
#else
printf("未知のバージョンです。\n");
#endif
return 0;
}
コンパイルして実行すると次のようになります。
gcc ifdef_ifndef.c -o ifdef_ifndef ./ifdef_ifndef プラットフォーム: macOS [DEBUG] プログラムを開始します。 [DEBUG] x = 42 バージョン2以上の機能を使います。
ヘッダガード(インクルードガード)
同じヘッダファイルが複数回インクルードされると型や関数の多重定義エラーが発生します。『#ifndef』を使ったヘッダガードで防ぎます。
player.h
#ifndef PLAYER_H
#define PLAYER_H
typedef struct {
int id;
char name[32];
int hp;
} Player;
void player_print(const Player *p);
#endif /* PLAYER_H */
ifdef_header_guard.c
#include <stdio.h>
#include "player.h"
#include "player.h" /* 2回インクルードしてもエラーにならない */
void player_print(const Player *p) {
printf("id=%d name=%s hp=%d\n", p->id, p->name, p->hp);
}
int main(void) {
Player p = {1, "Kiryu", 100};
player_print(&p);
return 0;
}
コンパイルして実行すると次のようになります。
gcc ifdef_header_guard.c -o ifdef_header_guard ./ifdef_header_guard id=1 name=Kiryu hp=100
よくあるミス
よくあるミス: ヘッダガードのマクロ名衝突
ヘッダガードのマクロ名が短すぎると他のヘッダと衝突します。ファイル名をもとに一意な名前を付けることが推奨されます。
/* NG: 短すぎて衝突しやすい */ #ifndef H #define H /* ヘッダの内容 */ #endif
修正後は次の通りです。
/* OK: ファイル名をもとにした一意な名前 */ #ifndef MYPROJECT_UTILS_H #define MYPROJECT_UTILS_H /* ヘッダの内容 */ #endif /* MYPROJECT_UTILS_H */
概要
ヘッダファイルには必ずヘッダガードを付けてください。同じヘッダが複数回インクルードされると型や関数の多重定義エラーが発生します。ヘッダガードのマクロ名には大文字・アンダースコアでファイル名を表す名前(例:『MYHEADER_H』)を使い、他のマクロと衝突しないようにしてください。
コンパイラによっては『#pragma once』という非標準の命令でヘッダガードを簡潔に書けますが、C言語の規格ではないため、移植性を重視する場合は従来の『#ifndef』パターンを使ってください。
『#pragma』については『#pragma / #error / #warning』を、マクロ定義の基本については『#include / #define(定数)』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。