大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
预处理器
编译前,预处理器处理 #include、#define 与条件编译。对照 /roadmaps/c,读懂头文件守卫与跨平台宏是维护 C 项目的必备技能。
学习目标
- 理解
#include与头文件组织 - 编写带参数的宏并知道其局限
- 使用
#if、#ifdef做条件编译 - 区分宏与
const、内联函数
核心概念
宏与条件编译
/* config.h */
#ifndef CONFIG_H
#define CONFIG_H
#define VERSION "1.0.0"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define LOG(fmt, ...) printf("[LOG] " fmt "\n", ##__VA_ARGS__)
#if defined(_WIN32)
# define PATH_SEP '\\'
#else
# define PATH_SEP '/'
#endif
#ifdef DEBUG
# define DBG(msg) fprintf(stderr, "DEBUG: %s\n", msg)
#else
# define DBG(msg) ((void)0)
#endif
#endif /* CONFIG_H */
/* main.c */
#include <stdio.h>
#include "config.h"
static int square(int x) {
return x * x;
}
int main(void) {
LOG("version=%s sep=%c", VERSION, PATH_SEP);
DBG("entering main");
int a = 3, b = 7;
printf("MAX=%d square=%d\n", MAX(a, b), square(a));
/* 宏陷阱:多次求值 */
int i = 5;
printf("MAX(i++, 10)=%d i=%d\n", MAX(i++, 10), i);
#if 0
puts("disabled block");
#endif
return 0;
}
最佳实践
| 做法 | 说明 |
|---|---|
| 头文件守卫 | #ifndef / #define / #endif 防重复包含 |
| 宏参数加括号 | #define SQR(x) ((x)*(x)) |
| 复杂逻辑 | 用 static inline 函数代替宏 |
| 平台差异 | #ifdef _WIN32 等集中管理 |
#include "local.h" 先搜当前目录;#include <stdio.h> 搜系统路径。
与 C++/Java 对照
| 特性 | C 预处理器 | C++ | Java |
|---|---|---|---|
| 文本替换 | #define 宏 | 有 + 模板/constexpr | 无 |
| 条件编译 | #ifdef | 有 | 无(靠 profile/模块) |
| 头文件 | #include 展开 | 同左 + #pragma once | import / module |
| 常量 | 宏或 const | constexpr | final static |
| 泛型 | 宏模拟 | 模板 | 泛型类 |
Java 无预处理器;配置靠构建工具与注解。C 宏强大但缺类型检查,现代 C 倾向 const + 内联函数。
练习
- 为自定义头文件
math_utils.h添加 include guard - 写宏
ARRAY_SIZE(arr)计算数组元素个数 - 用
#ifdef DEBUG切换日志输出 - 说明
MAX(i++, 10)为何可能出错,并给出更安全写法
小结
预处理器在编译前改写源码;宏慎用,头文件守卫必用。 下一课学习编译与构建。