🔷 第八章 结构体与共用体
📄 一句话概括

声明枚举的格式是 enum 枚举名 { 名字1, 名字2, ... };——名字之间用逗号,末尾分号照旧不能漏。默认编号从 0 开始、依次加 1;也可以自己指定(RED = 1),后面没写的会接着上一个 +1 递增。所以"从 1 开始编号"只需给第一个赋值 = 1,后面的自动跟上。

📝 本节和前面几节的关系

8.6 讲了"枚举是什么、为什么有用",这一节落地语法。它和 8.1.1(结构体定义)在形式上高度相似——都是"关键字 + 名字 + 花括号里一串东西 + 末尾分号",区别只是花括号里放的是"成员变量"还是"名字列表"。末尾分号这个坑两处都一样。

一、标准写法

enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };

逐段拆解:

部分 内容 说明
关键字 + 名字 enum Weekday Weekday 是新造出来的类型名,从此可以像 int 一样声明变量
名字列表 { MON, TUE, ... } 用逗号分隔(不是分号!),末尾不加逗号
末尾分号 }; 必须有(和 8.1.1 一样)

编号规则:

enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };
//              0    1    2    3    4    5    6

默认从 0 开始,依次 +1。

二、自己指定编号

enum Color { RED = 1, GREEN = 2, BLUE = 4 };
//              1          2          4

也可以只指定部分,后面自动接着上一个递增:

enum Status { OK = 200, CREATED, ACCEPTED, NO_CONTENT };
//              200      201      202        203
enum Level { LOW = 1, MID, HIGH };
//             1        2     3          ← 只给第一个赋值,后面自动跟上

小技巧:想让枚举"从 1 开始数",只要给第一个写 = 1 就行——这是最常见的需求("第 1 种状态、第 2 种状态",比"第 0 种"顺耳)。

允许重复值和跳跃(不推荐,但要知道):

enum Weird { A = 1, B = 1, C = 10, D };    // B 和 A 同值(合法但易混);D = 11

三、能"一位一位"用吗?——可以

既然能自定义编号,就能顺手用2 的幂表示"可以叠加的选项":

enum Permission {
    READ    = 1,        // 0001
    WRITE   = 2,        // 0010
    EXECUTE = 4,        // 0100
    DELETE  = 8         // 1000
};

配合 3.1.6 的位运算就能组合权限:

int myPerm = READ | WRITE;          // 3 —— 读写都有
if (myPerm & WRITE) cout << "可写" << endl;

这不是枚举本身的语法特性,而是"自定义编号 + 位运算"的组合用法——在权限、标志位系统里非常常见。看到 = 1, 2, 4, 8 这样的编号,就要想到"这是可以叠加的标志位"。

四、命名习惯(照着写就不会踩坑)

  1. 枚举名首字母大写(Weekday、Color)——和结构体的约定一致(8.1.1)。
  2. 枚举值全大写(MON、RED、UNPAID)——这是 C/C++ 的通行惯例,一眼看出"这是个常量而不是变量"。
  3. 枚举值不要重名——同一作用域内所有枚举值共享命名空间,很容易撞车:
enum Color { RED, GREEN };
enum Traffic { RED, YELLOW, GREEN };     // ❌ 编译错:RED 和 GREEN 重名!

这是新手最容易踩的一个坑:枚举值不是"关在里面"的,它们是普通标识符,直接暴露在当前作用域里。所以不同枚举里不要用同名常量,或者给名字加前缀(COLOR_RED、TRAFFIC_RED)。

(C++11 的 enum class 解决了这个问题——它的值必须写 Color::RED。教材环境可能不支持,看到 enum class 知道是"带作用域的枚举"就行。)

五、动手 Demo:三种编号方式一起看

#include <iostream>
using namespace std;

