C语言学习: 自定义类型—联合和枚举

一、联合体(共用体,union

1. 核心概念

联合体是一种特殊的自定义数据类型,和结构体类似 ,但所有成员共享同一块内存空间,因此也叫 "共用体"。

  • 关键字:union
  • 内存分配规则:编译器只按最大成员的大小分配内存,保证能装下所有成员中占空间最大的那个。
  • 特点:给其中一个成员赋值,其他成员的值会被覆盖(因为共享内存)。

2. 基础示例解析

复制代码
#include <stdio.h>

// 联合体类型声明
union Un
{
    char c;  // 1字节
    int i;   // 4字节
};

int main()
{
    union Un un = {0}; // 联合体变量初始化
    printf("%d\n", sizeof(un)); // 输出:4
    return 0;
}

为什么输出是 4?

  • 联合体的大小 =最大成员的大小 (这里int i****占 4 字节char c占 1 字节 ),所以**sizeof(un)为 4。**
  • 注意:如果有内存对齐规则 ,联合体大小会是最大成员大小的整数倍,这里刚好满足,所以直接是 4。

3. 联合体的关键特性:地址共享

代码 1:验证地址相同
复制代码
#include <stdio.h>

union Un
{
    char c;
    int i;
};

int main()
{
    union Un un = {0};
    printf("%p\n", &un.i); // 成员i的地址
    printf("%p\n", &un.c); // 成员c的地址
    printf("%p\n", &un);   // 联合体变量本身的地址
    return 0;
}

输出结果:三个地址完全相同

  • 这直接证明了联合体的所有成员共享同一块内存的起始地址。
代码 2:验证赋值互相影响
复制代码
#include <stdio.h>

union Un
{
    char c;
    int i;
};

int main()
{
    union Un un = {0};
    un.i = 0x11223344; // 给int成员赋值(十六进制)
    un.c = 0x55;       // 给char成员赋值
    printf("%x\n", un.i); // 输出:11223355(小端机器)
    return 0;
}

为什么输出是11223355

  • 小端机器下,int 0x11223344在内存中按低地址到高地址存储为:44 33 22 11
  • un.c = 0x55,本质是修改了低地址的第 1 个字节 (也就是**44的位置** ),变成55 所以最终的**int值变成0x11223355。**

4. 结构体 vs 联合体:内存对比(抽象一点就是富家千金和穷苦人家的区别)

特性 结构体(struct 联合体(union
内存分配 每个成员独立分配内存,总大小是所有成员大小之和(含对齐) 所有成员共享同一块内存大小为最大成员的大小(含对齐)
成员关系 成员之间互不影响 成员之间互相覆盖赋值会影响其他成员
适用场景 描述包含多个独立属性的对象 描述同一时间只需要存储其中一个属性的对象(比如多种类型的数据共用内存)

5. 联合体大小的计算(核心规则)

联合体的大小计算遵循两条铁律:

  1. 基础规则 :联合体的大小至少等于最大成员的大小要能装下最大的那个成员)。
  2. 对齐规则 :联合体的大小必须是最大对齐数的整数倍
    • 联合体的「最大对齐数」= 所有成员中,对齐数最大的那个(对齐数通常等于成员自身的大小 ,比如int是 4 字节,对齐数就是 4)。

题目计算:union Un1union Un2

我们来一步步算:

1. union Un1
复制代码
union Un1
{
    char c[5]; // 大小:5字节,对齐数:1
    int i;     // 大小:4字节,对齐数:4
};
  • 第一步:找最大成员大小 → char c[5] 是 5 字节。
  • 第二步:找最大对齐数 → int i 的对齐数是 4。
  • 第三步:向上对齐到最大对齐数的整数倍 →比 5 大的、4 的倍数是 8。
  • 所以:sizeof(union Un1) = 8
2. union Un2
复制代码
union Un2
{
    short c[7]; // 大小:7×2=14字节,对齐数:2
    int i;      // 大小:4字节,对齐数:4
};
  • 第一步:找最大成员大小 → short c[7] 是 14 字节。
  • 第二步:找最大对齐数 → int i 的对齐数是 4。
  • 第三步:向上对齐到最大对齐数的整数倍 →比 14 大的、4 的倍数是 16
  • 所以:sizeof(union Un2) = 16

✅ 最终输出结果:

复制代码
8
16

6. 联合体的实际应用:礼品兑换单内存优化

⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品图书、杯⼦、衬衫。
每⼀种商品 都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨

这个例子**完美体现了联合体「互斥属性共享内存」**的优势:

6.1 传统结构体写法(浪费内存)

复制代码
struct gift_list
{
    // 公共属性
    int stock_number;    // 4字节
    double price;        // 8字节
    int item_type;       // 4字节

    // 所有商品的特殊属性(全部独立存储)
    char title[20];     // 20字节(图书用)
    char author[20];    // 20字节(图书用)
    int num_pages;       // 4字节(图书用)
    char design[30];     // 30字节(杯子/衬衫用)
    int colors;          // 4字节(衬衫用)
    int sizes;           // 4字节(衬衫用)
};
  • 问题:不管当前商品是哪种所有属性都占用内存,比如图书会白白浪费杯子 / 衬衫的属性空间。
  • 粗略大小(含对齐):4+8+4 + 20+20+4 + 30+4+4 = 98****字节实际对齐后更大)。

6.2 联合体优化写法(节省内存)

复制代码
struct gift_list
{
    // 公共属性(独立内存)
    int stock_number;    // 4字节
    double price;        // 8字节
    int item_type;       // 4字节

    // 特殊属性(联合体共享内存)
    union {
        struct {
            char title[20];  // 图书属性:20+20+4=44字节
            char author[20];
            int num_pages;
        } book;
        struct {
            char design[30]; // 杯子属性:30字节
        } mug;
        struct {
            char design[30]; // 衬衫属性:30+4+4=38字节
            int colors;
            int sizes;
        } shirt;
    } item; // 联合体大小 = 最大成员大小(44字节,对齐后为48字节)
};
  • 优势:三种商品的特殊属性共享同一块内存,同一时间只占用其中一种属性的大小。
  • 总大小(含对齐):4+8+4 + 48 = 64 字节,相比传统写法节省了近一半内存。

7. 联合体经典练习:判断大小端

这段代码利用联合体「成员共享内存」的特性,是 C 语言的经典面试题:

复制代码
int check_sys()
{
    union
    {
        int i;    // 4字节
        char c;   // 1字节
    } un;
    un.i = 1;
    return un.c; // 返回1:小端;返回0:大端
}

原理拆解

给**un.i = 1后,int 1** 在内存中的存储方式由大小端决定:

表格

机器类型 低地址字节(un.c读取的位置) 高地址字节 un.c的值 判断结果
小端(低地址存低位) 0x01 0x00 0x00 0x00 1 小端
大端(低地址存高位) 0x00 0x00 0x00 0x01 0 大端

8. 易错点提醒

  1. 联合体大小不是简单的 "最大成员大小" :必须考虑对齐规则,比如char c[5]的联合体最终大小是 8 而不是 5。
  2. 联合体同一时间只能用一个成员 :给一个成员赋值后,其他成员的值会被覆盖,不要同时读写多个成员。
  3. 联合体不能直接整体赋值 :C 语言中联合体不能直接赋值,只能通过成员访问赋值

二、枚举类型是什么?

枚举(enum)就是把变量所有可能的取值一一列举出来 ,专门用来表示有限且固定的选项 ,比如星期、性别、颜色、状态等。

  • 关键字:enum
  • 核心特点:枚举常量本质是int类型的常量 ,但自带类型检查 ,比直接用**数字或#define**更安全。

1. 枚举类型的声明与赋值

1.1 基础声明(默认值)

复制代码
// 星期枚举:默认从0开始,依次+1
enum Day {
    Mon,    // 0
    Tues,   // 1
    Wed,    // 2
    Thur,   // 3
    Fri,    // 4
    Sat,    // 5
    Sun     // 6
};

// 性别枚举
enum Sex {
    MALE,    // 0
    FEMALE,  // 1
    SECRET   // 2
};
  • {}里的内容叫枚举常量默认从0开始 ,后面的常量依次在前一个基础上 + 1。

2. 手动指定初始值

你也可以手动给枚举常量赋值,后续常量会在前一个的基础上 + 1:

复制代码
enum Color {
    RED=2,    // 手动赋值为2
    GREEN=4,  // 手动赋值为4
    BLUE=8    // 手动赋值为8
};

2. 枚举 vs #define:为什么更推荐用枚举?

枚举的 5 个核心优势

对比项 #define 定义常量 枚举(enum
类型检查 无类型检查,只是文本替换,容易出现非法赋值 有严格的类型检查 ,只能赋值枚举常量,更安全
可读性 常量分散,语义弱,不知道这些常量属于同一类 集中定义,一看就知道是同一类数据,比如RED/GREEN/BLUE
调试友好 预处理阶段会被替换成数字,调试时看不到符号名 编译器保留符号名调试时能直接看到常量名
作用域 全局作用域,容易和其他宏 / 变量重名冲突 遵循作用域规则**,函数内声明的枚举只能在函数内使用**
定义效率 一次只能定义一个常量 次可以定义多个相关常量代码更简洁

3. 枚举类型的使用与易错点

1. 正确用法:用枚举常量赋值

复制代码
enum Color {
    RED=1,
    GREEN=2,
    BLUE=4
};

int main()
{
    enum Color clr = GREEN; // 正确:用枚举常量给枚举变量赋值
    return 0;
}

2. 整数赋值的区别

  • C 语言中可以直接用整数给枚举变量赋值 (比如 clr = 2;),但不推荐,会失去枚举的类型检查优势。
  • C++ 中不允许直接用整数赋值 ,必须用枚举常量类型检查更严格

4. 补充:枚举的常见实用场景

  1. 状态机(游戏 / 业务逻辑)

    复制代码
    enum PlayerState {
        IDLE,    // 待机
        RUN,     // 跑步
        JUMP,    // 跳跃
        ATTACK,  // 攻击
        DEAD     // 死亡
    };
  2. 选项开关(位运算场景)

    复制代码
    enum Permission {
        READ = 1,    // 读权限 0001
        WRITE = 2,   // 写权限 0010
        EXECUTE = 4  // 执行权限 0100
    };
    // 同时赋予读+写权限
    int user_perm = READ | WRITE;

5. 易错点提醒

  1. 枚举常量本质是intsizeof(enum Color) 通常等于**sizeof(int)**(4 字节),可以直接当整数用。
  2. 不要超出枚举范围赋值:虽然 C 语言允许,但超出范围的值会让代码逻辑混乱,失去枚举的意义。
  3. 枚举常量名要避免冲突 :枚举常量本质是全局符号,不要和其他变量 / 宏重名。
相关推荐
Honker_yhw1 小时前
大数据管理与应用系列丛书《数据挖掘》(吕欣等著)读书笔记-偏相关分析
笔记·学习
gumichef1 小时前
栈和队列(2)
数据结构·算法·链表
进击的荆棘1 小时前
递归、搜索与回溯——综合(下)
c++·算法·leetcode·深度优先·dfs
不知名的忻1 小时前
归并排序(Java)
java·算法·排序算法
陈天伟教授1 小时前
人生的力量来源何处?
人工智能·学习
YUDAMENGNIUBI4 小时前
day20_逻辑回归
算法·机器学习·逻辑回归
澈2078 小时前
C++并查集:高效解决连通性问题
java·c++·算法
旖-旎10 小时前
深搜练习(单词搜索)(12)
c++·算法·深度优先·力扣
企客宝CRM11 小时前
2026年中小企业CRM选型指南:企客宝CRM处于什么位置?
android·算法·企业微信·rxjava·crm