预处理器

翻译整理自 cppreference - 预处理。

预处理器在编译前对源码做文本替换,主要指令以 # 开头。

1. 文件包含 #include

#include <stdio.h>            // 系统头文件
#include "myheader.h"         // 自定义头文件(先在当前目录找)

实现:把被包含文件内容文本插入当前文件。

2. 宏定义 #define

简单宏

#define PI 3.14159
#define MAX_LEN 256

带参数的宏

#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int y = SQUARE(3);            // 9
int z = MAX(1 + 1, 3);        // 3

宏中的参数与整体结果都应加括号,避免运算优先级问题。

多行宏

#define LOG(fmt, ...) \
    do { \
        fprintf(stderr, fmt, __VA_ARGS__); \
    } while (0)

__VA_ARGS__ 代表 ... 中的实参(C99)。

3. 取消宏 #undef

#define PI 3.14
#undef PI
#define PI 3.14159

4. 条件编译

#ifdef DEBUG
    printf("debug mode\n");
#endif

#ifndef VERSION
#define VERSION "1.0"
#endif

#if defined(_WIN32) || defined(_WIN64)
    // Windows
#elif defined(__linux__)
    // Linux
#else
    // Other
#endif

#if __STDC_VERSION__ >= 201112L
    // C11
#endif

5. 预定义宏

宏 含义
__FILE__ 当前源文件名
__LINE__ 当前行号
__DATE__ 编译日期
__TIME__ 编译时间
__func__ 当前函数名(C99+,非宏)
__STDC__ 是否遵循标准
__STDC_VERSION__ 标准 C 版本号
fprintf(stderr, "%s:%d %s: error\n", __FILE__, __LINE__, __func__);

6. #pragma 指令

#pragma once                    // 头文件只包含一次
#pragma pack(push, 1)           // 1 字节对齐
struct Hdr { short len; int type; };
#pragma pack(pop)

#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused"
#endif

7. _Pragma 运算符(C99)

等价于 #pragma,但可用于宏中:

_Pragma("pack(push, 1)")

8. 头文件写法

// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H

int add(int, int);

#endif // MYHEADER_H

#pragma once 是非标准但广泛支持的简化方案:

#pragma once
int add(int, int);

9. 宏 vs 函数

特性 宏 函数
类型 无类型 有类型
求值 多次(如 MAX(i++, j++)) 一次
调用开销 无(直接替换) 有
调试 难(无符号) 易

10. 字符串化与拼接

#define STR(x) #x
#define XSTR(x) STR(x)

STR(hello)            // "hello"
#define VERSION 100
XSTR(VERSION)         // "100"

#define CAT(a, b) a##b
CAT(foo, bar)         // foobar

11. 防止重复包含

// classic
#ifndef _MY_H_
#define _MY_H_
...
#endif

12. 常用预定义平台宏

#ifdef _WIN32
    // Windows
#elif defined(__APPLE__)
    // macOS / iOS
#elif defined(__ANDROID__)
    // Android
#elif defined(__linux__)
    // Linux
#endif

小结

预处理器是 C 的"代码生成"工具,用得好能极大减少重复代码,但滥用会降低可读性,建议优先用 const、enum、inline 替代宏。