C++ 预处理命令是编译器在正式编译之前执行的一系列指令。它们以 # 号开头,用于控制编译过程。预处理命令可以进行宏定义、文件包含、条件编译等操作,从而提高代码的灵活性和可维护性。C++是从C语言发展过来,预处理也是继承自C语言。

1、预处理指令

预处理指令是以#号开头的代码行,# 号必须是该行除了任何空白字符外的第一个字符。# 后是指令关键字,在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

常用的预处理指令:

指令

描述

#define

定义宏

#include

包含一个源代码文件

#undef

取消已定义的宏

#ifdef

如果宏已经定义,则返回真

#ifndef

如果宏没有定义,则返回真

#if

如果给定条件为真,则编译下面代码

#else

#if 的替代方案

#elif

如果前面的 #if 给定条件不为真,

当前条件为真,则编译下面代码

#endif

结束一个 #if……#else 条件编译块

#error

当遇到标准错误时,输出错误消息

#pragma

使用标准化方法,

向编译器发布特殊的命令到编译器中

2、宏定义(#define)

宏定义又称为宏代换、宏替换,简称“宏”。

1) 不带参数的宏定义

格式如下:

#define 标识符 文本

其中的标识符就是所谓的符号常量,也称为“宏名”。

例如,

#include<iostream>
using namespace std;

#define PI 3.14159

int main() {
    double r = 5.0;
    double area = PI * r * r; // PI 会被替换为 3.14159
    return 0;
}

注意:

宏名一般用大写

使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义

可以用#undef命令终止宏定义的作用域

宏定义可以嵌套

2)带参数的宏

除了一般的字符串替换,还要做参数代换。

格式如下:

#define 宏名(参数表) 文本

例如,

#include<iostream>
using namespace std;


#define SQUARE(x) ((x) * (x))

int main() {
    int a = 5;
    int result = SQUARE(a); // SQUARE(5) 替换为 ((5) * (5))
    cout << result;
    return 0;
}

注意:有时宏替换后和我们预期的结果不一样需要注意一下,宏替换只是简单的替换。

3、文件包含

C++ 程序文件中一个文件包含另一个文件的内容。

格式如下:

#include "文件名"

#include <文件名>

常见用法:

 #include <iostream>            //标准库头文件
 #include <iostream.h>          //旧式的标准库头文件

注意iostream.h只支持窄字符集,iostream则支持窄/宽字符集。一般使用iostream

#include <iostream>  // 标准库头文件
#include "myfile.h"  // 用户定义的头文件
using namespace std;

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

相关文档: C语言中#include <filename> 和 #include "filename"的区别

编译时以包含处理以后的文件为编译单位,被包含的文件是源文件的一部分。
编译以后只得到一个目标文件.obj
被包含的文件又被称为“标题文件”或“头部文件”、“头文件”,并且常用.h作扩展名。
修改头文件后所有包含该文件的文件都要重新编译
头文件的内容除了函数原型和宏定义外,还可以有结构体定义,全局变量定义:

1)一个#include命令指定一个头文件;

2)文件1包含文件2,文件2用到文件3,则文件3的包含命令#include应放在文件1的头部第一行;

3)包含可以嵌套;

4)<文件名>称为标准方式,系统到头文件目录查找文件,"文件名"则先在当前目录查找,而后到头文件目录查找;

5)被包含文件中的静态全局变量不用在包含文件中声明。

4、 条件编译

有些语句在条件满足时才编译。例如,不同平台使用不同的代码实现,需要针对平台来编译。

格式:

#ifdef 标识符
程序段1
#else
程序段2
#endif

或者

#ifdef
程序段1
#endif

注意:

上面格式中,当标识符已经定义时,程序段1才参加编译。

或者

#ifndef 标识符
#define 标识1
程序段1
#endif

注意:

上面格式中,如果标识符没有被定义,则重定义标识1,且执行程序段1。

或者

#if 表达式1
程序段1
#elif 表达式2
程序段2
……
#elif 表达式n
程序段n
#else
程序段n+1
#endif

注意:

上面格式中,当表达式1成立时,编译程序段1,当不成立时,编译程序段2。

使用条件编译可以使目标程序变小,运行时间变短。

#include <iostream>  // 标准库头文件
using namespace std;

#define VALUE 10

#if VALUE > 20
    #define MESSAGE "Value is greater than 20"
#elif VALUE == 20
    #define MESSAGE "Value is equal to 20"
#else
    #define MESSAGE "Value is less than 20"
#endif

int main() {
    std::cout << MESSAGE << std::endl;
    return 0;
}

5、使用示例

编译器指令#pragma为编译器提供特殊指令(不同编译器支持不同指令)。. 报告错误#error在编译阶段生成错误消息。更改行号#line更改当前行号和文件名。

#include <iostream>

// 使用 #pragma 指令(根据不同编译器使用不同功能)
#ifdef _MSC_VER
#pragma warning(disable : 4996)  // 禁用 MSVC 编译器关于不安全函数的警告
#endif

// 检查 C++ 版本是否符合要求
#if __cplusplus < 201103L
#error "This program requires C++11 or higher!"  
// 如果不是 C++11 或更高版本,报错
#endif

// 使用 #line 更改当前文件名和行号
#line 100 "custom_file.cpp"

struct MyStruct {
    char a;
    int b;
};
int main() {
    // #pragma 示例:结构体对齐
#pragma pack(1)  
// 设置数据对齐为 1 字节
    MyStruct obj;

    std::cout 
    << "This code is at line 100 in custom_file.cpp" 
    << std::endl;
    std::cout 
    << "Size of MyStruct (1-byte alignment): " 
    << sizeof(obj) << std::endl;

    // 恢复默认对齐方式
#pragma pack()  // 取消自定义对齐
    return 0;
}

推荐文档