1. 预处理概述
1.1 什么是预处理?
预处理是编译过程的第一个阶段,在正式编译之前对源代码进行处理。预处理器读取源代码,执行以 # 开头的指令,生成纯净的C代码供编译器处理。
源文件(.c) → 预处理器 → 预处理后文件(.i) → 编译器 → 目标文件(.o)
↑
处理 #include
#define #if等
1.2 预处理器的主要功能
功能 指令 说明
文件包含 #include 插入头文件内容
宏定义 #define 定义符号常量或宏函数
条件编译 #if #ifdef #ifndef 根据条件编译代码
错误处理 #error 产生编译错误
行号控制 #line 修改行号和文件名
编译器指令 #pragma 编译器特定功能
2. 文件包含指令
2.1 #include 的两种形式
// 形式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
2.2 防止头文件重复包含
// 方法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
2.3 头文件设计原则
// 良好的头文件结构
#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
3. 宏定义
3.1 简单宏(对象宏)
// 定义常量
#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");
3.2 带参数的宏(函数宏)
// 基本用法
#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);
3.3 宏定义的关键技巧
// ⚠️ 重要:宏参数要加括号
#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
3.4 预定义宏
#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;
}
4. 条件编译
4.1 #if、#elif、#else、#endif
// 基本条件编译
#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
4.2 #ifdef、#ifndef
// 头文件保护
#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
4.3 defined 操作符
// 组合条件
#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
5. 条件编译的应用场景
5.1 调试代码控制
// 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
5.2 跨平台开发
// 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
5.3 功能特性开关
// 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
6. #error 和 #line
6.1 #error 产生编译错误
// 强制要求定义某个宏
#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
6.2 #line 修改行号
// 重置行号和文件名
#line 100 "custom_file.c"
// 后续代码行号从100开始
int main() {
// 这里的行号是101
printf("Line: %d\n", LINE); // 输出 101
return 0;
}
7. #pragma 指令
7.1 常见 #pragma 用法
// 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")
7.2 编译器检测与 #pragma 兼容
// 跨平台的 #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];
8. 宏的高级应用
8.1 日志系统
// 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
8.2 通用数据结构宏
// 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;
}
8.3 错误处理宏
// 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
9. 常见陷阱与最佳实践
9.1 宏的常见陷阱
// 陷阱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))
9.2 最佳实践总结
// ✅ 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
10. 总结
10.1 预处理指令速查表
指令 功能 示例
#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
10.2 核心要点
预处理在编译之前执行,不进行语法检查
宏是文本替换,理解替换规则避免陷阱
条件编译用于调试、跨平台、功能开关
头文件保护防止重复包含
合理使用宏可以提高代码可维护性,但过度使用会降低可读性
记住:预处理是C语言的强大特性,但要谨慎使用。清晰的代码结构往往比精巧的宏更重要!