C++条件编译指令:#if、#elif、#ifdef、#ifndef、#elifdef、#elifndef、#else、#endif

目录

[1. 条件编译指令](#1. 条件编译指令)

[2. 语法](#2. 语法)

[3. #if #elif #else #endif](#if #elif #else #endif)

注意:预处理阶段不能使用枚举变量,枚举常量

[3. #ifndef #define #endif 声明头文件](#ifndef #define #endif 声明头文件)

[4. 检测标识符是否被定义为宏名](#4. 检测标识符是否被定义为宏名)

[5. 综合示例](#5. 综合示例)


1. 条件编译指令

预处理指令控制预处理器的行为。编译器将源代码生成目标程序时,预处理器在编译前的翻译阶段4执行,预处理的结果会生成单个文件,这个文件会传递给实际的编译器。

条件编译指令 用于控制预处理器根据条件判断决定是否编译某段代码。这一行为由 #if#elif#ifdef#ifndef#elifdef (C++23起) 、#elifndef (C++23起) 、#else 、和 #endif 预处理指令控制。

2. 语法

  • #if 表达式
  • #elif 表达式
  • #ifdef 标识符
  • #ifndef 标识符
  • #elifdef 标识符 (C++23起)
  • #elifndef 标识符 (C++23起)
  • #else
  • #endif

说明

(1)#if 和 #elif 接的表达式 中可以含有如 defined 标识符defined(标识符) 的一元运算符,当此标识符已经被定义为宏名时,结果为 1,否则结果为 0。当整个表达式求值为非0时,编译其所控制的代码块,否则不编译该代码块。

cpp 复制代码
#include <iostream>

#define DEBUG 0

int main() {
#if defined DEBUG   // 因为 DEBUG 标识符已被定义为宏名,所以 defined DEBUG 的结果为 1
    std::cout << "DEBUG 标识符已被定义为宏名" << std::endl;
#endif

#if defined(DEBUG)  // 因为 DEBUG 标识符已被定义为宏名,所以 defined(DEBUG) 的结果为 1
    std::cout << "DEBUG 标识符已被定义为宏名" << std::endl;
#endif

    return 0;
}

(2)#if 和 #elif 接的表达式 中也可以含有**__has_include** (C++17 起) 或 __has_cpp_attribute (C++20 起) 表达式。

__has_include 表达式,检测某个头文件或源文件是否存在

__has_cpp_attribute 表达式,检测给定属性记号是否受支持和支持的版本。

cpp 复制代码
#include <iostream>

int main() {
#if __has_include("define.h")
        std::cout << "define.h 存在" << std::endl;
#endif

#if __has_include(<string>)
    std::cout << "string 头文件存在" << std::endl;
#endif

    return 0;
}

(3)在进行所有宏展开和 defined、__has_include (C++17 起) 和 __has_cpp_attribute (C++20 起) 表达式的求值后,任何非布尔字面量(即不是 true 和 false)的标识符都被替换成数字 ​0​(这包含词法上为关键字的标识符,但不包括如 and 之类的代用记号)。

cpp 复制代码
#include <iostream>

int main() {
#if std::cout // std::cout 在预处理阶段会被数字 0 替代
    std::cout << "这句代码不会被编译" << std::endl;
#endif

    int a = 1;
#if a and ture  // 因为变量a是在编译阶段才会被处理,在预处理阶段只是一个非布尔字面量的标识符,会被 0 替代
    std::endl << "这句代码不会被编译" << std::endl;
#endif

#if 0 and false
    std::cout << "这句代码不会被编译" << std::endl;
#else
    std::cout << "这句代码会被编译,也会被执行" << std::endl;
#endif

    return 0;
}

3. #if #elif #else #endif

语法

cpp 复制代码
#if 表达式1
    // 表达式1为真,该段代码会被编译
#elif 表达式2
    // 如果表达式1为假,表达式2为真,该段代码会被编译
#else
    // 表达式1和表达式2都为假,则该段代码会被编译
#endif

示例

cpp 复制代码
#include <iostream>

#define platform 1

int main() {
#if platform == 0
    std::cout << "windows" << std::endl;
#elif platform == 1
    std::cout << "apple" << std::endl;
#else
    std::cout << "android" << std::endl;
#endif

    return 0;
}

注意:预处理阶段不能使用枚举变量,枚举常量

因为在预处理阶段,只要是非布尔字面量的标识符都会被数字 0 代替

以下是错误举例

cpp 复制代码
#include <iostream>

enum PLATFORM {
    WINDOWS,     // windows
    APPLE,       // 苹果
    ANDROID      // 安卓
};
PLATFORM platform = APPLE;

int main() {
    // 预编译阶段不能使用枚举变量、枚举常量
#if platform == WINDOWS // 在预编译阶段 platform 和 WINDOWS 都是一个非布尔字面量的标识符,
                        // 都会被数字 0 替代,0 == 0 为真,所以这段代码会被编译
    std::cout << "windows" << std::endl;
#elif platform == APPLE
    std::cout << "apple" << std::endl;
#else
    std::cout << "android" << std::endl;
#endif

    return 0;
}

3. #ifndef #define #endif 声明头文件

cpp 复制代码
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 头文件内容
#endif // MY_HEADER_H

4. 检测标识符是否被定义为宏名

(1)#ifdef 标识符,如果标识符已被定义为宏名则为真,否则为假

(2)#ifndef 标识符,如果标识符没有被定义为宏名则为真,否则为假

(3)#elifdef 标识符,如果标识符已被定义为宏名则为真,否则为假

(4)#elifndef 标识符,如果标识符没有被定义为宏名则为真,否则为假

#ifdef 标识符 等价于 #if defined(标识符)

#ifndef 标识符 等价于 #if !defined(标识符)

#elifdef 标识符 等价于 #elif defined(标识符)

#elifndef 标识符 等价于 #elif !defined(标识符)

cpp 复制代码
#include <iostream>

#define AAA 1

int main() {
#ifdef AAA
    std::cout << "AAA" << std::endl;
#endif
#if defined(AAA)
    std::cout << "AAA" << std::endl;
#endif
    return 0;
}

5. 综合示例

cpp 复制代码
#include <iostream>

#define AAA 0
#define TRUE true

int main() {
#if 0
    std::cout << "这句代码不会被编译,也不会被执行" << std::endl;
#endif

#if TRUE // 预处理器会将宏TRUE展开为 true
#ifdef AAA
    std::cout << "AAA 宏已被定义" << std::endl;
#else
    std::cout << "AAA 宏没有被定义" << std::endl;
#endif

#if !defined(BBB)
    std::cout << "BBB 宏都没有被定义" << std::endl;
#endif

#ifdef AAA and __has_include(<string>)
    std::cout << "AAA 宏已被定义,并且 string 头文件存在" << std::endl;
#endif
#endif

    return 0;
}
相关推荐
Zixhy3 小时前
手撕Reactor模型实现同步高并发服务器及Muduo网络库深入解析
linux·服务器·网络·数据库·c++
傻啦嘿哟9 小时前
某招聘平台爬虫:爬取招聘岗位数据,分析各城市薪资水平
开发语言·爬虫·python
2501_933670799 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
李少兄10 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
码匠许师傅10 小时前
【设计模式精讲】29.行为型模式总结与对比(Behavioral Patterns Summary)
c++·设计模式·uml
Seoyoneh11 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
啦啦啦啦啦zzzz11 小时前
Logger的组装(c++20)
c++·算法·c++20
Tangyuewei11 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界11 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
繁星蓝雨12 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进