C 语言联合体与枚举:共用内存 + 常量枚举 + 实战

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyX游戏规划

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

  • [C 语言联合体与枚举:共用内存 + 常量枚举 + 实战](#C 语言联合体与枚举:共用内存 + 常量枚举 + 实战)
    • [前景回顾:结构体核心速记 📝](#前景回顾:结构体核心速记 📝)
    • [一、联合体(共用体):共享同一块内存的节省神器 🧩](#一、联合体(共用体):共享同一块内存的节省神器 🧩)
      • [1. 联合体的声明与基本特性](#1. 联合体的声明与基本特性)
      • [2. 联合体的关键特点:修改一个成员,影响其他成员](#2. 联合体的关键特点:修改一个成员,影响其他成员)
      • [3. 结构体 vs 联合体:核心差异对比](#3. 结构体 vs 联合体:核心差异对比)
      • [4. 联合体大小的计算规则(高频考点)](#4. 联合体大小的计算规则(高频考点))
      • [5. 联合体的实战应用:节省内存的场景](#5. 联合体的实战应用:节省内存的场景)
      • [6. 经典面试题:用联合体判断大小端字节序](#6. 经典面试题:用联合体判断大小端字节序)
    • [二、枚举类型:一一列举的常量集合 📜](#二、枚举类型:一一列举的常量集合 📜)
    • [写在最后 📝](#写在最后 📝)

C 语言联合体与枚举:共用内存 + 常量枚举 + 实战

继结构体之后,我们继续深挖C语言自定义类型的另外两大核心------联合体枚举!联合体是"共用内存"的节省空间神器,枚举是"常量列举"的代码可读性利器。这一篇我们从两者的声明、特性、大小计算,到实战应用、优势对比,全方位拆解,帮你掌握自定义类型的完整知识体系!

前景回顾:结构体核心速记 📝

C 语言结构体全解析:声明 + 内存对齐 + 位段 + 传参优化

回顾结构体的核心知识点,能帮我们更好区分联合体与结构体的差异:

  1. 结构体成员各自占用独立内存空间,总大小遵循内存对齐规则。
  2. 位段是结构体的紧凑版,通过指定bit位数节省空间,但存在跨平台问题。
  3. 结构体传参优先传地址,避免拷贝带来的效率损耗。

一、联合体(共用体):共享同一块内存的节省神器 🧩

联合体(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. 联合体大小的计算规则(高频考点)

联合体的大小计算遵循两个规则,比结构体简单很多

  1. 联合体的大小至少是最大成员的大小
  2. 当最大成员的大小不是最大对齐数的整数倍时,需要对齐到最大对齐数的整数倍。
实战案例:计算联合体大小
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语言自定义类型的重要补充,各自有着不可替代的作用:

  1. 联合体的核心是共用内存,适合存储互斥数据,大幅节省内存空间;
  2. 枚举的核心是常量列举 ,提升代码可读性和可维护性,比#define更严谨;
  3. 联合体大小计算要牢记"最大成员大小+对齐规则",是面试高频考点;
  4. 用联合体判断大小端的代码,是面试中的经典手写题,务必掌握。

至此,C语言自定义类型(结构体、联合体、枚举)的核心内容就全部讲解完毕啦!这些知识点是连接基础语法和实战开发的桥梁,建议大家结合案例多敲代码,加深理解。

相关推荐
徐先生 @_@|||2 小时前
三式掌握知识法
java·python
yousuotu2 小时前
基于Python实现亚马逊销售数据分析与预测
开发语言·python·数据分析
L Jiawen2 小时前
【Golang基础】基础知识(上)
开发语言·后端·golang
zore_c2 小时前
【C语言】排序算法——快速排序详解(含多种变式)!!!
c语言·数据结构·笔记·算法·排序算法·深度优先·推荐算法
m0_462605222 小时前
第N11周:seq2seq翻译实战-Pytorch复现
人工智能·pytorch·python
qq_393060472 小时前
在WSL2的Jupyter中正确显示中文字体seaborn覆盖plt的问题
ide·python·jupyter
张登杰踩2 小时前
django后台管理配置教程
后端·python·django
laocooon5238578862 小时前
对传入的 x , y 两个数组做折线图, x 对应 x 轴, y 对应 y 轴。并保存到 Task1/image1/T2.png
python·numpy·pandas·matplotlib
羽飞2 小时前
GCC ABI炸弹
linux·c++·python