简短不看版:
关键原则
-
预处理器只认识
#define宏-
在需要计算的地方(
#if,#elif):-
所有标识符必须有数值
-
未定义的标识符被当作0
-
-
-
枚举类型在编译阶段才被处理
-
在条件编译 (
#if,#elif) 中必须使用宏定义,不能使用枚举值 -
在纯文本替换的地方(函数参数、代码体):
-
预处理器只做简单的字符串替换
-
替换后的文本由编译器处理
-
如果替换后语法错误,由编译器报错
-
C/C++ 的条件编译和简单的文本替换不同,
例如:形如#if (CHOSEN_DEFINE == FISRT_DEFINE_CHOICE) 这样的条件编译,在预处理(编译的第一步)的时候,就必须给出结果。
如果FISRT_DEFINE_CHOICE是未定义的,或者是 enum 类型的成员——那么属于在预处理阶段,预处理器“不认识”的,统一将其处理为 0
例如,有如下形式案例
typedef enum
{ZERO_DEFINE_CHOICE = 0,FISRT_DEFINE_CHOICE = 1,
}enum_choice_t;
但是下面的情形,如果应用上面的代码:是不会按人的想法正常工作的
#define CHOSEN_DEFINE (FISRT_DEFINE_CHOICE)#if (CHOSEN_DEFINE == ZERO_DEFINE_CHOICE)/*实际编译包含这里*/
#elif (CHOSEN_DEFINE == FISRT_DEFINE_CHOICE) //尽管人想让这里执行,但是预处理器"不认识"FISRT_DEFINE_CHOICE, 将其强制化为0/*不会编译包含这里*/
#endif
解决办法,不使用 enum 枚举,直接使用宏:
#define ZERO_DEFINE_CHOICE (0)
#define FISRT_DEFINE_CHOICE (1)
正文
预处理器做的不是字符替换吗?它把CURRENT_AUDIO_FORMAT 替换为AUDIO_FORMAT_RAW_PCM,后续再表示为2不就行了,为啥会把它当作0 处理
预处理器的工作方式
预处理器确实是做字符替换,但关键在于:预处理器不认识枚举常量。
具体替换过程:
-
第一次替换:
// 原始
#if (CURRENT_AUDIO_FORMAT == AUDIO_FORMAT_RAW_PCM)// 替换 CURRENT_AUDIO_FORMAT
#if (AUDIO_FORMAT_RAW_PCM == AUDIO_FORMAT_RAW_PCM) //为真
#if (AUDIO_FORMAT_RAW_PCM == ANOTHER_FORMAT_RAW_PCM) //也为真,因为 ANOTHER_FORMAT_RAW_PCM 未定义,强制变为0,//AUDIO_FORMAT_RAW_PCM被定义 AUDIO_FORMAT_RAW_PCM,但AUDIO_FORMAT_RAW_PCM未定义,因此也为0
-
问题出现:
现在预处理器需要处理AUDIO_FORMAT_RAW_PCM == AUDIO_FORMAT_RAW_PCM,但:
-
AUDIO_FORMAT_RAW_PCM是枚举常量,不是#define宏 -
预处理器找不到
AUDIO_FORMAT_RAW_PCM的定义 -
根据C标准,未定义的标识符在
#if中被当作 0
所以实际变成:
#if (0 == 0) // 条件为真
核心规则总结
-
在需要计算的地方(
#if,#elif):-
所有标识符必须有数值
-
未定义的标识符被当作0
-
-
在纯文本替换的地方(函数参数、代码体):
-
预处理器只做简单的字符串替换
-
替换后的文本由编译器处理
-
如果替换后语法错误,由编译器报错
-
验证这个问题的简单测试
#include <stdio.h>// 情况1:使用枚举(会出问题)
typedef enum {TEST_VALUE = 100
} test_enum_t;#define CURRENT_TEST TEST_VALUE// 情况2:使用宏定义(正常工作)
#define TEST_MACRO 100
#define CURRENT_TEST_MACRO TEST_MACROint main() {
// 这个条件编译会出错,因为TEST_VALUE在预处理阶段不可见
#if CURRENT_TEST == 100printf("This won't print with enum\n");
#elseprintf("This will print - enum treated as 0\n");
#endif// 这个能正常工作
#if CURRENT_TEST_MACRO == 100 printf("This will print with macro\n");
#endifreturn 0;
}
安全的使用模式
模式1:统一使用宏定义
// 配置值全部用宏定义
#define CFG_FORMAT_SPEEX 0
#define CFG_FORMAT_OPUS 1
#define CFG_FORMAT_RAW_PCM 2#define CURRENT_AUDIO_FORMAT CFG_FORMAT_RAW_PCM// 条件编译安全
#if CURRENT_AUDIO_FORMAT == CFG_FORMAT_RAW_PCM
// 正确工作
#endif// 枚举只用于类型检查
typedef enum {AUDIO_FMT_SPEEX = CFG_FORMAT_SPEEX,AUDIO_FMT_OPUS = CFG_FORMAT_OPUS,AUDIO_FMT_RAW_PCM = CFG_FORMAT_RAW_PCM
} audio_format_t;
模式2:分离配置和类型
// 配置宏(用于条件编译)
#define USING_RAW_PCM 1#if USING_RAW_PCM
#define CURRENT_AUDIO_FORMAT 2
// 直接配置RAW_PCM参数
#endif// 枚举类型(用于代码类型安全)
typedef enum {AUDIO_FORMAT_SPEEX = 0,AUDIO_FORMAT_OPUS = 1,AUDIO_FORMAT_RAW_PCM = 2
} audio_format_t;