初识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)
    };

  • 最大成员是 arr[5] (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语言代码更高效、更易读。

相关推荐
麦麦鸡腿堡4 小时前
Java的抽象类实践-模板设计模式
java·开发语言·设计模式
云知谷4 小时前
【经典书籍】《编写可读代码的艺术》精华
开发语言·c++·软件工程·团队开发
空空kkk4 小时前
Java——接口
java·开发语言·python
程小k5 小时前
C++设计模式
c语言·c++
夜晚中的人海5 小时前
【C++】二分查找算法习题
开发语言·c++·算法
狮子座的男孩6 小时前
js基础:06、函数(创建函数、参数、返回值、return、立即执行函数、对象(函数))和枚举对象的属性
开发语言·前端·javascript·经验分享·函数·枚举对象·立即执行函数
sulikey6 小时前
【C++ STL 深入解析】insert 与 emplace 的区别与联系(以 multimap 为例)
开发语言·c++·stl·stl容器·insert·emplace
墨白曦煜6 小时前
Java集合框架整体分类(完整的集合框架关系)
java·开发语言