自定义类型:结构体、联合与枚举(2)

目录

前言

一、 联合体类型的声明

介绍:

注意:

二、 联合体的特点

介绍:

代码举例:

三、联合体⼤⼩的计算

介绍:

联合体大小的计算规则

1. 基础规则

1. 确定最大成员大小

2. 计算对齐模数的最小公倍数

3. 最终大小

结论

关键总结

结论

四、枚举类型的声明

介绍:

五、枚举类型的优点

介绍:

总结


前言

上一篇文章讲解结构体类型的声明、结构体变量的创建和初始化、结构成员访问操作符、结构体内存对⻬知识的相关内容,本篇文章将讲解联合体类型的声明、联合体的特点、联合体⼤⼩的计算、枚举类型的声明、枚举类型的优点、枚举类型的使⽤知识的相关内容。

一、 联合体类型的声明

介绍:

在C语言中,联合体(Union) 是一种特殊的数据类型,它允许在同一段内存空间中存储不同类型的数据,但同一时刻只能保存其中一种类型的值。以下是联合体类型的常见声明方式:

union 联合体标签名 {
    成员类型1 成员名1;
    成员类型2 成员名2;
    // ... 其他成员
};
  • 使用场景:声明后可通过 union 联合体标签名 定义变量。

代码例:

union Data {
    int num;
    float f;
    char str[20];
};
union Data data; // 定义联合体变量

注意:

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

2.联合体的关键字是union. 但是编译器只为最⼤的成员分配⾜够的内存空间。

3.联合体的特点是所有成员共⽤同⼀块内存空间。所 以联合体也叫:共⽤体。 给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

二、 联合体的特点

介绍:

联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合 ⾄少得有能⼒保存最⼤的那个成员)。

代码举例:

#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;
}

由此我们可以得出联合的成员是共⽤同⼀块内存空间的,那么,改其中一个成员的地址,其他成员的地址受到影响吗?

例:


#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;
}

由代码以及结果,我们可知:我们改其中一个成员的地址,其他成员的地址受到影响,通过改c,将i的第4个字节的内容修改为55了。

这处我们通过图来讲解:

详细讲解:

    • union Un 包含 char c(1字节)和 int i(4字节),因此联合体大小为 4字节(取最大成员 int 的大小)。
    • 所有成员共用这4字节内存,c 存储在内存的最低地址字节(取决于系统字节序,默认这是小端字节序,即低字节存低位数据)。
  1. 初始赋值与修改过程

    • 步骤1un.i = 0x11223344
      在小端字节序下,4字节内存的存储形式为(从低地址到高地址):
      44(0x44)、33(0x33)、22(0x22)、11(0x11)。

    • 步骤2un.c = 0x55
      char c 会覆盖内存中最低地址的1字节,将原 44 替换为 55。此时内存变为:
      55(0x55)、33(0x33)、22(0x22)、11(0x11)。

  2. 最终读取 un.i 的值
    内存中的4字节数据组合为 0x11223355(高地址字节 11 为最高位,低地址字节 55 为最低位),因此 printf("%x\n", un.i) 输出 11223355

既然联合体是公用内存,那我们来对⽐⼀下相同成员的结构体和联合体的内存布局情况。

先列出联合体:

#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;
}

结构体:

#include <stdio.h>
struct s
{
    char c;
    int i;
};
int main()
{   struct s un = {0};
    printf("%p\n", &(un.i));
    printf("%p\n", &(un.c));
    printf("%p\n", &un);
    return 0;
}

结合图:

三、联合体⼤⼩的计算

介绍:

联合体计算大小和结构体相同,有内存对齐规则:

下面解释一下:

联合体大小的计算规则

联合体(Union)的大小由最大成员的大小内存对齐规则共同决定,核心原则是:联合体的总大小必须能容纳最大的成员,且满足所有成员的对齐要求

1. 基础规则
  • 取最大成员大小:联合体的大小首先至少是所有成员中占用内存最大的那个成员的大小。
  • 满足内存对齐:最终大小需向上调整为所有成员对齐模数的最小公倍数的倍数(对齐模数通常为成员类型的大小,如int为4字节,double为8字节等)。

列举下例子:

union Test1 {
    char a;    // 1字节
    int b;     // 4字节
    double c;  // 8字节
};

1. 确定最大成员大小
  • 成员 achar):1字节
  • 成员 bint):4字节
  • 成员 cdouble):8字节
  • 最大成员大小 = 8字节double c)。
2. 计算对齐模数的最小公倍数

