
在嵌入式C/C++开发中,预处理阶段虽然不参与真正的编译和链接,但却是面试中出现频率极高、同时又最容易被忽略的一部分。很多看似"诡异"的Bug,本质都源于对预处理机制理解不清。
1 什么是预处理?它在编译流程中的位置?
C/C++程序的构建流程如下:
bash
预处理 → 编译 → 汇编 → 链接
预处理阶段主要完成:
- 宏替换(
#define) - 条件编译(
#ifdef/#ifndef/#if) - 头文件展开(
#include) - 生成最终送入编译器的源码
⚠️重点:预处理不做语法检查,只是"机械式文本替换"
2 #error预处理指令的作用?
2.1 #error的作用?
#error 用于在预处理阶段主动制造一个编译错误,并输出自定义错误信息,编译会立即终止。
语法:
cs
#error error-message
2.2 使用场景:检测宏是否被定义?
在大型嵌入式工程中:
- 宏可能来自 Makefile
- 也可能来自系统头文件
- 有时你并不确定某个宏是否已经定义
示例:
cs
#ifdef XXX
#error "XXX has been defined"
#else
// normal code
#endif
👉 如果编译报错并输出 XXX has been defined,说明宏 XXX 已存在
3 定义常量:#define还是const?
一句话总结:
能用
const,就不用#define
3.1 本质区别?
| 对比项 | #define | const |
| 处理阶段 | 预处理 | 编译期 |
| 原理 | 文本替换 | 真正的变量 |
| 是否分配内存 | ❌ | ✅ |
| 类型检查 | ❌ | ✅ |
| 调试支持 | ❌ | ✅ |
|---|
3.2 深度理解
cs
#define MAX 100
const int max = 100;
MAX:- 只是一个符号替换
- 编译后根本不存在
max:- 有类型
- 有地址
- 可以被调试器看到
👉 嵌入式开发中推荐使用 const 定义常量
4 typedef和#define的区别?
4.1 原理不同
-
#define👉 预处理阶段,纯文本替换,不做任何检查
-
typedef👉 编译阶段,有完整类型系统支持
示例:
cs
#define PI 3.1415926
typedef int INTEGER;
4.2 功能不同?
typedef:- 给类型起别名
- 支持 struct / pointer / function pointer
#define:- 常量
- 宏函数
- 编译开关
📌 typedef 的重要用途之一:定义机器无关类型
cs
typedef long double REAL;
4.3 作用域不同?
cs
void fun()
{
#define A int
}
void gun()
{
A x; // 合法!宏没有作用域
}
⚠️ typedef 有作用域,宏没有
4.4 指针问题?
cs
#define INTPTR1 int*
typedef int* INTPTR2;
INTPTR1 p1, p2; // int* p1, p2 → p2是int
INTPTR2 p3, p4; // p3、p4 都是 int*
👉 原因:
#define是"拆开替换"typedef是"整体类型"
4.5 const + typedef 的陷阱?
cs
const INTPTR1 p1 = &a; // 指向常量的指针
const INTPTR2 p2 = &b; // 指针常量
📌 面试总结口诀:
typedef定义的是"类型",const修饰的是"整个类型"
5 经典宏题:一年有多少秒?
cs
#define SECOND_PER_YEAR (60 * 60 * 24 * 365UL)
📌 考点:
- 宏要加括号
- 使用
UL防止整型溢出
6 #include <>与 #include ""的区别?
| 写法 | 搜索路径 |
| <file.h> | 先系统目录 |
"file.h" |
先当前目录,再系统目录 |
|---|
👉 面试标准回答:
- 系统头文件用
< > - 自定义头文件用
" "
7 头文件的作用?
-
①提供接口,隐藏实现
- 只暴露函数声明
- 源码不必给用户
-
②增强类型安全
- 编译期检查函数参数、返回值
- 极大降低调试成本
8 为什么不能在头文件中定义静态变量?
❌ 不推荐,原因如下:
- 每个
.c文件都会生成一份 - 造成 资源浪费
- 可能引发 逻辑错误
👉 原则:
头文件只放声明,不放定义
9 标准宏MIN?
cs
#define MIN(A, B) ((A) <= (B) ? (A) : (B))
📌 面试延伸:
- 为什么要加括号?
- 宏参数有副作用怎么办?
10 不使用流程控制打印 1~1000?
方法一:宏嵌套
cs
#define I printf("%3d", i++)
#define N printf("\n")
#define L I,I,I,I,I,I,I,I,I,I,N
#define P L,L,L,L,L,L,L,L,L,L
#define B P,P,P,P,P,P,P,P,P,P
方法二:递归宏(简写)
cs
#define A(x) x;x;x;x;x;x;x;x;x;x
A(A(A(printf("%d ", n++))))
📌 考察点:
- 宏展开
- 预处理理解深度
11 总结
#define是文本替换,typedef是类型别名const比#define更安全- 宏没有作用域,typedef 有
- 指针 + typedef 是面试重灾区
- 头文件只放声明,不放定义