名前空間
変種
操作

条件付きインクルージョン

From cppreference.com

プリプロセッサは、ソースファイルの一部を条件付きでコンパイルすることをサポートしています。この動作は、#if#else#elif#ifdef#ifndefディレクティブ、および#endifディレクティブによって制御されます。#elifdef#elifndef(C23以降)も含まれます。

目次

[編集] 構文

#if
#ifdef 識別子
#ifndef 識別子
#elif
#elifdef 識別子 (C23以降)
#elifndef 識別子 (C23以降)
#else
#endif

[編集] 説明

条件付きプリプロセッサブロックは、#if#ifdef、または#ifndefディレクティブで開始され、その後、任意の数の#elif(C23以降)、#elifdef、または#elifndefディレクティブがオプションで続き、最大1つの#elseディレクティブがオプションで続き、#endifディレクティブで終了します。内部の条件付きプリプロセッサブロックは個別に処理されます。

#if#ifdef#ifndef#elif(C23以降)、#elifdef#elifndef、および#elseディレクティブは、最初の#elif(C23以降)、#elifdef#elifndef#else#endifディレクティブ(内部の条件付きプリプロセッサブロックに属さないもの)までのコードブロックを制御します。

#if#ifdef#ifndefディレクティブは、指定された条件(下記参照)をテストし、それが真と評価された場合は、制御されたコードブロックをコンパイルします。この場合、後続の#else(C23以降)、#elifdef#elifndef、および#elifディレクティブは無視されます。それ以外の場合、指定された条件が偽と評価された場合、制御されたコードブロックはスキップされ、後続の#else(C23以降)、#elifdef#elifndef、または#elifディレクティブ(存在する場合)が処理されます。後続のディレクティブが#elseの場合、#elseディレクティブによって制御されるコードブロックは無条件にコンパイルされます。それ以外の場合、#elif(C23以降)、#elifdef、または#elifndefディレクティブは、#ifディレクティブのように動作します。つまり、条件をチェックし、結果に基づいて制御されたコードブロックをコンパイルまたはスキップし、後者の場合は後続の#elif(C23以降)、#elifdef#elifndef、および#elseディレクティブを処理します。条件付きプリプロセッサブロックは#endifディレクティブで終了します。

[編集] 条件付き評価

[編集] #if, #elif

は、定数と、 #define ディレクティブを使用して定義された識別子のみを使用する定数式です。#defineディレクティブで定義されていないリテラルでない識別子は、0と評価されます(C23以降では、true1と評価されます)。

式には、defined 識別子またはdefined (識別子)の形式の単項演算子を含めることができます。これは、識別子 #define ディレクティブを使用して定義されている場合は1を返し、そうでない場合は0を返します。C23以降では、このコンテキストでは、__has_include__has_embed、および__has_c_attributeは、定義されたマクロの名前として扱われます。式がゼロ以外の値を評価した場合、制御されたコードブロックは含まれ、それ以外の場合はスキップされます。使用されている識別子が定数でない場合、0に置き換えられます。

プリプロセッサディレクティブのコンテキストでは、__has_c_attribute式は、指定された属性トークンがサポートされているかどうか、およびそのサポートされているバージョンを検出します。属性テストを参照してください。

(C23以降)

注:DR 412まで、#if cond1 ... #elif cond2は、#if cond1 ... #elseに続いて#if cond3とは異なっていました。なぜなら、cond1が真の場合、2番目の#ifはスキップされ、cond3は有効である必要がなかったのに対し、#elifcond2は有効な式である必要があったからです。DR 412以降、スキップされたコードブロックに続く#elifもスキップされます。

[編集] 複合ディレクティブ

識別子がマクロ名として定義されているかどうかをチェックします。

#ifdef 識別子は、実質的に#if defined 識別子と同等です。

#ifndef 識別子は、実質的に#if !defined 識別子と同等です。

#elifdef 識別子は、実質的に#elif defined 識別子と同等です。

#elifndef 識別子は、実質的に#elif !defined 識別子と同等です。

(C23以降)

[編集] 注意

#elifdefおよび#elifndefディレクティブはC23を対象としていますが、実装では、準拠拡張として古い言語モードにバックポートする場合があります。

[編集]

#define ABCD 2
#include <stdio.h>
 
int main(void)
{
 
#ifdef ABCD
    printf("1: yes\n");
#else
    printf("1: no\n");
#endif
 
#ifndef ABCD
    printf("2: no1\n");
#elif ABCD == 2
    printf("2: yes\n");
#else
    printf("2: no2\n");
#endif
 
#if !defined(DCBA) && (ABCD < 2 * 4 - 3)
    printf("3: yes\n");
#endif
 
// C23 directives #elifdef/#elifndef
#ifdef CPU
    printf("4: no1\n");
#elifdef GPU
    printf("4: no2\n");
#elifndef RAM
    printf("4: yes\n"); // selected in C23 mode, may be selected in pre-C23 mode
#else
    printf("4: no3\n"); // may be selected in pre-C23 mode
#endif
}

実行結果の例

1: yes
2: yes
3: yes
4: yes

[編集] 不具合報告

以下の動作変更を伴う欠陥報告が、以前に発行されたC規格に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
DR 412 C89 失敗した#elifの式は有効である必要がありました 失敗した#elifはスキップされます

[編集] 参照

  • C23標準 (ISO/IEC 9899:2024)
  • 6.10.1 条件付きインクルージョン (p: TBD)
  • C17標準 (ISO/IEC 9899:2018)
  • 6.10.1 条件付きインクルージョン (p: 118-119)
  • C11標準 (ISO/IEC 9899:2011)
  • 6.10.1 条件付きインクルージョン (p: 162-164)
  • C99標準 (ISO/IEC 9899:1999)
  • 6.10.1 条件付きインクルージョン (p: 147-149)
  • C89/C90標準 (ISO/IEC 9899:1990)
  • 3.8.1 条件付きインクルージョン

[編集] 関連項目

C++ ドキュメント条件付きインクルージョン
English 日本語 中文(简体) 中文(繁體)