
🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录
- [C 语言联合体与枚举:共用内存 + 常量枚举 + 实战](#C 语言联合体与枚举:共用内存 + 常量枚举 + 实战)
-
- [前景回顾:结构体核心速记 📝](#前景回顾:结构体核心速记 📝)
- [一、联合体(共用体):共享同一块内存的节省神器 🧩](#一、联合体(共用体):共享同一块内存的节省神器 🧩)
-
- [1. 联合体的声明与基本特性](#1. 联合体的声明与基本特性)
- [2. 联合体的关键特点:修改一个成员,影响其他成员](#2. 联合体的关键特点:修改一个成员,影响其他成员)
- [3. 结构体 vs 联合体:核心差异对比](#3. 结构体 vs 联合体:核心差异对比)
- [4. 联合体大小的计算规则(高频考点)](#4. 联合体大小的计算规则(高频考点))
- [5. 联合体的实战应用:节省内存的场景](#5. 联合体的实战应用:节省内存的场景)
- [6. 经典面试题:用联合体判断大小端字节序](#6. 经典面试题:用联合体判断大小端字节序)
- [二、枚举类型:一一列举的常量集合 📜](#二、枚举类型:一一列举的常量集合 📜)
-
- [1. 枚举类型的声明与使用](#1. 枚举类型的声明与使用)
- 2. 枚举的核心优势:为什么不用`#define`而用枚举?
- [写在最后 📝](#写在最后 📝)
C 语言联合体与枚举:共用内存 + 常量枚举 + 实战
继结构体之后,我们继续深挖C语言自定义类型的另外两大核心------联合体 与枚举!联合体是"共用内存"的节省空间神器,枚举是"常量列举"的代码可读性利器。这一篇我们从两者的声明、特性、大小计算,到实战应用、优势对比,全方位拆解,帮你掌握自定义类型的完整知识体系!
前景回顾:结构体核心速记 📝
回顾结构体的核心知识点,能帮我们更好区分联合体与结构体的差异:
- 结构体成员各自占用独立内存空间,总大小遵循内存对齐规则。
- 位段是结构体的紧凑版,通过指定bit位数节省空间,但存在跨平台问题。
- 结构体传参优先传地址,避免拷贝带来的效率损耗。
一、联合体(共用体):共享同一块内存的节省神器 🧩
联合体(Union)也叫共用体,核心特性是所有成员共用同一块内存空间,编译器只为最大的成员分配内存。这一特性让它成为节省内存的绝佳选择。
1. 联合体的声明与基本特性
联合体的声明语法和结构体类似,关键字为union:
c
#include <stdio.h>
// 声明联合体类型union Un
union Un
{
char c; // 1字节
int i; // 4字节
};
int main()
{
// 联合体大小 = 最大成员的大小(需满足内存对齐)
printf("%zd\n", sizeof(union Un)); // 输出:4
union Un u;
// 所有成员的地址完全相同!
printf("%p\n", &u); // 联合体变量地址
printf("%p\n", &(u.c)); // 成员c的地址
printf("%p\n", &(u.i)); // 成员i的地址
return 0;
}
💡 核心结论:联合体所有成员共享同一块内存,成员地址与联合体变量地址完全一致。
2. 联合体的关键特点:修改一个成员,影响其他成员
由于成员共用内存,给任意一个成员赋值,都会覆盖其他成员的内存区域,从而改变其他成员的值。我们用小端存储环境举例:
c
#include <stdio.h>
union Un
{
char c; // 占用低地址1字节
int i; // 占用4字节
};
int main()
{
union Un un = {0};
// 给i赋值0x11223344,小端存储:低地址→高地址 44 33 22 11
un.i = 0x11223344;
// 给c赋值0x55,覆盖低地址1字节:内存变为 55 33 22 11
un.c = 0x55;
// 读取i的值:0x55332211
printf("%x\n", un.i); // 输出:55332211
return 0;
}
✅ 效果演示:修改c的值直接改变了i的低字节数据,完美体现"共用内存"的特性。
3. 结构体 vs 联合体:核心差异对比
用表格清晰区分结构体和联合体的本质不同:
| 特性 | 结构体 | 联合体 |
|---|---|---|
| 内存分配方式 | 成员各自占用独立内存空间 | 所有成员共用同一块内存空间 |
| 内存大小计算 | 成员大小之和 + 对齐填充字节 | 至少为最大成员大小,需满足对齐 |
| 成员修改影响 | 修改一个成员,不影响其他成员 | 修改一个成员,会覆盖其他成员 |
| 核心优势 | 存储不同类型的独立数据 | 节省内存,适合互斥数据的存储 |
4. 联合体大小的计算规则(高频考点)
联合体的大小计算遵循两个规则,比结构体简单很多:
- 联合体的大小至少是最大成员的大小;
- 当最大成员的大小不是最大对齐数的整数倍时,需要对齐到最大对齐数的整数倍。
实战案例:计算联合体大小
c
#include <stdio.h>
// 案例1:union Un1
union Un1
{
char c[5]; // 大小5字节,对齐数1
int i; // 大小4字节,对齐数4
};
// 案例2:union Un2
union Un2
{
short c[7]; // 大小14字节,对齐数2
int i; // 大小4字节,对齐数4
};
int main()
{
// Un1:最大成员5,最大对齐数4 → 5不是4的倍数,对齐到8
printf("%zd\n", sizeof(union Un1)); // 输出:8
// Un2:最大成员14,最大对齐数4 →14不是4的倍数,对齐到16
printf("%zd\n", sizeof(union Un2)); // 输出:16
return 0;
}
💡 计算技巧:先找最大成员大小和最大对齐数,再判断是否需要对齐填充。
5. 联合体的实战应用:节省内存的场景
联合体特别适合存储互斥的相关数据(即同一时间只会用到其中一个成员)。比如线上礼品订单系统,订单只会是图书、杯子、衬衫中的一种:
c
#include <stdio.h>
// 礼品订单结构体
struct gift_list
{
int stock_number; // 公共属性:库存量
double price; // 公共属性:售价
int item_type; // 公共属性:商品类型(1=图书 2=杯子 3=衬衫)
// 匿名联合体:存储互斥的商品属性,节省内存
union
{
// 图书专属属性
struct
{
char title[20]; // 书名
char author[20]; // 作者
int num_pages; // 页码
}book;
// 杯子专属属性
struct
{
char design[30]; // 设计风格
}mug;
// 衬衫专属属性
struct
{
char design[30]; // 设计风格
int colors; // 可选颜色数
int size; // 可选尺寸数
}shirt;
};
};
✅ 优势:用联合体存储互斥属性,避免了为每种商品单独开辟内存,大幅节省空间。
6. 经典面试题:用联合体判断大小端字节序
利用联合体成员共享内存的特性,可以写出极简的大小端判断代码:
c
#include <stdio.h>
int check_sys()
{
// 匿名联合体:只用一次,无需命名
union
{
char c; // 占用低地址1字节
int i; // 占用4字节
}un;
un.i = 1; // 小端存储:低地址是01;大端存储:低地址是00
return un.c; // 小端返回1,大端返回0
}
int main()
{
if (check_sys() == 1)
printf("小端字节序\n");
else
printf("大端字节序\n");
return 0;
}
💡 核心思路:通过int赋值1,读取char成员的值,判断低地址存储的是高位还是低位字节。
二、枚举类型:一一列举的常量集合 📜
枚举(Enum)即"一一列举",用于定义一组有名字的常量,比如一周的七天、一年的十二个月、性别等。它能让代码更具可读性和可维护性。
1. 枚举类型的声明与使用
枚举的关键字是enum,语法格式为:enum 枚举名 {枚举常量1, 枚举常量2, ...};
c
#include <stdio.h>
// 枚举"星期":枚举常量默认从0开始,依次递增1
enum Day
{
Monday, // 0
Tuesday, // 1
Wednesday, // 2
Thursday, // 3
Friday, // 4
Saturday, // 5
Sunday // 6 最后一个常量后无逗号
};
// 枚举"性别":可以手动指定枚举常量的值
enum Sex
{
MALE = 1, // 手动赋值1
FEMALE, // 自动递增为2
SECRET = 16 // 手动赋值16
};
int main()
{
enum Day d = Sunday; // 定义枚举变量,只能赋值枚举常量
enum Sex s = MALE; // 定义枚举变量
// 打印枚举常量的值
printf("%d\n", MALE); // 输出:1
printf("%d\n", FEMALE); // 输出:2
printf("%d\n", SECRET); // 输出:16
// MALE = 5; // 错误!枚举常量是常量,不能修改
return 0;
}
💡 注意事项:
- 枚举常量是常量,不能赋值修改;
- 枚举变量只能赋值对应的枚举常量;
- 枚举常量默认从0开始递增,也可以手动指定值。
2. 枚举的核心优势:为什么不用#define而用枚举?
我们可以用#define定义常量,但枚举的优势更加明显,用表格对比:
| 对比项 | #define定义常量 |
枚举类型 |
|---|---|---|
| 代码可读性 | 常量分散定义,关联性弱 | 常量集中列举,关联性强,可读性高 |
| 类型检查 | 无类型检查,本质是文本替换 | 有类型检查,更加严谨 |
| 调试友好性 | 预处理阶段被替换,调试不可见 | 调试时可见常量名,便于排查问题 |
| 定义效率 | 需逐一定义,繁琐 | 一次定义多个常量,简洁高效 |
| 作用域规则 | 全局有效,无作用域限制 | 遵循作用域规则,局部枚举仅局部有效 |
写在最后 📝
联合体和枚举是C语言自定义类型的重要补充,各自有着不可替代的作用:
- 联合体的核心是共用内存,适合存储互斥数据,大幅节省内存空间;
- 枚举的核心是常量列举 ,提升代码可读性和可维护性,比
#define更严谨; - 联合体大小计算要牢记"最大成员大小+对齐规则",是面试高频考点;
- 用联合体判断大小端的代码,是面试中的经典手写题,务必掌握。
至此,C语言自定义类型(结构体、联合体、枚举)的核心内容就全部讲解完毕啦!这些知识点是连接基础语法和实战开发的桥梁,建议大家结合案例多敲代码,加深理解。