自定义类型:联合和枚举

目录

[1. 联合体](#1. 联合体)

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

[1.2 联合体的特点](#1.2 联合体的特点)

[1.3 相同成员的结构体和联合体对比](#1.3 相同成员的结构体和联合体对比)

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

[1.5 联合的一个练习](#1.5 联合的一个练习)

[2. 枚举类型](#2. 枚举类型)

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

[2.2 枚举类型的优点](#2.2 枚举类型的优点)

[2.3 枚举类型的使用](#2.3 枚举类型的使用)


1. 联合体

1.1 联合体类型的声明

联合体所有成员共享同一块内存,所以联合体也称:共用体。

编译器只为最大的成员分配足够的内存空间,但由于内存对齐要求,联合体的实际大小可能大于最

大成员的大小。

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

  • char c:1 字节
  • int i :4 字节 最大成员是 int,基础需要至少 4 字节。

为什么是4呢?

1.2 联合体的特点

联合的成员是共用同⼀块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员),但是受内存对齐 的影响,联合体的实际大小可能大于最大
成员的大小

联合体所有成员共享同一块内存起始地址

&un (联合体起始地址) = &un.i = &un.c

结构体各个成员地址依次递增;联合体所有成员起始地址完全一致,这是最核心区分特征。

如果将一个变量赋值或者修改其中一个变量,会改变共享这块内存的内容,其他变量的内容读取

到的结果也可能受到影响。

可以发现:

联合体成员共用同一块内存,修改成员c会覆盖共用内存的一部分,因此再读取成员i时,得到的值

也会受到影响。

内存分布如下:

前提:常见 x86 小端平台(低字节存低地址)

  1. un.i = 0x11223344 内存排布: 地址 0:0x44 地址 1:0x33 地址 2:0x22 地址 3:0x11
  2. un.c = 0x55 修改起始第 0 字节 ,地址 0 被覆盖为 0x55 ,最终内存:0x55 0x33 0x22 0x11, 打印结果:11223355

1.3 相同成员的结构体和联合体对比

复制代码
// 结构体
struct S
{
    char c;
    int i;
};

// 联合体
union Un
{
    char c;
    int i;
};

1. 内存大小(32/64 位主流环境)

  • sizeof(struct S) = 8 字节
    • char c:1 字节;存在 3 字节内存对齐填充(粉色浪费区域)
    • int i:4 字节,必须从 4 的倍数地址开始存放
  • sizeof(union Un) = 4 字节
    • 分配空间 = 最大成员大小(int占 4 字节)
    • 所有成员共享同一片内存,无独立空间

2. 内存布局核心区别

结构体 struct S

成员独占内存,地址依次往后排列

&s.c ≠ &s.i,两个成员地址不同,互不覆盖。

联合体 union Un

所有成员共享同一个起始地址,重叠占用内存

&un.c == &un.i == &un

⚠️ 赋值冲突:修改**un.c** 会覆盖**un.i** 最低字节;修改**un.i** 会修改**un.c**的值。

3. 核心特性对照表

💡 典型使用场景

  • 结构体:用来描述一组同时存在的属性(学生:姓名、年龄、分数)
  • 联合体:节省内存;大小端检测;协议解析(同一内存用多种类型解析)

1.4 联合体大小的计算

规则:

1. 联合体的大小至少是最大成员的大小;

2. 当最大成员的大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。

复制代码
#include <stdio.h>
union Un1
{
    char c[5];
    int i;
};

union Un2
{
    short c[7];
    int i;
};
int main()
{
    //下面输出的结果是什么?
    printf("%d\n", sizeof(union Un1));
    printf("%d\n", sizeof(union Un2));
    return 0;
}

1. union Un1

  • char c[5]→ 5 字节
  • int i → 4 字节 最大成员字节数:5 向上补齐到 4 的倍数:8
  • sizeof(union Un1) = 8

2. union Un2

  • short c[7] :short 占 2 字节,7×2 = 14 字节
  • int i :4 字节 最大成员字节数:14 向上补齐到 4 的倍数:16
  • sizeof(union Un2) = 16

使用联合体是可以节省空间的,举例:

比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬

衫。

每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:书名、作者、页数

杯子:设计

衬衫:设计、可选颜色、可选尺寸

一般会这样写:

复制代码
struct gift_list
{
//公共属性
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
//特殊属性
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
};

上述的结构其实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这

样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性

信息是常用的。比如:

商品是图书,就不需要 design、colors、sizes。

所以我们就可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体起来,这样就可

减少所需的内存空间,一定程度上节省了内存。

复制代码
struct gift_list
{
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型

    union {
        struct
        {
            char title[20];//书名
            char author[20];//作者
            int num_pages;//页数
        }book;
        struct
        {
            char design[30];//设计
        }mug;
        struct
        {
            char design[30];//设计
            int colors;//颜色
            int sizes;//尺寸
        }shirt;
    }item;
};

1.5 联合的一个练习

写一个程序,判断当前机器是大端?还是小端?

原理讲解

  1. 大小端概念
  • 小端模式 :数据低字节 存放在内存低地址(PC、x86 架构默认)

  • 大端模式 :数据高字节 存放在内存低地址(网络传输常用)

  1. 联合体核心特性 联合体所有成员共享同一块内存空间。 int ichar c 起始地址完全相同。

  2. 执行过程 un.i = 1; 二进制:00000000 00000000 00000000 00000001

  • 小端 :低地址存放**00000001un.c** 读到 1 ,函数返回 1

  • 大端 :低地址存放 00000000un.c 读到 0 ,函数返回 0

2. 枚举类型

2.1 枚举类型的声明

枚举顾名思义就是一一列举

把可能的取值一一列举。

现实例子:

  • 一周的星期一到星期日是有限的 7 天,可以一一列举

  • 性别有:男、女、保密,也可以一一列举

  • 月份有 12 个月,也可以一一列举

  • 三原色,也是可以一一列举

这类有限取值的数据,就可以使用枚举。

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

enum Sex//性别
{
    MALE,
    FEMALE,
    SECRET
};

enum Color//颜色
{
    RED,
    GREEN,
    BLUE
};

enum Dayenum Sexenum Color 都是枚举类型

大括号**{}** 内是枚举类型的可能取值,称为 枚举常量

枚举常量数值规则

  1. 默认从 0 开始,向后依次递增 1

  2. 声明枚举时,可以手动指定初始值

    enum Color//颜色
    {
    RED=2,
    GREEN=4,
    BLUE=8
    };

补充知识点

  • 枚举常量本质是整型常量
  • 如果只给某一个常量赋值,后面未赋值的常量在前一个数值基础上 + 1
  • 示例:
复制代码
  enum Color
  {
      RED=5,
      GREEN,  // 6
      BLUE    // 7
  };

2.2 枚举类型的优点

思考问题

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

枚举五大优点

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

  2. 和**#define** 定义的标识符对比,枚举具备类型检查,代码更加严谨。

  3. 便于调试 :预处理阶段编译器会删除**#define**定义的符号,调试器看不到宏名;枚举常量符号可以在调试时直接查看。

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

  5. 枚举常量遵循作用域规则 :如果枚举声明写在函数内部,枚举常量只能在该函数内使用;而**#define**作用域默认从定义处直到文件末尾。

2.3 枚举类型的使用

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

enum Color clr = GREEN;//使用枚举常量给枚举变量赋值

重要特性:整型向枚举变量赋值的区别

1. C语言:

允许直接用整数给枚举变量赋值,编译器通常只会产生警告,而不会报错。

复制代码
enum Color clr = 2;  // C语言合法

本质 C 语言中枚举常量底层是 int,类型检查宽松。

2. C++:

类型检查更加严格,不允许直接将字面整数赋值给枚举变量,直接写会编译报错。 如果一定要转

换,必须显式强制类型转换:

复制代码
enum Color clr = (enum Color)2;
相关推荐
weixin_456808382 小时前
【沁恒蓝牙开发】BLE常见错误码与解析
c语言·单片机·嵌入式硬件
HJX_07242 小时前
嵌入式软件C语言八股文复习笔记5——编译、链接与底层硬核机制
c语言·开发语言·笔记
haolin123.3 小时前
类和对象(下)
c语言·开发语言·c++
(Charon)6 小时前
【C++】手写 MySQL 连接池(三):异步任务队列与工作线程
c语言·c++
加油码6 小时前
共享内存详解:原理、系统调用与高性能进程间通信
linux·c语言·c++
Lzh编程小栈7 小时前
【STM32底层精讲】RCC时钟系统超全详解(时钟树+源码+避坑指南)
c语言·stm32·单片机·嵌入式硬件·面试
Nebula嵌入式17 小时前
【C语言】09-深入解析main函数
linux·c语言·开发语言·嵌入式
天空'之城20 小时前
C 语言工业级通用组件手写 23:卡尔曼滤波(简易版)
c语言·卡尔曼滤波·嵌入式算法·工业级组件
888CC++20 小时前
C语言与C++的区别:从面向过程到面向对象
java·c语言·c++