深入C语言,发现多样的数据之枚举和联合体

简介: 深入C语言,发现多样的数据之枚举和联合体

一、枚举

枚举 是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。是一个被命名的整型常数的集合。简单来说就将某种特定类型的对象一一进行列举,一一列举特定类型可能的取值。


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


比如在我们的日常生活中:


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

性别有:男、女,也可以一一列举。

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


说白了,枚举常量属于枚举类型,值是整形。


1.1 枚举的定义

枚举的定义与结构体类似

1. enum 枚举类型名
2. {
3.     标识符1,
4.     标识符2,
5.     …
6. };
  • 枚举类型名受自己定义,如:week,year…,标识符就是其中的枚举常量,如Mon,Tues,Wed…
  • 每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。


1.2 枚举的使用

1.2.1 枚举的声明

如果我们想对一个星期,不用枚举用#define:

#define MON  1
#define TUE  2
#define WED  3
#define THU  4
#define FRI  5
#define SAT  6
#define SUN  7


使用枚举简化,代码示例如下:

enum DAY
{
      MON=1, //指定从1开始,否则默认从0开始
      TUE,
      WED,
      THU, 
      FRI, 
      SAT, 
      SUN
};


与结构体类似,我们也可以使用typedef来简化枚举

typedef enum DAY
{
    MON,
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;


1.2.2 打印枚举常量

typedef enum DAY
{
    MON, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    for (int i = MON; i < SUN; i++)
    {
        printf("%d ", i);
    }
    return 0;
}


输出结果如下:

0 1 2 3 4 5 6

这也间接证明枚举是一个常量,默认从0开始,后面依次递增1。


1. 这些枚举类型的可能取值也叫做枚举常量,那既然是常量,就意味这不能被修改。

typedef enum DAY
{
    MON, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    MON = 9;
    for (int i = MON; i < SUN; i++)
    {
        printf("%d ", i);
    }
    return 0;
}


2. 虽然不能修改,但是我们可以在定义是给它们赋初值。

如果只给第一个赋初值,就从该初值开始,还是依次增1。

typedef enum DAY
{
    MON = 3, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    for (int i = MON; i < SUN; i++)
    {
        printf("%d ", i);
    }
    return 0;
}


输出结果为:

3 4 5 6 7 8 9

如果中间某个赋初值,前面的还是默认值,后面的会依次增1。

typedef enum DAY
{
    MON, 
    TUE,
    WED,
    THU = 6,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    for (int i = MON; i < SUN; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

输出结果为:

0 1 2 6 7 8 9

1.2.3 枚举变量的创建与初始化

那定义好了类型,我们就可以那这些类型来定义变量了:

typedef enum Color//颜色
{
  RED,
  GREEN,
  BLUE
}Color;
 
int main()
{
  Color col = RED;
}


1.3 枚举的大小

已给枚举变量,只会用来存放一个枚举常量的值,所以一个枚举变量的大小应该就是一个 int 的大小,也就是4个字节。下面我们通过代码来测试一下。

#include <stdio.h>
enum color1
{
    RED,
    GREEN,
    BLUE
};
 
enum color2
{
    GRAY = 0x112233445566,
    YELLOW,
    PURPLE
};
 
int main()
{
    printf("%d\n", sizeof(enum color1));
    printf("%d\n", sizeof(enum color2));
    return 0;
}


输出结果为:

4

4

1.4 枚举的优点

于是这里就产生了一个问题,枚举类型的成员均为常量,不可在使用中被修改,那么我们同样可使用宏 #define 去定义常量,为什么非要使用枚举类型呢?


这是因为,相比于宏,枚举类型具有很多优点

  • 增加代码的可读性和可维护性
  • 和 #define 定义的标识符比较,枚举有类型检查,更加严谨
  • 防止了命名污染(封装)
  • 便于调试,#define在调试的时候会完成替换
  • 使用方便,一次可以定义多个变量


二、联合体

2.1 联合体的定义

和结构体一样联合也是一种特殊的自定义类型,这种类型定义的变量也包含有一系列的成员,但不同的是这些成员共用同一块空间(遂也被称作共用体)

union UN
{
  char c;
  int i;
};
//定义了一个共用体类型
 
int main()
{
  union UN un;//定义了一个共用体变量
    printf("%d\n", sizeof(un));
  return 0;
}


2.2 联合体的特点

那现在大家来思考一个问题,上面的联合体变量un的大小是多少?

结果为4


这是为什么呢,在这里要注意上面的一句话:这些成员公用同一块空间(所以联合也叫共用体)。


联合体un只有两个成员,char c; int i;c 大小1个字节,i是最大的成员4个字节。

那么因为它们共用同一块空间,所以四个字节就够了。

4个字节既可以放得下c ,也能放得下i。


联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员),受限于联合体的特点,我们不会同时使用联合里面的成员,只会用一种的一个。

我们要怎么证明这一点?

union UN
{
  char c;
  int i;
};
//定义了一个共用体类型
 
int main()
{
  union UN un;//定义了一个共用体变量
  printf("%d\n", sizeof(un));
  printf("%p\n", &un);
  printf("%p\n", &un.c);
  printf("%p\n", &un.i);
  return 0;
}


输出结果为:

4

00EFFA2C

00EFFA2C

00EFFA2C


可以看出, un 、 un.c 、 un.i 它们三个的起始地址都一样,这也说明,联合体的成员确实会共用同一块内存空间。


那他们共用一块内存空间意味着什么呢?


是不是意味着它们不能同时存在啊,或者说它们不能同时使用。


就拿上面的哪个union Un来说:当我们使用成员 i 时,对于c来说,此时c的那一个字节里面是不是存的是 i 的内容,就相当于此时c是不存在的。


让我们来看一段代码:

1.  un.i = 0x11223344;
2.  un.c = 0x55;
3.  printf("%x\n", un.i);

因为它们共用一块空间,上述代码中先给un.i赋值。


那此时un的四个字节的空间里放的应该是这样的内容:

那我们在去给un.c赋值,是不是就会改变1个字节的内容(因为un.c是1个字节)。

应该就变成这样了:


那我们再以printf("%x\n", un.i);(%x是以16进制形式打印)打印出来是不是就是11223355了。

来验证一下:

输出结果为:


这样更好的验证了联合体的成员共用一块空间,不能同时存在。

2.3 联合体大小的计算

  1. 联合的大小至少是最大成员的大小
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。


来看下面这么一段代码:

#include<stdio.h>
 
//联合体1:
union TEST1
{
  char c[5];
  int i;
};
 
//联合体2:
union TEST2
{
  short c[7];
  int i;
};
 
int main()
{
  //检查联合体的大小:
  printf("The size of TEST1 is %d\n", sizeof(union TEST1));
  printf("The size of TEST2 is %d\n", sizeof(union TEST2));
 
  return 0;
}


输出结果为:

The size of TEST1 is 8

The size of TEST2 is 16


在联合体 TEST1 中,占用空间最大的成员是 char 类型数组 c ,且其中含有 5 个元素,则其所占空间大小为 5 个字节,而我们都知道 VS 的对齐数默认为 8 ,则将会对齐至默认对齐数 4 的整数倍,即 8 个字节;而联合体 TEST2 中,占用空间最大的成员是 short 类型数组 c ,且其中含有 7 个元素,则其所占空间的大小为 14 个字节,那么就将会对齐至对齐数 4 的整数倍,即 16 个字节.


2.4 联合体判断机器大小端

我们早在学习数据在内存中的存储时就已经了解过一种判断大小端的方法,今天就为大家介绍另一种方法——通过联合体判断大小端。


那如何取出第一位呢?除了通过指针,我们也能利用联合体共用同一块内存这一性质判断。

代码如下:

int check_sys()
{
  union
  {
    int i;
    char c;
  }un;
  un.i = 1;
  return un.c; //返回1是⼩端,返回0是⼤端
}
int main()
{
  int ret = check_sys();
  if (ret == 1)
  {
    printf("⼩端\n");
  }
  else
  {
    printf("⼤端\n");
  }
  return 0;
}
相关文章
|
2天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1519 4
|
29天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
5天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
503 19
|
2天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
179 1
|
8天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
9天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
457 5
|
7天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
314 2
|
23天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
25天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2608 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析