C 语言 enum
更新时间:2026-08-26。本文是
languages/c/主题入门层第 49 篇。程序里到处是"几个固定取值":星期一到星期日、成功/失败/超时、红黄绿。用魔法数字写(if (status == 3))没人看得懂;enum给每个取值起名字,代码自己解释自己。
本文要回答的问题
enum怎么写?值怎么编号?- 它和
#define常量有什么区别? - 状态机、错误码场景里怎么用?
一、基本语法
c
enum Weekday {
MON, TUE, WED, THU, FRI, SAT, SUN
};
enum Weekday today = WED;
printf("%d\n", today); // 2(从 0 开始编号)默认从 0 递增:MON=0, TUE=1, ..., SUN=6。枚举名是常量,直接用,不用加前缀(WED 就是 WED,不是 Weekday.WED——C 里没有这种写法,这点和 Java/Python 不同)。
二、手动指定值
默认编号不够用,可以手动给:
c
enum Status {
OK = 0,
ERROR_IO = 10, // 显式指定
ERROR_MEM, // 自动 = 11
ERROR_TIMEOUT = 20,
ERROR_UNKNOWN = -1 // 允许负值
};规律:没写值的,取上一个值 +1。所以 ERROR_MEM = 11。
也可以让一组枚举跟某个常量对齐:
c
enum Flags {
FLAG_NONE = 0x00,
FLAG_READ = 0x01, // 位掩码写法,配合位运算
FLAG_WRITE = 0x02,
FLAG_EXEC = 0x04
};三、enum vs #define
很多人问:#define OK 0 不就行了,为什么用 enum?
| 对比项 | #define | enum |
|---|---|---|
| 本质 | 预处理文本替换 | 编译期常量(int 类型) |
| 类型检查 | 无 | 有(debugger 能看到名字) |
| 作用域 | 全局,易污染 | 受作用域限制 |
| 调试体验 | 值显示为数字 | gdb 显示枚举名 |
| 自动编号 | 无 | 有(+1 递增) |
经验:一组相关的、有名字的整型常量用 enum;单个散落的常量用 #define 或 const。
四、实战:状态机
enum 是状态机的天然燃料——每个状态一个名字,switch 分支清晰可读:
c
typedef enum {
ST_IDLE,
ST_RUNNING,
ST_PAUSED,
ST_STOPPED
} State;
State state = ST_IDLE;
void handle_event(int event) {
switch (state) {
case ST_IDLE:
if (event == 1) state = ST_RUNNING;
break;
case ST_RUNNING:
if (event == 2) state = ST_PAUSED;
break;
case ST_PAUSED:
if (event == 3) state = ST_RUNNING;
if (event == 4) state = ST_STOPPED;
break;
default:
break;
}
}比起 switch (state) { case 0: ...,每个状态有名字,读代码像读需求文档。完整版见状态机实践。
五、实战:错误码
返回错误码时,enum 让调用方一眼看懂:
c
typedef enum {
ERR_OK = 0,
ERR_INVALID_ARG = 1,
ERR_NOT_FOUND = 2,
ERR_TIMEOUT = 3,
ERR_UNKNOWN = 99
} ErrorCode;
ErrorCode parse_config(const char *path) {
if (path == NULL) return ERR_INVALID_ARG;
// ...
return ERR_OK;
}配合 switch 处理错误,比用魔法数字健壮得多。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 忘了 C 里枚举是 int | 值类型检查宽松 | 用它当类型标签而非精确类型 |
| 多个 enum 用同名常量 | 重名冲突 | 加前缀(如 ST_、ERR_) |
| 默认从 0 开始但逻辑以为 1 | 偏移错误 | 显式赋值或查文档 |
| switch 少写一个 case | 漏状态 | 编译选项 -Wswitch-enum / 加 default |
| 把枚举名当字符串输出 | 打印出数字 | 自写查表函数 |
七、与本站主线衔接
一句话总结
enum 给一组固定取值起名字(默认 0 递增、可用 = 显式赋值、未赋值自动 +1),是"有名字的 int 常量集合",比 #define 多了类型与调试优势;状态机、错误码、标志位是它的三大主场——别再让 if (x == 3) 折磨读代码的人了。