条件包含

来自cppreference.com

预处理器支持有条件地编译源文件的某些部分。这一行为由 #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 起) 指令,接下来是最多一个可选的 #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

表达式可以含有形式为 defined 标识符defined (标识符) 的一元运算符,若用 #define 指令定义了该 标识符 ,则返回 1 ,否则返回 0 。若 表达式 求值为非零值,则包含该控制代码块并跳过其他。若所用的任何标识符不是常量,则用 0 替换它。

预处理器语境中, __has_c_attribute 表达式检测给定属性记号是否受支持及其受支持版本。见属性测试

(C23 起)

注: DR 412 前,“#if cond1 ... #elif cond2”和“#if cond1 ... #else”后面跟着“#if cond3”是不同的,因为当 cond1 为真时第二个 #if 将被跳过,且 cond3 并不需要是良构的,而 #elif 中的 cond2 则必须是合法的表达式。 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 指令 #elifdef/#elifndef
#ifdef CPU
    printf("4: no1\n");
#elifdef GPU
    printf("4: no2\n");
#elifndef RAM
    printf("4: yes\n"); // C23 模式中得到选择, C23 之前模式中可能得到选择
#else
    printf("4: no3\n"); // C23 之前模式中可能得到选择
#endif
}

可能的输出:

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

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C 标准。

缺陷报告 应用于 出版时的行为 正确行为
DR 412 C++14 要求失败的 #elif 中的表达式合法 跳过失败的 #elif

引用

  • C17 标准(ISO/IEC 9899:2018):
  • 6.10.1 Conditional inclusion (第 118-119 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 6.10.1 Conditional inclusion (第 162-164 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 6.10.1 Conditional inclusion (第 147-149 页)
  • C89/C90 标准(ISO/IEC 9899:1990):
  • 3.8.1 Conditional inclusion

参阅