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

C语言自定义类型深度解析:联合体与枚举

在C语言中,自定义类型为数据组织提供了极大的灵活性。除了常用的结构体,联合体(共用体)和枚举也是非常重要的自定义类型。本文将结合实例,详细解析联合体和枚举的特性、用法及实际应用场景。

一、联合体(Union):共用内存的特殊类型

联合体(又称共用体)是一种特殊的自定义类型,它的所有成员共用同一块内存空间,这一特性使其在内存优化场景中非常实用。

1.1 联合体的声明与定义

联合体的声明语法与结构体类似,但成员的内存布局完全不同。

// 联合体类型声明
union Un {char c;  // 字符型成员int i;   // 整型成员
};int main() {// 联合体变量定义并初始化union Un un = {0}; // 计算联合体大小printf("联合体大小:%d\n", sizeof(un));  // 输出结果:4return 0;
}

为什么输出结果是4?
因为联合体的大小至少是最大成员的大小,上述代码中int类型成员i占4字节,因此联合体大小为4。

1.2 联合体的核心特点:成员共用内存

联合体最核心的特性是所有成员共用同一块内存空间,这意味着:

  • 联合体变量的地址与各成员的地址相同;
  • 给一个成员赋值,可能会覆盖其他成员的值。
实例1:验证成员地址相同
#include <stdio.h>
union Un {char c;int i;
};int main() {union Un un = {0};// 打印联合体变量及成员的地址printf("&un:%p\n", &un);printf("&un.i:%p\n", &un.i);printf("&un.c:%p\n", &un.c);return 0;
}

输出结果:三个地址完全相同,证明成员共用同一块内存。

实例2:成员赋值的相互影响
#include <stdio.h>
union Un {char c;int i;
};int main() {union Un un = {0};un.i = 0x11223344;  // 给整型成员赋值un.c = 0x55;        // 给字符型成员赋值(覆盖低字节)printf("un.i = %x\n", un.i);  // 输出结果:11223355return 0;
}

解析

  • int类型在内存中占4字节,0x11223344的内存布局为(假设小端存储):44 33 22 11
  • char类型仅占1字节(低地址字节),赋值0x55后覆盖了低字节,内存变为55 33 22 11,因此un.i最终为0x11223355

1.3 结构体与联合体的内存布局对比

类型内存布局特点示例大小(char+int)
结构体(struct)成员按顺序存储,存在内存对齐浪费8字节(1+3对齐+4)
联合体(union)成员共用内存,无对齐浪费4字节(取最大成员大小)

内存布局示意图

  • 结构体:[char][3字节对齐浪费][int]
  • 联合体:[char和int共用4字节空间]

1.4 联合体大小的计算规则

联合体的大小需满足两个条件:

  1. 至少是最大成员的大小(保证能容纳最大成员);
  2. 必须是最大对齐数的整数倍(内存对齐要求)。
对齐数定义:

每个成员的对齐数 = 成员自身大小与编译器默认对齐数(通常为8)的较小值。

实例计算:
#include <stdio.h>// 案例1:char[5]与int
union Un1 {char c[5];  // 大小5,对齐数1(char大小1)int i;      // 大小4,对齐数4(int大小4)
};// 案例2:short[7]与int
union Un2 {short c[7]; // 大小14(2*7),对齐数2(short大小2)int i;      // 大小4,对齐数4(int大小4)
};int main() {printf("Un1大小:%d\n", sizeof(union Un1));  // 输出:8printf("Un2大小:%d\n", sizeof(union Un2));  // 输出:16return 0;
}

计算过程

  • Un1:最大成员大小5,最大对齐数4。5不是4的倍数,向上对齐到8(4×2);
  • Un2:最大成员大小14,最大对齐数4。14不是4的倍数,向上对齐到16(4×4)。

1.5 联合体的实用场景

场景1:节省内存空间

当不同属性不会同时使用时,用联合体共用内存可大幅减少内存占用。例如礼品兑换单设计:

// 优化前:结构体包含所有属性,内存浪费
struct gift_list_old {int stock_number;  // 库存量double price;      // 定价int item_type;     // 商品类型char title[20];    // 书名(仅图书用)char author[20];   // 作者(仅图书用)char design[30];   // 设计(仅杯子/衬衫用)int colors;        // 颜色(仅衬衫用)
};// 优化后:用联合体存储差异化属性
struct gift_list {int stock_number;  // 公共属性double price;      int item_type;     union {  // 差异化属性共用内存struct { char title[20]; char author[20]; } book;  // 图书struct { char design[30]; } mug;                    // 杯子struct { char design[30]; int colors; } shirt;      // 衬衫} item;
};
场景2:判断机器字节序(大小端)

