在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.pswp.cn/diannao/95054.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95054.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95054.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Numpy科学计算与数据分析:Numpy数据分析基础之统计函数应用

Numpy统计函数实战&#xff1a;数据的聚合与分析 学习目标 通过本课程的学习&#xff0c;学员将掌握Numpy中用于统计分析的关键函数&#xff0c;如求和(sum)、平均值(mean)、标准差(std)等&#xff0c;能够熟练地在实际数据集中应用这些函数进行数据的聚合与分析。 相关知识…

从引导加载程序到sysfs:Linux设备树的完整解析与驱动绑定机制

摘要本报告旨在为嵌入式Linux开发者详细梳理设备树&#xff08;Device Tree, DT&#xff09;在系统启动中的完整解析流程。报告将从引导加载程序&#xff08;Bootloader&#xff09;如何准备和传递设备树二进制文件&#xff08;DTB&#xff09;开始&#xff0c;逐步深入到内核如…

基于深度学习的污水新冠RNA测序数据分析系统

基于深度学习的污水新冠RNA测序数据分析系统 摘要 本文介绍了一个完整的基于深度学习技术的污水新冠RNA测序数据分析系统&#xff0c;该系统能够从未经处理的污水样本中识别新冠病毒变种、监测病毒动态变化并构建传播网络。我们详细阐述了数据处理流程、深度学习模型架构、训练…

宝塔面板配置Nacos集群

一、环境准备 准备三台及以上的服务器&#xff0c;我这里准备了3台服务器&#xff0c;172.31.5.123&#xff5e;125&#xff1b;分别安装好宝塔面板&#xff0c;软件商店里安装nacos&#xff1b;二、Nacos集群配置 配置数据库连接&#xff1a;​ 进入每台服务器上 Nacos 解压后…

Spring Boot 3.x 全新特性解析

Spring Boot 是企业级 Java 开发中最常用的框架之一。自 Spring Boot 3.x 发布以来&#xff0c;其引入的一系列重大变更与优化&#xff0c;为开发者提供了更现代、更高效的开发体验。本文将重点解析 Spring Boot 3.x 的关键特性及其对项目架构的影响。 一、基于 Jakarta EE 10 …

2025.8.10总结

今天晚上去跑了2公里&#xff0c;跑完还挺爽的&#xff0c;然后花了1.5个小时去公司刷题&#xff0c;没有进行限时练&#xff0c;花了一周的时间才做完这题&#xff0c;共找了20个bug&#xff0c;虽然没有进行限时练&#xff0c;但我仿佛对测试技术掌握得更好了&#xff0c;知道…

qt中实现QListWidget列表

使用最基本的QListWidgetItem来创建列表项&#xff0c;具体使用下面setText、setIcon、addItem这三个方法#include "mainwindow.h" #include "ui_mainwindow.h" #include "QDebug"enum CustomRoles {IdRole Qt::UserRole, // 存储IDPhoneR…

nginx-主配置文件

nginx-主配置文件一、主配置文件nginx.conf内容二、修改配置的文件后的操作三、配置虚拟主机的域名1. 修改nignx.conf配置文件2. 新建域名对应的网页根目录3. 重载nginx配置4. 验证一、主配置文件nginx.conf内容 [rootweb1 conf]# cat nginx.conf#user nobody; # nginx woke…

DBSACN算法的一些应用

以下是 DBSCAN 算法在 Python 中的几个典型应用示例&#xff0c;涵盖了基础使用、参数调优和可视化等方面&#xff1a;import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import DBSCAN from sklearn.datasets import make_moons, make_blobs from skl…

java9学习笔记-part1

G1 成为默认垃圾回收器在 Java 8 的时候&#xff0c;默认垃圾回收器是 Parallel Scavenge&#xff08;新生代&#xff09;Parallel Old&#xff08;老年代&#xff09;。到了 Java 9, CMS 垃圾回收器被废弃了&#xff0c;G1&#xff08;Garbage-First Garbage Collector&#x…

【github.io静态网页 怎么使用 github.io 搭建一个简单的网页?】

