初识C语言13.自定义类型(联合体与枚举)

目录

前言:

一、联合体(Union):共享内存的"多面手"

[1.1 联合体的声明](#1.1 联合体的声明)

[1.2 联合体的核心特点:内存共享](#1.2 联合体的核心特点:内存共享)

[1.3 联合体大小的计算](#1.3 联合体大小的计算)

[1.4 联合体的实际应用](#1.4 联合体的实际应用)

二、枚举(Enum):给常量起"名字"

[2.1 枚举类型的声明](#2.1 枚举类型的声明)

[2.2 枚举的核心优点](#2.2 枚举的核心优点)

2.3枚举的实际使用

三、联合体与结构体的对比

总结


前言:

在C语言的自定义类型中,联合体(Union) 和 枚举(Enum) 是两种灵活且实用的工具,它们既能优化内存使用,也能让代码更具可读性。下面我们逐一解析这两种类型的核心知识

一、联合体(Union):共享内存的"多面手"

1.1 联合体的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。

但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。

给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

复制代码
// 联合体的声明
union MyUnion {
    char c;      // 成员1:字符型
    int i;       // 成员2:整型
    float f;     // 成员3:浮点型
};
  • 关键字是 union ,后接联合体名(如 MyUnion ),大括号内是成员列表。

  • 定义变量时,可直接在声明后加变量名: union MyUnion u1; ,也可通过 typedef 简化:

    typedef union MyUnion {
    char c;
    int i;
    } MyUnion;
    MyUnion u2; // 直接用MyUnion定义变量

1.2 联合体的核心特点:内存共享

与结构体(成员各自占用独立内存)不同,联合体的所有成员共享同一块内存空间,这是它最关键的特性:

  • 同一时间,只能使用其中一个成员(若修改一个成员,其他成员的值会被覆盖)。

  • 联合体的内存大小,由最大的成员大小决定(同时需满足内存对齐规则,见下文"大小计算")。

    //代码1
    #include <stdio.h>
    //联合类型的声明
    union Un
    {
    char c;
    int i;
    };
    int main()
    {
    //联合变量的定义
    union Un un = {0};
    // 下⾯输出的结果是⼀样的吗?
    printf("%p\n", &(un.i));
    printf("%p\n", &(un.c));
    printf("%p\n", &un);
    return 0;
    }

输出结果:

复制代码
001AF85C
001AF85C
001AF85C

//代码2
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
return 0;
}

输出结果:

复制代码
11223355

代码1输出的三个地址⼀模⼀样,代码2的输出,我们发现将i的第4个字节的内容修改为55了。 我们仔细分析就可以画出,un的内存布局图。

1.3 联合体大小的计算

联合体的大小需满足两个规则:

  1. 至少容纳最大的成员:大小 ≥ 最大成员的字节数;

  2. 满足内存对齐:大小是"联合体中最大基本类型成员的对齐数"的整数倍。

    union SizeDemo {
    char arr[5]; // 大小5字节(基本类型是char,对齐数1)
    int num; // 大小4字节(基本类型是int,对齐数4)
    };

  • 最大成员是 arr5 (5字节);

  • 最大基本类型对齐数是 int 的4字节;

  • 因此联合体大小需是4的整数倍,且≥5 → 最终大小是8字节。

1.4 联合体的实际应用

联合体的核心价值是节省内存,常见场景:

  • 存储不同类型但互斥的数据:比如一个变量"要么是字符,要么是整数",用联合体可避免额外内存开销;

  • 类型转换(谨慎使用):利用内存共享特性,可实现不同类型的"强制转换"(但需注意平台的字节序):

    union Convert {
    int i;
    char c[4];
    };
    union Convert u;
    u.i = 0x12345678;
    // 在小端序系统中,c[0]是0x78,c[1]是0x56,以此类推

二、枚举(Enum):给常量起"名字"

2.1 枚举类型的声明

枚举顾名思义就是⼀⼀列举。把可能的取值⼀⼀列举。

比如我们现实生活中:

⼀周的星期⼀到星期日是有限的7天,可以⼀⼀列举 性别有:男、女、保密,也可以⼀⼀列举

月份有12个有月,也可以⼀⼀列举 三原色,也是可以意义列举

这些数据的表⽰就可以使⽤枚举了

复制代码
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
}

枚举是一种将一组常量命名的自定义类型,关键字是 enum ,用于替代无意义的数字常量

复制代码
// 枚举的声明:表示"星期"的常量
enum Week {
    Mon,    // 默认值0
    Tue,    // 默认值1
    Wed,    // 默认值2
    Thu,    // 默认值3
    Fri,    // 默认值4
    Sat,    // 默认值5
    Sun     // 默认值6
};
  • 大括号内是"枚举常量"(如 Mon 、 Tue ),默认从 0 开始依次递增;

  • 也可手动指定枚举常量的值:

    enum Week {
    Mon = 1, // Mon=1
    Tue, // Tue=2(自动递增)
    Wed = 5, // Wed=5
    Thu // Thu=6(自动递增)
    };

2.2 枚举的核心优点

为什么使用枚举?

我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

  1. 增加代码的可读性和可维护性

  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

  3. 便于调试,预处理阶段会删除 #define 定义的符号

  4. 使用方便,⼀次可以定义多个常量

  5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用

  6. 简化维护:若需修改常量值,只需改枚举声明,无需逐个修改代码中的数字。

2.3枚举的实际使用

定义枚举变量后,可直接赋值枚举常量:

复制代码
enum Week today;
today = Wed;  // 合法:赋值枚举常量
// today = 10;  // 不推荐:部分编译器会警告(类型不匹配)

也可通过 typedef 简化类型名:

复制代码
typedef enum Week {
    Mon, Tue, Wed
} Week;
Week tomorrow = Tue;

三、联合体与结构体的对比

为了更清晰区分,我们用表格对比联合体和结构体:

特性 联合体(Union) 结构体(Struct)

内存分配 所有成员共享同一块内存 每个成员占用独立内存,总大小是成员之和(+对齐)

成员使用 同一时间只能用一个成员 可同时使用所有成员

大小计算 由最大成员决定(+对齐) 成员大小之和(+对齐)

典型场景 互斥数据的存储、节省内存 不同属性的组合存储(如"学生"的姓名、年龄)

总结

联合体和枚举是C语言中"轻量级"的自定义类型:

  • 联合体通过内存共享实现了内存优化,适合存储互斥的数据;

  • 枚举通过给常量命名提升了代码的可读性与可维护性。

合理使用这两种类型,能让C语言代码更高效、更易读。

相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜11 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa11 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio