Namespaces
Variants

Conditional inclusion

From cppreference.net

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

目次

構文

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

説明

条件付きプリプロセスブロックは #if #ifdef または #ifndef ディレクティブで始まり、任意の数の #elif #elifdef または #elifndef (C23以降) ディレクティブを任意の数だけ含み、任意で最大1つの #else ディレクティブを含み、 #endif ディレクティブで終了します。内部の条件付きプリプロセスブロックは個別に処理されます。

#if #ifdef #ifndef #elif #elifdef #elifndef (C23以降) および #else ディレクティブはそれぞれ、最初の #elif #elifdef #elifndef (C23以降) #else #endif ディレクティブに到達するまでのコードブロックを制御します。ただし、内側の条件付きプリプロセスブロックに属さないものに限ります。

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

条件付き評価

#if, #elif

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

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

プリプロセッサ指令の文脈において、 __has_c_attribute 式は、指定された属性トークンがサポートされているかどうか、およびそのサポートされているバージョンを検出します。詳細は 属性テスト を参照してください。

(C23以降)

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

結合ディレクティブ

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

#ifdef identifier は本質的に #if defined identifier と同等です。

#ifndef identifier は本質的に #if !defined identifier と同等です。

#elifdef identifier は本質的に #elif defined identifier と同等です。

#elifndef identifier は本質的に #elif !defined identifier と同等です。

(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++ documentation for 条件付きインクルード