这里是一张展示 GitHub Pages 静态网站架构与部署流程的示意图&#xff0c;可以帮助你更直观理解整个流程。 要使用 github.io&#xff08;GitHub Pages&#xff09;搭建一个简单的网页&#xff0c;你可以按照以下步骤操作&#xff1a; 快速入门&#xff1a;个人网站&#xff…

记录一次ubuntu20.04 解决gmock not found问题的过程

在电脑上源码编译moveit&#xff0c;系统是ubuntu20.04&#xff0c;有三个电脑&#xff0c;分别叫做A,B,C好了&#xff0c;A和C都可以很顺畅地走流程编译通过&#xff0c;但是B遇到了gmock not found的问题&#xff0c;一开始没当回事&#xff0c;感觉重装下库&#xff0c;或者…

Java基础编程核心案例:从逻辑到应用

Java编程的核心在于将逻辑思维转化为可执行的代码。本专栏通过8个实用案例&#xff0c;覆盖条件判断、循环结构、数组操作、用户交互等基础知识点&#xff0c;展示如何用Java解决实际问题&#xff0c;从简单游戏到数据计算&#xff0c;逐步构建编程思维。 案例一&#xff1a;剪…

Starlink卫星终端对星策略是终端自主执行的还是网管中心调度的?

以下文章首先来源于Google Gemini的Deep Research的内容,在Deep Research的报告参考了SpaceX公开信息、FCC技术报告、相关专利(如US9906292B2)以及学术研究的综合分析,并参考了RFWirelessWorld和APNIC博客等二次来源。 文章完成之后,前后发给了Grok和deepseek,让Grok和d…

【CDA案例】数据分析案例拆解:解锁数据分析全流程!

在当今数字化时代&#xff0c;数据如同一座座金矿&#xff0c;蕴含着巨大的价值。企业、组织乃至个人都渴望从海量的数据中挖掘出有用的信息&#xff0c;以指导决策、优化运营、提升竞争力。今天我们以一个实际的数据分析案例为蓝本&#xff0c;深入拆解其全过程&#xff0c;带…

vulnhub-drippingblues靶场攻略

1.打开靶场&#xff0c;我们将网络连接方式改为NAT模式2.然后使用nmap扫描一下nat的网段3.存在21&#xff0c;22&#xff0c;80端口我们先来看一下21端口的ftp协议&#xff0c;发现可以直接匿名登录&#xff0c;并且可以下载存在的东西4.但是这个压缩包被加密了&#xff0c;我们…

afsim2.9_使用QtCreator和VSCode编译

使用QtCreator和VSCode编译AFSIM2.9源代码指南 准备工作 在开始编译AFSIM2.9源代码前&#xff0c;需要确保您的开发环境满足以下条件&#xff1a; 安装QtCreator安装Visual Studio Code&#xff08;最新稳定版&#xff09;获取AFSIM2.9源代码包安装必要的编译工具链&#xf…

TC39x STM(System Timer)学习记录

STM有哪些特性&#xff1f;自由运行的 64 位计数器所有 64 位可同步读取可同步读取 64 位计数器的不同 32 位部分基于与 STM 部分内容的比较匹配&#xff0c;灵活地产生服务请求在应用复位后自动开始计数若 ARSTDIS.STMxDIS 位清零&#xff0c;应用复位将复位 STM 寄存器&#…

css初学者第四天

<1>snipaste工具的使用snipaste是一个简单但强大的截图工具&#xff0c;也可以让你将截图贴回屏幕上。常用的快捷方式&#xff1a;1、F1可以截图&#xff0c;同时测量大小&#xff0c;设置箭头 书写文字等2、F3在桌面置顶显示3、点击图片&#xff0c;alt可以取色&#xf…

CompletableFuture实现Excel 多个sheet页批量导出

CompletableFuture实现Excel 多个sheet页批量导出 文章目录 CompletableFuture实现Excel 多个sheet页批量导出 为什么不能直接合并文件或Sheet? 我的方案合理性 1. 操作实现步骤 1.1、导入所需要的依赖 1.2 、Excel 导入导出对象 1.3、异步生成 Excel 文件到指定路径 1.4、合并…