各成员的对齐数(通常为类型大小):

  • char:1字节
  • int:4字节
  • double:8字节 (最⼤对⻬数)
  • 最小公倍数 = 8(1、4、8的最小公倍数为8)。
3. 最终大小

联合体大小需满足:

  • 容纳最大成员(8字节);
  • 是对齐模数最小公倍数(8)的倍数。

因此,union Test1 的大小为 8字节 ✅。

结论

union Test1 在内存中占用 8字节,由最大成员 double c 的大小和对齐要求共同决定。

例:

#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;
}

接下来,将对其详解:

union Un1 的大小计算

  • 最大成员大小char c[5] 占5字节(大于 int i 的4字节)。
  • 对齐模数
    • char 数组的对齐数 = 1字节(基础类型 char 的大小);
    • int 的对齐数 = 4字节。
    • 最小公倍数 = 4(1和4的最小公倍数)。
  • 调整对齐:5字节需向上调整为4的倍数(5 → 8,因为 4×2=8)。
  • 结果sizeof(union Un1) = 8

2. union Un2 的大小计算

  • 最大成员大小short c[7] 占14字节(大于 int i 的4字节)。
  • 对齐模数
    • short 数组的对齐数 = 2字节(基础类型 short 的大小);
    • int 的对齐数 = 4字节。
    • 最小公倍数 = 4(2和4的最小公倍数)。
  • 调整对齐:14字节需向上调整为4的倍数(14 → 16,因为 4×4=16)。
  • 结果sizeof(union Un2) = 16

关键总结

联合体大小需同时满足 “容纳最大成员” 和 “对齐模数最小公倍数的倍数” 两个条件,当最大成员大小不满足对齐要求时,需向上补齐 💡。

注意:基础类型 short 的大小为2个字节。

当然,联合体可以帮助我们解决很多问题,例如:求系统的大小端字节序:

如果不了解的可一看一下数据在内存中的存储该章节内容。

#include <stdio.h>
union Un1
{
    int i;
    char c;

};
int main()
{
   union Un1 a;
   a.i=1;
   a.c=*((char*)&a.i);
   if(a.c==1)
   {
       printf("小端字节序\n");
   }
   else
   {
       printf("大端字节序\n");
   }
    return 0;
}

解释:

  • 联合体成员共享同一块内存空间,i 和 c 的起始地址相同。
  • char c 仅访问内存中 第一个字节 的数据。

字节序判断原理

  • a.i = 1:将整数 1 存入 int i,其二进制表示为(假设32位系统):
    00000000 00000000 00000000 00000001(高位字节在前,低位字节在后)。

  • 字节序影响内存存储

    • 小端字节序:低位字节存低地址 → 第一个字节为 00000001(值为1)。
    • 大端字节序:高位字节存低地址 → 第一个字节为 00000000(值为0)。
  • a.c = *((char*)&a.i):直接读取 i 的第一个字节数据到 c

条件判断结果

  • 若 a.c == 1 → 第一个字节为 1 → 小端字节序。
  • 若 a.c == 0 → 第一个字节为 0 → 大端字节序

结论

程序通过联合体共享内存的特性,读取整数 1 的第一个字节数据,最终判断当前系统为 小端字节序 💡。

四、枚举类型的声明

介绍:

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

代码示例:

enum Color

{
    RED,
    GREEN,
    BLUE
};

详解:

  1. 默认值规则

    • 首个常量默认值为 0,后续常量默认值为 前一个常量值 + 1
    • 可通过 = 显式指定任意整数(支持负数),未指定的后续常量自动递增。
enum Color

{
    RED=2,
    GREEN,
    BLUE
};

这样实现。

五、枚举类型的优点

介绍:

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

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

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

4. 使⽤⽅便,⼀次可以定义多个常量。

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

使用示例:

#include <stdio.h>

// 声明并定义枚举
typedef enum {
    SPRING = 1,  // 显式从1开始
    SUMMER,      // 2
    AUTUMN,      // 3
    WINTER       // 4
} Season;

int main() {
    Season s = SUMMER;
    printf("当前季节编号:%d\n", s);  // 输出:2
    return 0;
}

总结

 以上就是今天要讲的内容,本文介绍了联合体类型的声明、联合体的特点、联合体⼤⼩的计算、枚举类型的声明、枚举类型的优点、枚举类型的使⽤知识的相关内容,为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位,同时自定义类型:结构体、联合与枚举部分也结束了,下篇文将为大家带来新的知识。

评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值