enum Weekday  { MON, TUE, WED, THU, FRI, SAT, SUN };       // 0 ~ 6
enum Level    { LOW = 1, MID, HIGH };                      // 1 ~ 3
enum HttpCode { OK = 200, CREATED, ACCEPTED };             // 200 ~ 202
enum Flag     { F1 = 1, F2 = 2, F4 = 4, F8 = 8 };          // 2 的幂:可叠加

int main() {
    cout << "=== 默认编号(从 0 开始) ===" << endl;
    cout << "MON=" << MON << " TUE=" << TUE << " WED=" << WED
         << " THU=" << THU << " FRI=" << FRI << " SAT=" << SAT << " SUN=" << SUN << endl;

    cout << "=== 只给第一个赋值 ===" << endl;
    cout << "LOW=" << LOW << " MID=" << MID << " HIGH=" << HIGH << endl;

    cout << "=== 跳跃编号 ===" << endl;
    cout << "OK=" << OK << " CREATED=" << CREATED << " ACCEPTED=" << ACCEPTED << endl;

    cout << "=== 2 的幂(可叠加) ===" << endl;
    int perm = F1 | F4;                          // 1 | 4 = 5
    cout << "F1|F4 = " << perm << endl;
    cout << "含 F1 吗? " << ((perm & F1) ? "是" : "否") << endl;
    cout << "含 F2 吗? " << ((perm & F2) ? "是" : "否") << endl;

    cout << "=== 枚举类型本身多大 ===" << endl;
    cout << "sizeof(Weekday) = " << sizeof(Weekday) << " 字节" << endl;
    return 0;
}

看到什么算成功:

=== 默认编号(从 0 开始) ===
MON=0 TUE=1 WED=2 THU=3 FRI=4 SAT=5 SUN=6
=== 只给第一个赋值 ===
LOW=1 MID=2 HIGH=3
=== 跳跃编号 ===
OK=200 CREATED=201 ACCEPTED=202
=== 2 的幂(可叠加) ===
F1|F4 = 5
含 F1 吗? 是
含 F2 吗? 否
=== 枚举类型本身多大 ===
sizeof(Weekday) = 4 字节

四个看点: - 默认 0~6、只给第一个赋值就变 1~3、给定 200 后面接着 201/202 —— 三种编号规则一次看清; - F1 | F4 = 5,而且用 & 能查"有没有这一位"——权限/标志位的经典玩法(3.1.6 的位运算 + 本节的自定义编号); - sizeof(Weekday) = 4——枚举底层用整数存(你的机器上可能是 4,也可能是编译器按取值范围压缩过的更小值)。

30 秒实验:在 enum Color { RED, GREEN }; 后面再加一句 enum Traffic { RED, YELLOW };,编译——你会看到 RED 重定义 的报错。这就是第四节说的"枚举值共享命名空间",亲身踩一次记得最牢。

六、常见误区

  1. "名字列表用分号分隔"——用逗号。分号那是结构体成员列表的写法(8.1.1)。两个定义长得像,但分隔符不同,别混。
  2. "末尾不用加分号"——必须加(和结构体一样)。
  3. "不同枚举里可以有同名常量"——不行,会报"重定义"。要么改名、要么加前缀(COLOR_RED)。这是本节最容易翻车的地方。
  4. "枚举值必须是 0 开始"——可以自定义,也可以只给第一个赋值让后面自动递增。
  5. "枚举只能有连续编号"——可以用 = 1, 2, 4, 8 这种跳跃编号来做可叠加的标志位(配合位运算)。
自测三问(点开对答案) 1. `enum Level { LOW = 1, MID, HIGH };` 里 `HIGH` 等于多少?——**3**(`LOW=1`,后面自动 +1)。 2. 想定义"红绿蓝从 1 开始编号",最短的写法是什么?——`enum Color { RED = 1, GREEN, BLUE };`(只给第一个赋值)。 3. 两个不同枚举里都写 `RED` 会怎样?怎么解决?——**编译报"重定义"**。因为枚举值是普通标识符、共享命名空间。解决:改名字或加前缀。

相关笔记