当前位置: 首页 > news >正文

【C语言】条件编译时谨慎使用枚举值

简短不看版:

关键原则

  • 预处理器只认识 #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 处理

预处理器的工作方式

预处理器确实是做字符替换,但关键在于:预处理器不认识枚举常量

具体替换过程:

  1. 第一次替换

// 原始
#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
  1. 问题出现
    现在预处理器需要处理 AUDIO_FORMAT_RAW_PCM == AUDIO_FORMAT_RAW_PCM,但:

  • AUDIO_FORMAT_RAW_PCM 是枚举常量,不是 #define 宏

  • 预处理器找不到 AUDIO_FORMAT_RAW_PCM 的定义

  • 根据C标准,未定义的标识符在 #if 中被当作 0

所以实际变成:

#if (0 == 0)  // 条件为真

核心规则总结

  1. 在需要计算的地方#if#elif):

    • 所有标识符必须有数值

    • 未定义的标识符被当作0

  2. 在纯文本替换的地方(函数参数、代码体):

    • 预处理器只做简单的字符串替换

    • 替换后的文本由编译器处理

    • 如果替换后语法错误,由编译器报错

验证这个问题的简单测试

#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:统一使用宏定义

c
// 配置值全部用宏定义
#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:分离配置和类型

c
// 配置宏(用于条件编译)
#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;
http://www.jsqmd.com/news/52124/

相关文章:

  • [源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅
  • 黑马点评完结!
  • 洛谷 P10378:[GESP202403 七级] 交流问题 ← 二分图 + 染色法
  • 2025 YJV电线电缆行业权威榜单:深圳中缆电缆集团——高导电性能与环保创新的领军者
  • hyx_蓝桥杯C++学习_系列一
  • 每日反思(2025年11月26)
  • 手机电池突然掉电?工程师揭秘锂电池保养十大误区,延长续航200%的冷知识!
  • hyx_蓝桥杯C++_学习系列一
  • Proxifier代理游戏加速器
  • Ai元人文:从心所欲不逾矩
  • Markdown常用语法总结
  • 阿里低代码引擎- lowcode-demo运行
  • CSES1448-Maximum Building II
  • 汉明距离相关应用
  • JUC
  • 基因组共线性分析
  • 博弈论笔记
  • Bazaar - 现代化的 GNOME 应用商店
  • 快速排序板子
  • 回文数
  • 黑马程序员SpringCloud微服务开发与实战-微服务05
  • map用法
  • CF1774F2
  • sscanf用法
  • sprintf用法
  • 订单多到做不完?四步把交期、缺料、进度和插单都解决了
  • 八、热插拔
  • 第37天(中等题 数据结构)
  • PostgreSQL权限管理实践
  • 预编译命令