言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

C言語辞典

  1. トップページ
  2. C言語辞典
  3. #ifdef / #ifndef / #if / #endif

#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(定数)』を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。