预处理器(preprocessor)在真正编译之前处理以 # 开头的指令。它可以定义符号、按条件保留代码,以及把头文件内容引入当前源文件。预处理只是文本层面的准备阶段,不理解变量类型。
宏
宏把一个名字替换为一段文本。表示常量的宏通常使用全大写名称。
#include <iomanip>
#include <iostream>
#define PI 3.1415926
double circle_area(double radius) {
return PI * radius * radius;
}
int main() {
double radius = 3.0;
std::cout << std::fixed << std::setprecision(2);
std::cout << "area = " << circle_area(radius) << '\n';
return 0;
}函数式宏
函数式宏看起来像函数调用,但它不会创建栈帧,也不会进行类型检查。参数可能是一整段表达式,所以参数与最终结果都应该加括号。还要避免把 i++ 这类有副作用的表达式传给会重复使用参数的宏。
#include <iostream>
#define SQUARE(x) ((x) * (x))
int main() {
int n = SQUARE(3 + 1);
std::cout << "n = " << n << '\n';
return 0;
}知识测验
题目
为什么 SQUARE(x) 的参数和整个结果都需要括号?
互动实验
宏展开传送带
访问权限免费
一步步替换 SQUARE 的两个参数,再观察括号如何决定计算顺序。预处理器只替换文本,不计算数值。
宏定义 · 替换规则#define SQUARE(x) ((x) * (x))
01 / 预处理:替换文本
识别宏与实参SQUARE(3 + 1)
SQUARE 是宏名。括号内的整段文本 3 + 1 是实参,此时还没有做加减法。
1 / 8
条件编译
#if 根据条件决定保留哪一段源代码,#else 提供条件不成立时的另一段代码,#endif 结束这一组条件编译。它们在编译前生效,不是程序运行时的 if/else。
下面的圆形面积计算器有两种构建模式:调试版显示半径和中间结果,正式版只显示用户需要的面积。修改 DEBUG 后重新编译,就能切换输出。
#include <iomanip>
#include <iostream>
#define PI 3.1415926
#define DEBUG 1
int main() {
double radius = 3.0;
double area = PI * radius * radius;
std::cout << std::fixed << std::setprecision(2);
#if DEBUG
std::cout << "[DEBUG] radius = " << radius << ", area = " << area << '\n';
#else
std::cout << "Area: " << area << '\n';
#endif
return 0;
}DEBUG 为 1 时,预处理器保留 #if 分支,输出 [DEBUG] radius = 3.00, area = 28.27;改为 0 并重新编译后,只保留 #else 分支,输出 Area: 28.27。未选中的分支不会进入编译结果。
#ifdef 和 #ifndef 则检查宏是否已定义;下一节会用 #ifndef 保护头文件。
知识测验
题目
当 DEBUG 为 0 时,#if DEBUG 中的代码会怎样?
动手练习
可切换调试的圆形计算器
编写一个可切换调试信息的圆形计算器。
- 定义 PI、SQUARE(x) 和 DEBUG。
- 读取半径,计算面积以及直径的平方。
- 仅当 DEBUG 为 1 时输出原始半径。
- 把 DEBUG 改成 0,确认调试输出消失。