大家好我是网域小星球。前面我们学习了结构体 —— 一种能打包多种不同类型数据的自定义类型解决了复杂对象的描述问题。而在实际编程中我们还会遇到两种特殊场景一块内存空间同时只能存储一种数据节省内存变量的取值只能是固定的几个常量规范取值避免出错。C 语言提供的「共用体」和「枚举」就专门解决这两个问题。它们和结构体同属自定义类型用法简单、场景明确是进阶编程和笔试面试的基础知识点。本篇全程 VS2022 可直接运行从概念到实战一步步吃透。目录一、本章学习目标二、共用体Union共享内存的自定义类型2.1 什么是共用体2.2 共用体的定义语法2.3 共用体的初始化与使用1初始化2成员访问2.4 共用体的核心特性与适用场景核心特性适用场景2.5 共用体与结构体的区别重点三、枚举Enum固定常量的自定义类型3.1 什么是枚举3.2 枚举的定义语法3.3 枚举变量的定义与使用1定义枚举变量2使用枚举变量3.4 枚举的核心特性与适用场景核心特性适用场景四、综合实战案例VS2022 可直接运行五、高频易错点六、本章核心总结下期预告一、本章学习目标学完本篇你将彻底掌握共用体Union的定义、初始化与使用理解 “共享内存” 的核心特性共用体与结构体的区别明确共用体的适用场景枚举Enum的定义、初始化与使用掌握固定常量的定义方式枚举的实际应用避免 “魔法数字”让代码更规范、易读共用体与枚举的实战案例学会在项目中灵活运用。二、共用体Union共享内存的自定义类型2.1 什么是共用体共用体也叫联合体核心特点是所有成员共享同一块内存空间同一时间只能存储一个成员的值。结构体所有成员 “各自拥有独立内存”总大小是所有成员大小之和共用体所有成员 “共享一块内存”总大小是最大成员的大小保证能容纳任何一个成员。可以形象理解结构体 → 多个独立的房间每个成员一间房共用体 → 一个房间轮流住不同的人每个成员轮流使用这块内存。2.2 共用体的定义语法和结构体语法类似只需把struct换成union// 定义共用体类型 union 共用体名 { 数据类型 成员1; 数据类型 成员2; ... };示例定义一个能存储 int、char、float 的共用体// 共用体同一时间只能存储一个类型的值 union Data { int a; // 4字节 char b; // 1字节 float c; // 4字节 };这个共用体的大小是 4 字节最大成员 int 和 float 都是 4 字节而非 4149 字节。2.3 共用体的初始化与使用1初始化共用体只能初始化一个成员因为同一时间只能存储一个值语法和结构体类似// 初始化共用体只能给一个成员赋值 union Data d1 {10}; // 初始化成员aint类型 union Data d2 {.b A}; // 指定初始化成员bchar类型 union Data d3 {.c 3.14f}; // 指定初始化成员cfloat类型2成员访问和结构体一样用.访问成员如果是共用体指针用-访问#define _CRT_SECURE_NO_WARNINGS 1 #includestdio.h union Data { int a; char b; float c; }; int main() { union Data d; // 给成员a赋值此时共用体存储的是int类型 d.a 0x12345678; printf(d.a %d\n, d.a); // 输出3054198960x12345678对应的十进制 // 访问成员b共享同一块内存值会受a的影响 printf(d.b %c\n, d.b); // 输出x0x78对应的ASCII值 // 给成员c赋值会覆盖a的值共用体只能存一个值 d.c 3.14f; printf(d.c %.2f\n, d.c); // 输出3.14 printf(d.a %d\n, d.a); // 输出随机值被c覆盖 return 0; }2.4 共用体的核心特性与适用场景核心特性共享内存所有成员占用同一块内存修改一个成员会覆盖其他成员的值大小 最大成员的大小节省内存空间同一时间只能使用一个成员不能同时给多个成员赋值并使用。适用场景节省内存当数据之间 “互斥”同一时间只需要存储一种时比如存储学生的 “成绩类型”要么是 int百分制要么是 char等级制 A/B/C存储设备数据要么是 int整数指令要么是 float浮点数据。类型转换利用共享内存的特性实现不同类型数据的强制转换进阶用法。2.5 共用体与结构体的区别重点特性结构体struct共用体union内存分配所有成员独立分配内存所有成员共享一块内存总大小所有成员大小之和含内存对齐最大成员的大小成员使用可同时使用所有成员同一时间只能使用一个成员核心用途描述复杂对象多种数据共存节省内存互斥数据存储三、枚举Enum固定常量的自定义类型3.1 什么是枚举枚举就是 “列举”核心作用是定义一组固定的、有名字的常量让代码更规范、易读避免直接使用 “魔法数字”无意义的数字常量。比如一周的天数周一周日固定 7 个值性别男、女、未知固定 3 个值状态成功、失败、等待固定 3 个值。这些场景用枚举定义比直接写 1、2、3 更易理解也能避免写错值。3.2 枚举的定义语法// 定义枚举类型 enum 枚举名 { 常量1, 常量2, ... 常量n };示例 1定义 “星期” 枚举// 枚举一周的七天固定7个常量 enum Week { Monday, // 默认值0 Tuesday, // 1自动递增1 Wednesday, // 2 Thursday, // 3 Friday, // 4 Saturday, // 5 Sunday // 6 };示例 2自定义枚举值// 自定义枚举值未指定的自动递增 enum Gender { Male 1, // 自定义值1 Female 2, // 自定义值2 Unknown // 自动为3 };3.3 枚举变量的定义与使用1定义枚举变量有 3 种常用方式推荐前两种更规范// 方式1先定义枚举类型再定义变量 enum Week w1; // 方式2定义枚举类型的同时定义变量 enum Week w2, w3; // 方式3省略枚举名不推荐无法重复使用该枚举类型 enum { Success, Failed, Waiting } status;2使用枚举变量枚举变量的取值只能是枚举中定义的常量不能赋值其他值编译器会警告#define _CRT_SECURE_NO_WARNINGS 1 #include stdio.h // 定义枚举状态 enum Status { Success 0, // 成功 Failed 1, // 失败 Waiting 2 // 等待 }; int main() { enum Status s Success; // 枚举变量赋值只能用枚举常量 s Waiting; // 用switch判断枚举值最常用场景 switch (s) { case Success: printf(操作成功\n); break; case Failed: printf(操作失败\n); break; case Waiting: printf(操作等待中...\n); // 输出此句 break; default: printf(无效状态\n); } return 0; }3.4 枚举的核心特性与适用场景核心特性枚举常量是常量不能修改比如不能写Monday 10枚举变量的取值只能是枚举中定义的常量超出范围会有警告枚举常量默认从 0 开始依次递增 1也可以自定义每个常量的值枚举本质是 int 类型枚举常量的值可以参与整数运算。适用场景固定取值的场景星期、性别、状态、颜色等替代 “魔法数字”比如用Success替代 0Failed替代 1让代码更易读、易维护函数参数限制让函数参数只能取固定值避免传入无效参数进阶用法。四、综合实战案例VS2022 可直接运行结合共用体和枚举实现一个 “学生成绩录入” 程序支持两种成绩类型百分制 / 等级制用枚举限制成绩类型用共用体存储成绩#define _CRT_SECURE_NO_WARNINGS 1stdio.h // 枚举成绩类型百分制/等级制 enum ScoreType { Percent 1, // 百分制int Grade // 等级制char自动为2 }; // 共用体存储成绩同一时间只能存一种类型 union Score { int percent; // 百分制成绩0-100 char grade; // 等级制成绩A/B/C/D/E }; // 结构体学生信息包含成绩类型和成绩 struct Student { int id; char name[20]; enum ScoreType type; // 成绩类型 union Score score; // 成绩共用体 }; int main() { struct Student stu; printf(请输入学生信息\n); printf(学号); scanf(%d, stu.id); printf(姓名); scanf(%s, stu.name); printf(成绩类型1百分制2等级制); scanf(%d, stu.type); // 根据成绩类型录入对应成绩 if (stu.type Percent) { printf(请输入百分制成绩0-100); scanf(%d, stu.score.percent); } else if (stu.type Grade) { printf(请输入等级制成绩A-E); scanf( %c, stu.score.grade); // 空格避免读取换行符 } else { printf(无效的成绩类型\n); return 0; } // 输出学生信息 printf(\n学生信息如下\n); printf(学号%d\n, stu.id); printf(姓名%s\n, stu.name); printf(成绩); if (stu.type Percent) { printf(%d分百分制\n, stu.score.percent); } else { printf(%c级等级制\n, stu.score.grade); } return 0; }五、高频易错点共用体使用误区误以为能同时存储多个成员的值修改一个成员后其他成员的值会被覆盖共用体初始化错误同时给多个成员赋值编译器会报错或警告枚举使用误区给枚举变量赋值枚举外的常量虽然可能运行但不符合规范易出错混淆枚举与结构体误用共用体存储共存的数据比如同时存储学生的年龄和成绩导致数据错乱枚举常量修改试图修改枚举常量的值比如Sunday 10编译器会报错。六、本章核心总结共用体Union所有成员共享一块内存同一时间只能存储一个成员核心作用是节省内存共用体大小 最大成员的大小与结构体的 “成员内存独立” 有本质区别枚举Enum定义一组固定常量核心作用是规范取值、避免魔法数字让代码更易读枚举常量默认从 0 递增可自定义值枚举变量只能取枚举中定义的常量共用体 枚举 结构体可实现更灵活、规范的自定义数据管理是进阶编程的基础。下期预告下一篇我们进入指针进阶内容拆解易混的三大指针指针数组、数组指针、函数指针掌握它们的定义、区别与实战用法攻克 C 语言指针的核心难点。