目录
1 预处理指令是什么
预处理指令又称预编译指令,是一类在C/C++源文件编译前执行的指令,其通常以一个井号(#)开头,且以换行符结尾。通过预编译指令,可以实现文件包含、定义或取消定义宏、条件编译、重置行号和文件名信息、报告错误与警告、设置编译器等。
2 常用的预处理指令
以下是在C/C++编程中一些常用的预处理指令:
预处理指令 | 用法描述 |
---|---|
#include | 用于将指定的文件包含到当前的编译单元中 |
#define | 用于定义宏,宏可以是符号常量,也可以是参数宏 |
#undef | 用于撤销一个宏定义 |
#ifdef | 用于检查一个宏是否已被定义 |
#ifndef | 用于检查一个宏是否未被定义 |
#if | 用于进行条件编译 |
#elif | 用于#if和#else之间的条件编译 |
#else | 用于#if和#elif之外的条件编译 |
#endif | 用于结束#if, #ifdef, #ifndef, #elif 和#else指令 |
#pragma | 用于设置编译器的特定指令,用法和效果取决于编译器 |
#line | 用于重置编译器的编译行号和文件名信息 |
#error | 用于在预处理期间报告一个错误信息,并停止编译 |
#warning | 用于在预处理期间报告一个警告信息 |
3 预处理指令用法示例
3.1 包含文件
#include预处理指令用于在源文件中包含指定文件,当源文件被编译时,其将指定文件展开并将内容插入到该指令所在的位置,具体语法如下:
#include <头文件名> // example: #include <stdio.h> #include "文件名" // example: #include "myheader.h"
#include预处理指令的具体用法以及 <> 和 “” 的区别可参看文章《[C/C++]#include <> 和 “” 的用法》。
3.2 创建符号常量
#define预处理指令可以用于创建符号常量,该符号常量通常称为宏,示例如下:
#include <stdio.h> #define MY_WEBSITE "www.nixinglong.com" int main() { printf("my website is %s", MY_WEBSITE); // MY_WEBSITE 被替换为 "www.nixinglong.com" return 0; }
3.3 创建参数宏
#define预处理指令也可以用于创建带有参数的宏,示例如下:
#include <stdio.h> #define MIN(a,b) (a<b ? a : b) int main () { int i, j; i = 100; j = 30; printf("min value is %d", MIN(i, j)); // MIN(i, j) 被替换为 (i<j ? i : j) return 0; }
3.4 条件编译
条件编译是指编译器有选择地对部分程序源代码进行编译。通过#define、#if、#elif、#else、#endif、#ifdef、#ifndef预处理指令与defined运算符的结合使用,可以进行条件编译。
3.4.1 空宏
#define预处理指令也可以用于创建空宏,空宏没有替换作用,其主要用于条件编译,示例如下:
#include <stdio.h> #define DEBUG // 此处定义了 DEBUG 宏 #define MY_WEBSITE "www.nixinglong.com" int main() { printf("my website is %s", MY_WEBSITE); // MY_WEBSITE 被替换为 "www.nixinglong.com" #ifdef DEBUG // DEBUG 宏有被定义, 故以下代码段会被编译 printf("this function is %s", __FUNCTION__); #endif return 0; }
3.4.2 defined运算符
defined运算符可以返回当前环境是否定义了某个宏,若宏存在,则其返回值为1;若宏不存在,则其返回值为0。defined不是预处理指令,其通常搭配#if等预处理指令使用,示例如下:
#include <stdio.h> #define DEBUG // 此处定义了 DEBUG 宏 #define MY_WEBSITE "www.nixinglong.com" int main() { #if defined(DEBUG) // 等价于 #ifdef DEBUG printf("my website is %s", MY_WEBSITE); // MY_WEBSITE 被替换为 "www.nixinglong.com" #endif #undef DEBUG #if !defined(DEBUG) // 等价于 #ifndef DEBUG printf("my website is %s", MY_WEBSITE); // MY_WEBSITE 被替换为 "www.nixinglong.com" #endif return 0; }
3.4 报告编译错误与警告
为了方便程序编译与调试,可以通过#error与#warning预处理指令手动设置一些编译调试信息的输出,示例如下:
#define LCD_WEIGHT 1024 #define LCD_HEIGHT 768 #ifndef LCD_WEIGHT || LCD_HEIGHT #error no define LCD parameter // 若没有定义 LCD_WEIGHT 或 LCD_HEIGHT, 在编译时会报error, 并停止编译 #endif
#define LCD_WEIGHT 1024 #define LCD_HEIGHT 768 #ifndef LCD_WEIGHT || LCD_HEIGHT #warning no define LCD parameter // 若没有定义 LCD_WEIGHT 或 LCD_HEIGHT, 在编译时会报warning, 但继续编译 #endif
3.5 设置编译器
通过#pragma预处理指令可以控制编译器的行为,具体的参数用法和效果取决于编译器,常见的通用语法有:
- #pragma once:用于头文件第一行,指示编译器在一个编译单元中只包含该文件一次
#pragma once
- #pragma message:用于在编译过程中输出自定义的消息(类似于#error与#warning)
#pragma message("This is a custom message")
3.6 重置行号与文件名
通过#line预处理指令可以将编译器的行号和文件名报告值设置为给定行号和文件名,其语法如下:
#line <line-number> ["filename"]
- line-number:指定新的行号
- “filename”:指定新的文件名(可选参数)
以下示例演示了#line
以及 __LINE__
和 __FILE__
宏的使用:
// line_directive.cpp #include <stdio.h> int main() { printf( "This code is on line %d, in file %s\n", __LINE__, __FILE__ ); #line 10 printf( "This code is on line %d, in file %s\n", __LINE__, __FILE__ ); #line 20 "hello.cpp" printf( "This code is on line %d, in file %s\n", __LINE__, __FILE__ ); printf( "This code is on line %d, in file %s\n", __LINE__, __FILE__ ); }
输出结果为:
This code is on line 6, in file line_directive.cpp
This code is on line 10, in file line_directive.cpp
This code is on line 20, in file hello.cpp
This code is on line 21, in file hello.cpp