1. 预处理概述
1.1 什么是预处理?
预处理是编译过程的第一个阶段,在正式编译之前对源代码进行处理。预处理器读取源代码,执行以 # 开头的指令,生成纯净的 C 代码供编译器处理。
本文详细介绍了 C 语言预处理器的功能与指令。涵盖文件包含、宏定义(对象宏与函数宏)、条件编译、预定义宏及#pragma 指令。重点讲解了防止头文件重复包含的方法、宏参数括号规范、多语句宏的 do-while(0) 包装技巧,以及调试代码控制、跨平台开发和功能特性开关等应用场景。最后总结了常见陷阱与最佳实践,帮助开发者安全高效地使用预处理特性。

预处理是编译过程的第一个阶段,在正式编译之前对源代码进行处理。预处理器读取源代码,执行以 # 开头的指令,生成纯净的 C 代码供编译器处理。
源文件 (.c) → 预处理器 → 预处理后文件 (.i) → 编译器 → 目标文件 (.o)
↑
处理 #include
#define #if 等
| 功能 | 指令 | 说明 |
|---|---|---|
| 文件包含 | #include | 插入头文件内容 |
| 宏定义 | #define | 定义符号常量或宏函数 |
| 条件编译 | #if #ifdef #ifndef | 根据条件编译代码 |
| 错误处理 | #error | 产生编译错误 |
| 行号控制 | #line | 修改行号和文件名 |
| 编译器指令 | #pragma | 编译器特定功能 |
// 形式 1:尖括号 - 搜索系统头文件路径
#include <stdio.h>
#include <stdlib.h>
// 形式 2:双引号 - 先搜索当前目录,再搜索系统路径
#include "myheader.h"
#include "utils/config.h"
// 搜索路径示例(Linux)
// 系统路径:/usr/include, /usr/local/include
// 可通过 -I 选项添加自定义路径:gcc -I./include program.c
// 方法 1:使用条件编译(推荐)
#ifndef MYHEADER_H
#define MYHEADER_H
// 头文件内容
void func(void);
#define VALUE 100
#endif
// 方法 2:使用 #pragma once(非标准但广泛支持)
#pragma once
// 头文件内容
void func(void);
#define VALUE 100
// 良好的头文件结构
#ifndef LIBRARY_H
#define LIBRARY_H
// 1. 包含必要的其他头文件
#include <stdio.h>
#include <stddef.h>
// 2. 宏定义
#define LIB_VERSION "1.0.0"
#define MAX_BUFFER 1024
// 3. 类型定义
typedef struct {
int id;
char name[50];
} Library_t;
// 4. 函数声明
void library_init(void);
void library_cleanup(void);
// 5. 内联函数(可选)
static inline int max(int a, int b) {
return a > b ? a : b;
}
#endif
// 定义常量
#define PI 3.14159
#define MAX_SIZE 100
#define FILE_PATH "/usr/local/config"
// 定义字符常量
#define NEWLINE '\n'
#define TAB '\t'
// 定义字符串
#define APP_NAME "MyApplication"
#define VERSION "1.0.0"
// 使用示例
double area = PI * radius * radius;
int arr[MAX_SIZE];
FILE *fp = fopen(FILE_PATH, "r");
// 基本用法
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
// 使用示例
int result = SQUARE(5); // 展开为 ((5) * (5))
int bigger = MAX(10, 20); // 展开为 ((10) > (20) ? (10) : (20))
// 多语句宏
#define LOG_ERROR(msg) do { \
fprintf(stderr, "Error: %s at %s:%d\n", msg, __FILE__, __LINE__); \
exit(1); \
} while(0)
// 可变参数宏(C99)
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG] " fmt, __VA_ARGS__)
#define ERROR_PRINT(fmt, ...) \
fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
// 使用 DEBUG_PRINT("x = %d, y = %d\n", x, y);
// ERROR_PRINT("Invalid value: %d", value);
// ⚠️ 重要:宏参数要加括号
#define BAD_SQUARE(x) x * x // 错误!
// SQUARE(1+2) 展开为 1+2*1+2 = 5,期望 9
#define GOOD_SQUARE(x) ((x) * (x)) // 正确
// 宏的续行符 \
#define LONG_MACRO(x, y) \
do { \
int temp = (x) + (y); \
printf("Result: %d\n", temp); \
} while(0)
// 字符串化操作符 #
#define STR(x) #x
#define STRING(x) STR(x)
printf("%s\n", STR(Hello)); // 输出:Hello
printf("%s\n", STR(MAX_SIZE)); // 输出:MAX_SIZE
printf("%s\n", STRING(MAX_SIZE)); // 输出:100(如果 MAX_SIZE=100)
// 连接操作符 ##
#define CONCAT(a, b) a##b
#define VAR(name, num) name##num
int var1 = 10, var2 = 20;
printf("%d\n", CONCAT(var, 1)); // 输出:10
printf("%d\n", VAR(var, 2)); // 输出:20
#include <stdio.h>
int main() {
// 标准预定义宏
printf("文件:%s\n", __FILE__);
printf("行号:%d\n", __LINE__);
printf("函数:%s\n", __func__); // C99
printf("编译日期:%s\n", __DATE__);
printf("编译时间:%s\n", __TIME__);
// C99 新增
printf("是否严格遵循标准:%d\n", __STDC__);
printf("C 版本:%ld\n", __STDC_VERSION__);
// 编译器特定(GCC)
printf("编译器版本:%s\n", __VERSION__);
return 0;
}
// 基本条件编译
#define DEBUG_LEVEL 2
#if DEBUG_LEVEL >= 3
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG3] " fmt, __VA_ARGS__)
#elif DEBUG_LEVEL >= 2
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG2] " fmt, __VA_ARGS__)
#elif DEBUG_LEVEL >= 1
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG1] " fmt, __VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
// 平台适配
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM "Windows"
#define PATH_SEP '\\'
#elif defined(__linux__)
#define PLATFORM "Linux"
#define PATH_SEP '/'
#elif defined(__APPLE__)
#define PLATFORM "macOS"
#define PATH_SEP '/'
#else
#define PLATFORM "Unknown"
#define PATH_SEP '/'
#endif
// 头文件保护
#ifndef MYHEADER_H
#define MYHEADER_H
// 头文件内容
#endif
// 调试开关
#define DEBUG
#ifdef DEBUG
#define DEBUG_MSG(msg) printf("[DEBUG] %s\n", msg)
#else
#define DEBUG_MSG(msg)
#endif
// 特性检测
#ifndef __cplusplus
// C 语言特定代码
typedef struct {
int x;
int y;
} Point;
#endif
// 防止重复定义
#ifndef MAX
#define MAX 100
#endif
// 组合条件
#if defined(DEBUG) && defined(VERBOSE)
#define LOG(msg) printf("[LOG] %s\n", msg)
#elif defined(DEBUG)
#define LOG(msg) printf("[DBG] %s\n", msg)
#else
#define LOG(msg)
#endif
// 检查多个宏
#if defined(USE_OPENGL) || defined(USE_VULKAN)
#define USE_GRAPHICS
#endif
// 复杂条件
#if defined(__GNUC__) && (__GNUC__ >= 4)
#define GCC_VERSION "4.0+"
#endif
// debug.h
#ifndef DEBUG_H
#define DEBUG_H
#include <stdio.h>
// 调试级别
#define DEBUG_NONE 0
#define DEBUG_ERROR 1
#define DEBUG_WARN 2
#define DEBUG_INFO 3
#define DEBUG_TRACE 4
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL DEBUG_NONE
#endif
// 调试宏
#if DEBUG_LEVEL >= DEBUG_ERROR
#define DEBUG_ERROR_PRINT(fmt, ...) \
fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", \
__FILE__, __LINE__, __VA_ARGS__)
#else
#define DEBUG_ERROR_PRINT(fmt, ...)
#endif
#if DEBUG_LEVEL >= DEBUG_WARN
#define DEBUG_WARN_PRINT(fmt, ...) \
fprintf(stderr, "[WARN] %s:%d: " fmt "\n", \
__FILE__, __LINE__, __VA_ARGS__)
#else
#define DEBUG_WARN_PRINT(fmt, ...)
#endif
#if DEBUG_LEVEL >= DEBUG_INFO
#define DEBUG_INFO_PRINT(fmt, ...) \
printf("[INFO] " fmt "\n", __VA_ARGS__)
#else
#define DEBUG_INFO_PRINT(fmt, ...)
#endif
// 断言宏
#ifdef DEBUG
#define ASSERT(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "Assertion failed: %s at %s:%d\n", \
#cond, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
#else
#define ASSERT(cond)
#endif
#endif
// platform.h
#ifndef PLATFORM_H
#define PLATFORM_H
// 检测操作系统
#if defined(_WIN32) || defined(_WIN64)
#define OS_WINDOWS 1
#define OS_NAME "Windows"
#include <windows.h>
#elif defined(__linux__)
#define OS_LINUX 1
#define OS_NAME "Linux"
#include <unistd.h>
#include <pthread.h>
#elif defined(__APPLE__)
#define OS_MACOS 1
#define OS_NAME "macOS"
#include <unistd.h>
#else
#define OS_UNKNOWN 1
#define OS_NAME "Unknown"
#endif
// 编译器检测
#if defined(__GNUC__)
#define COMPILER_GCC 1
#define COMPILER_NAME "GCC"
#define COMPILER_VERSION __VERSION__
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#define COMPILER_NAME "MSVC"
#define COMPILER_VERSION _MSC_VER
#endif
// 导出符号(动态库)
#if defined(_WIN32)
#ifdef BUILDING_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#else
#define DLL_EXPORT __attribute__((visibility("default")))
#endif
// 线程本地存储
#if defined(_WIN32)
#define THREAD_LOCAL __declspec(thread)
#elif defined(__GNUC__)
#define THREAD_LOCAL __thread
#endif
// 内联函数
#ifdef _MSC_VER
#define INLINE __forceinline
#else
#define INLINE inline __attribute__((always_inline))
#endif
#endif
// features.h
#ifndef FEATURES_H
#define FEATURES_H
// 功能开关
#define ENABLE_CACHE 1
#define ENABLE_LOGGING 1
#define ENABLE_SSL 0
// 根据开关编译不同代码
#if ENABLE_CACHE
typedef struct {
void *data;
size_t size;
} Cache_t;
void cache_init(void);
void cache_set(const char *key, void *data);
void *cache_get(const char *key);
#else
#define cache_init()
#define cache_set(key, data)
#define cache_get(key) NULL
#endif
// 版本控制
#define VERSION_MAJOR 2
#define VERSION_MINOR 1
#define VERSION_PATCH 0
#if VERSION_MAJOR >= 2
#define USE_NEW_API 1
#endif
#endif
// 强制要求定义某个宏
#ifndef CONFIG_FILE
#error "CONFIG_FILE must be defined"
#endif
// 版本检查
#if __STDC_VERSION__ < 199901L
#error "C99 or later is required"
#endif
// 平台检查
#if defined(_WIN32) && !defined(_WIN64)
#error "32-bit Windows is not supported"
#endif
// 配置检查
#if MAX_BUFFER_SIZE < 1024
#error "MAX_BUFFER_SIZE must be at least 1024"
#endif
// 重置行号和文件名
#line 100 "custom_file.c"
// 后续代码行号从 100 开始
int main() {
// 这里的行号是 101
printf("Line: %d\n", __LINE__); // 输出 101
return 0;
}
// 1. 防止头文件重复包含(MSVC)
#pragma once
// 2. 关闭特定警告(MSVC)
#pragma warning(disable: 4996) // 禁用不安全函数警告
// 3. 设置结构体对齐
#pragma pack(push, 1) // 1 字节对齐
typedef struct {
char c;
int i;
} PackedStruct;
#pragma pack(pop)
// 4. 消息提示
#pragma message("Compiling main module...")
// 5. 链接库(MSVC)
#pragma comment(lib, "winmm.lib")
// 6. 优化设置(GCC)
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
// 跨平台的 #pragma 使用
#if defined(_MSC_VER)
#pragma warning(disable: 4996)
#define ALIGN(n) __declspec(align(n))
#elif defined(__GNUC__)
#define ALIGN(n) __attribute__((aligned(n)))
#else
#define ALIGN(n)
#endif
// 使用示例
ALIGN(16) int vector[4];
// logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <stdio.h>
#include <time.h>
// 日志级别
#define LOG_LEVEL_DEBUG 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_WARN 2
#define LOG_LEVEL_ERROR 3
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_LEVEL_INFO
#endif
// 获取时间戳
#define TIMESTAMP() \
do { \
time_t t = time(NULL); \
struct tm *tm = localtime(&t); \
printf("[%04d-%02d-%02d %02d:%02d:%02d] ", \
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, \
tm->tm_hour, tm->tm_min, tm->tm_sec); \
} while(0)
// 日志宏
#if LOG_LEVEL <= LOG_LEVEL_DEBUG
#define LOG_DEBUG(fmt, ...) \
do { \
printf("[DEBUG] %s:%d: ", __FILE__, __LINE__); \
printf(fmt, __VA_ARGS__); \
printf("\n"); \
} while(0)
#else
#define LOG_DEBUG(fmt, ...)
#endif
#if LOG_LEVEL <= LOG_LEVEL_INFO
#define LOG_INFO(fmt, ...) \
do { \
TIMESTAMP(); \
printf("[INFO] " fmt "\n", __VA_ARGS__); \
} while(0)
#else
#define LOG_INFO(fmt, ...)
#endif
#if LOG_LEVEL <= LOG_LEVEL_WARN
#define LOG_WARN(fmt, ...) \
do { \
fprintf(stderr, "[WARN] %s:%d: " fmt "\n", \
__FILE__, __LINE__, __VA_ARGS__); \
} while(0)
#else
#define LOG_WARN(fmt, ...)
#endif
#if LOG_LEVEL <= LOG_LEVEL_ERROR
#define LOG_ERROR(fmt, ...) \
do { \
fprintf(stderr, "[ERROR] %s:%d: " fmt "\n", \
__FILE__, __LINE__, __VA_ARGS__); \
} while(0)
#else
#define LOG_ERROR(fmt, ...)
#endif
#endif
// vector.h - 动态数组模板
#define VECTOR_DECLARE(type, name) \
typedef struct { \
type *data; \
size_t size; \
size_t capacity; \
} name##_t; \
\
name##_t* name##_create(void); \
void name##_destroy(name##_t *vec); \
int name##_push(name##_t *vec, type value); \
type name##_get(name##_t *vec, size_t index);
#define VECTOR_IMPLEMENT(type, name) \
name##_t* name##_create(void) { \
name##_t *vec = malloc(sizeof(name##_t)); \
if (!vec) return NULL; \
vec->capacity = 4; \
vec->size = 0; \
vec->data = malloc(vec->capacity * sizeof(type)); \
return vec; \
} \
\
void name##_destroy(name##_t *vec) { \
if (vec) { \
free(vec->data); \
free(vec); \
} \
} \
\
int name##_push(name##_t *vec, type value) { \
if (vec->size >= vec->capacity) { \
vec->capacity *= 2; \
type *new_data = realloc(vec->data, vec->capacity * sizeof(type)); \
if (!new_data) return 0; \
vec->data = new_data; \
} \
vec->data[vec->size++] = value; \
return 1; \
} \
\
type name##_get(name##_t *vec, size_t index) { \
return vec->data[index]; \
}
// 使用示例
VECTOR_DECLARE(int, IntVector)
VECTOR_IMPLEMENT(int, IntVector)
int main() {
IntVector_t *vec = IntVector_create();
IntVector_push(vec, 10);
IntVector_push(vec, 20);
printf("%d\n", IntVector_get(vec, 0));
IntVector_destroy(vec);
return 0;
}
// error.h
#ifndef ERROR_H
#define ERROR_H
#include <stdio.h>
#include <stdlib.h>
// 错误码
typedef enum {
ERROR_SUCCESS = 0,
ERROR_NULL_PTR,
ERROR_INVALID_PARAM,
ERROR_MEMORY,
ERROR_FILE_IO,
ERROR_UNKNOWN
} ErrorCode;
// 错误处理宏
#define CHECK_NULL(ptr) \
do { \
if ((ptr) == NULL) { \
fprintf(stderr, "NULL pointer at %s:%d\n", __FILE__, __LINE__); \
return ERROR_NULL_PTR; \
} \
} while(0)
#define CHECK_COND(cond, error_code) \
do { \
if (!(cond)) { \
fprintf(stderr, "Condition failed: %s at %s:%d\n", \
#cond, __FILE__, __LINE__); \
return (error_code); \
} \
} while(0)
#define TRY(expr) \
do { \
int _err = (expr); \
if (_err != ERROR_SUCCESS) { \
fprintf(stderr, "Error %d at %s:%d\n", _err, __FILE__, __LINE__); \
return _err; \
} \
} while(0)
#define RETURN_IF_ERROR(expr) \
do { \
int _err = (expr); \
if (_err != ERROR_SUCCESS) return _err; \
} while(0)
#define ASSERT(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "Assertion failed: %s at %s:%d\n", \
#cond, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
#endif
// 陷阱 1:缺少括号
#define BAD_SQUARE(x) x * x
int result = BAD_SQUARE(1+2); // 1+2*1+2 = 5,期望 9
// 陷阱 2:多次求值
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = 1, y = 2;
int max = MAX(x++, y++); // x++ 被求值两次!
// 实际:((x++) > (y++) ? (x++) : (y++)) → x=2, y=4
// 陷阱 3:分号问题
#define LOG(msg) printf("%s\n", msg)
if (condition)
LOG("message"); // 正常
else
LOG("error"); // 如果宏定义末尾有分号,这里会出错
// 陷阱 4:宏名与函数名冲突
#define max(a, b) ((a) > (b) ? (a) : (b))
// 可能覆盖标准库的 max 函数
// 正确做法:宏名使用大写
#define MAX(a, b) ((a) > (b) ? (a) : (b))
// ✅ 1. 宏名使用大写字母
#define BUFFER_SIZE 1024
// ✅ 2. 宏参数和整个表达式加括号
#define SQUARE(x) ((x) * (x))
// ✅ 3. 多语句宏使用 do-while(0) 包装
#define SWAP(a, b) \
do { \
int temp = (a); \
(a) = (b); \
(b) = temp; \
} while(0)
// ✅ 4. 使用 #undef 清理不再使用的宏
#define TEMP_VALUE 100
// ... 使用后
#undef TEMP_VALUE
// ✅ 5. 调试宏只在开发时定义
#ifdef DEBUG
#define ASSERT(cond) /* 断言实现 */
#else
#define ASSERT(cond)
#endif
// ✅ 6. 避免重复定义,使用 #ifndef 保护
#ifndef MAX_BUFFER
#define MAX_BUFFER 4096
#endif
| 指令 | 功能 | 示例 |
|---|---|---|
| #include | 包含文件 | #include <stdio.h> |
| #define | 定义宏 | #define PI 3.14 |
| #undef | 取消定义 | #undef PI |
| #if | 条件判断 | #if DEBUG |
| #ifdef | 如果定义 | #ifdef _WIN32 |
| #ifndef | 如果未定义 | #ifndef HEADER_H |
| #else | 否则 | #else |
| #elif | 否则如果 | #elif defined(linux) |
| #endif | 结束条件 | #endif |
| #error | 产生错误 | #error "Not supported" |
| #line | 修改行号 | #line 100 "file.c" |
| #pragma | 编译器指令 | #pragma once |
记住:预处理是 C 语言的强大特性,但要谨慎使用。清晰的代码结构往往比精巧的宏更重要!

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online