利用联合体成员共用内存的特性,可简单判断机器存储方式:

// 返回1:小端存储;返回0:大端存储
int check_sys() {union {int i;    // 4字节整型char c;   // 1字节字符} un;un.i = 1;  // 内存存储为0x00000001(大端)或0x01000000(小端)return un.c;  // 小端返回1,大端返回0
}

二、枚举类型(Enum):常量的有序集合

枚举类型用于定义一组具有离散值的常量,使代码更具可读性和可维护性。

2.1 枚举类型的声明与初始化

枚举通过enum关键字声明,其中的成员称为枚举常量。

// 基本声明(默认值从0开始递增)
enum Day {Mon,   // 0Tues,  // 1Wed,   // 2Thur,  // 3Fri,   // 4Sat,   // 5Sun    // 6
};// 自定义初始值(后续值依次递增)
enum Color {RED = 2,    // 2GREEN = 4,  // 4BLUE = 8    // 8
};

2.2 枚举的优点:为何不用#define?

#define定义常量相比,枚举具有明显优势:

  1. 增强可读性:枚举常量有明确的类型归属,代码逻辑更清晰;
  2. 类型检查:枚举是强类型,编译器会进行类型校验(#define无类型);
  3. 便于调试:枚举常量在调试阶段可见(#define在预处理阶段被替换,调试中无符号);
  4. 批量定义:一次可定义多个相关常量,无需重复写#define
  5. 作用域规则:枚举声明在函数内时,仅在函数内有效,避免命名冲突。

2.3 枚举类型的使用

枚举变量需用枚举常量赋值,C语言允许整数赋值但不推荐(C++严格禁止)。

enum Color {RED = 1,GREEN = 2,BLUE = 4
};int main() {enum Color clr = GREEN;  // 正确:用枚举常量赋值// enum Color clr2 = 2;  // C允许,C++禁止(类型不匹配)return 0;
}

三、总结

  • 联合体通过成员共用内存实现内存优化,适用于不同属性不同时使用的场景,大小计算需满足最大成员大小和对齐要求;
  • 枚举用于定义离散常量集合,相比#define具有更强的类型安全性和可读性,是提升代码质量的重要工具。

合理使用联合体和枚举,能让C语言代码更高效、更易维护,尤其在嵌入式开发、内存受限场景中发挥重要作用。

http://www.lryc.cn/news/610864.html

相关文章:

  • 工业设备远程监控的 “颠覆性突破”:边缘计算网关让千里之外如在眼前
  • BUUCTF杂项MISC题解题思路(3)(不断更新)
  • Android 性能基准测试(Benchmark)完全指南:专业方法与最佳实践
  • 视频水印技术中的变换域嵌入方法对比分析
  • 物联网后端系统架构:从基础到AI驱动的未来 - 第十章:AI促进IOT领域发生革命式发展
  • STM32H7+FreeRTOS+LwIP移植EtherCAT开源主站SOEM
  • UE5 安装Visual Studio
  • 百胜软件胜券AI「测试用例」智能体:重塑测试流程,释放效率新势能
  • Modbus tcp 批量写线圈状态
  • 机器翻译的局限性:歧义、文化差异、专业术语翻译难题
  • 推特矩阵背后的多账号协同高效传播体系
  • 电感矩阵-信号完整性分析
  • sqli-labs靶场less36-less40
  • 是的,或许这就是意识!
  • 【qt5_study】1.Hello world
  • Groovy学习篇章一之—— GDK 探秘:Groovy如何给Java对象“开外挂”,让String也能“跑命令”!
  • Git与TortoiseGit在Gitee平台的应用
  • 从零开始学网页开发:HTML、CSS和JavaScript的基础知识
  • SpringCloud学习-------Eureka详解
  • SpringBoot3.x入门到精通系列:4.3 性能优化技巧
  • HTTP性能优化实战:解决高并发场景下的连接瓶颈与延迟问题
  • 浏览器渲染 首屏优化 性能优化
  • ArrayList 深度剖析:从底层原理到性能优化的实战指南
  • MySQL索引底层原理与性能优化实践
  • 力扣:2246. 相邻字符不同的最长路径
  • 解析图像几何变换:从欧式到仿射再到透视
  • 从达梦到 StarRocks:国产数据库实时入仓实践
  • Python高级编程与实践:Python装饰器深入解析与应用
  • 使用 BAML 模糊解析改进 LangChain 知识图谱提取:成功率从25%提升到99%
  • 力扣刷题日常(15-16)