C语言从入门到实战——联合体和枚举

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
容器镜像服务 ACR,镜像仓库100个 不限时长
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: C语言中,联合体(union)是一种特殊的数据类型,允许存储不同类型的数据在同一块内存空间中。联合体的大小取决于其中最大的成员的大小,共享同一块内存空间的成员可以存储不同类型的数据。联合体的定义方式与结构体类似,使用关键字union,后跟联合体的名称。联合体的成员可以是任意类型的变量,包括基本数据类型、数组、指针等。

联合体和枚举


前言

C语言中,联合体(union)是一种特殊的数据类型,允许存储不同类型的数据在同一块内存空间中。联合体的大小取决于其中最大的成员的大小,共享同一块内存空间的成员可以存储不同类型的数据。

联合体的定义方式与结构体类似,使用关键字union,后跟联合体的名称。联合体的成员可以是任意类型的变量,包括基本数据类型、数组、指针等。

使用联合体时,可以使用成员访问运算符".“或指针运算符”->"来访问联合体的成员。

下面是一个联合体的示例:

#include <stdio.h>
union Data {
   int i;
   float f;
   char str[20];
};
int main() {
   union Data data;
   data.i = 10;
   printf("data.i : %d\n", data.i);
   data.f = 3.14;
   printf("data.f : %f\n", data.f);
   strcpy(data.str, "Hello");
   printf("data.str : %s\n", data.str);
   return 0;
}

输出结果为:

data.i : 10
data.f : 3.140000
data.str : Hello

在这个示例中,联合体Data定义了三个成员:整型变量i、浮点型变量f和字符数组str。在主函数中,我们可以修改和访问这些成员,注意到修改一个成员会影响到其他成员的值。这是因为它们都共享同一个内存空间。

使用联合体时要注意成员的赋值和访问,确保类型和内存空间的正确使用。联合体主要用于在不同类型的数据之间进行转换或共享内存空间的情况。

C语言中,枚举(enum)是一种特殊的数据类型,用于定义一组具有相互关联的常量。枚举常量可以使用标识符来表示,并且可以有一个或多个枚举器(enumerator)。

枚举的定义方式如下:

enum 枚举名称 {
   枚举器1,
   枚举器2,
   ...
   枚举器n
};

枚举常量可以是整数常量、字符常量或字符串常量。

下面是一个使用枚举的示例:

#include <stdio.h>
enum Weekday {
   Monday,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday,
   Sunday
};
int main() {
   enum Weekday today;
   today = Wednesday;
   if (today == Wednesday) {
      printf("Today is Wednesday.\n");
   } else {
      printf("Today is not Wednesday.\n");
   }
   return 0;
}

在这个示例中,我们定义了一个枚举Weekday,包含了一周中的每天。在主函数中,我们定义了一个变量today,然后将其赋值为Wednesday。然后使用if语句判断today的值,输出相应的结果。

输出结果为:

Today is Wednesday.

枚举常量在定义时会默认从0开始递增。在上面的示例中,Monday的值为0,Tuesday的值为1,以此类推。

你也可以显式地为枚举常量赋值,如下所示:

enum Weekday {
   Monday = 1,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday,
   Sunday
};

在这个示例中,Monday的值为1,Tuesday的值为2,以此类推。使用显式赋值可以更准确地控制枚举常量的值。

枚举在编程中常用于定义一组相关的常量,提高程序的可读性和维护性。


一、 联合体

1.1 联合体类型的声明

像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。

所以联合体也叫:共用体。

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

#include <stdio.h>
//联合类型的声明
union Un
{
  char c;
  int i;
};
int main()
{
  //联合变量的定义
  union Un un = {0};
  //计算连个变量的大小
  printf("%d\n", sizeof(un));
  return 0;
}

为什么是4呢?

1.2 联合体的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

//代码1
#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;
}

输出结果:

001AF85C
001AF85C
001AF85C
//代码2
#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;
}

输出结果:

11223355

代码1输出的三个地址一模一样,代码2的输出,我们发现将i的第4个字节的内容修改为55了。

我们仔细分析就可以画出,un的内存布局图。

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

我们再对比一下相同成员的结构体和联合体的内存布局情况。

struct S
{
  char c;
  int i;
};
struct S s = {0};
union Un
{
  char c;
  int i;
};
union Un un = {0};

1.4 联合体大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
#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;
}

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

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

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

图书:书名、作者、页数

杯子:设计

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

那我们思考一下,直接写出一下结构:

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; //尺寸
};

上述的结构其实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。

比如:商品是图书,就不需要designcolorssizes。所以我们就可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体起来,这样就可以介绍所需的内存空间,一定程度上节省了内存。

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 联合的一个练习

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

int check_sys()
{
  union
  {
    int i;
    char c;
  }un;
  un.i = 1;
  return un.c; //返回1是小端,返回0是大端
}

除了判断大小端之外,我们还可以使用联合体来解决实际生活中碰到的取几位地址的问题

#include<stdio.h>
typedef union Un
{
  int n;
  struct s
  {
    char c1;
    char c2;
    char c3;
    char c4;
  }s;
}Un;
int main()
{
  Un test = { 0 };
  test.n = 0x11223344;
  printf("%x %x %x %x",test.s.c1,test.s.c2,test.s.c3,test.s.c4);
  return 0;
}

二、枚举类型

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 都是枚举类型。

{ }中的内容是枚举类型的可能取值,也叫枚举常量 。

这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。

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

2.2 枚举类型的优点

为什么使用枚举?

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

枚举的优点:

  1. 增加代码的可读性和可维护性
  2. #define定义的标识符比较枚举有类型检查,更加严谨。
  3. 便于调试,预处理阶段会删除 #define 定义的符号
  4. 使用方便,一次可以定义多个常量
  5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用

2.3 枚举类型的使用

enum Color//颜色
{
  RED=1,
  GREEN=2,
  BLUE=4
};
enum Color clr = GREEN; //使用枚举常量给枚举变量赋值

那是否可以拿整数给枚举变量赋值呢?

在C语言中是可以的,但是在C++是不行的C++的类型检查比较严格。

相关文章
|
1月前
|
存储 C语言 开发者
C语言实战 | Flappy Bird游戏
【7月更文挑战第4天】Flappy Bird是由越南开发者制作的简单却极具挑战性的游戏,玩家需控制小鸟穿越水管障碍。游戏涉及角色初始化、显示和更新。小鸟和水管结构体存储数据,使用变量和数组。初始化小鸟和水管,显示背景、小鸟和水管,更新小鸟位置及碰撞检测。代码示例展示了小鸟和水管的状态管理,当小鸟与管道碰撞或触地时,游戏结束。游戏的成功在于其独特的虐心体验。
40 0
C语言实战 | Flappy Bird游戏
|
6天前
|
C语言
C语言------程设设计入门
这篇文章是C语言程序设计的入门教程,涵盖了C程序的实现过程、VC集成开发环境的使用、基本数据类型的使用、格式控制字符的作用,以及通过示例代码演示了如何使用printf()函数输出不同类型的数据。
C语言------程设设计入门
|
1月前
|
C语言
C语言实战 | 弹跳的小球
【7月更文挑战第6天】使用C语言实现了一个小球(小方块)在屏幕上斜向移动并反弹的程序。当小球碰到边界时,其运动方向会发生改变。代码分为三部分,分别处理初始化、主循环和更新位置及边界检测。变量drow和dcol控制移动方向,遇到边界时会反转它们的值。
19 3
C语言实战 | 弹跳的小球
|
18天前
|
NoSQL Java 编译器
C语言从入门到精通该怎样学?
持续学习与实践:编程是一门需要不断学习和实践的技能,要保持对新技术和新知识的敏感性,并持续进行编程实践。
29 1
|
1月前
|
存储 Java C语言
【C语言入门】初识C语言:掌握编程的基石
【C语言入门】初识C语言:掌握编程的基石
35 4
【C语言入门】初识C语言:掌握编程的基石
|
1月前
|
存储 大数据 文件存储
C语言实战 | 用户管理系统重构
【7月更文挑战第3天】在大数据背景下,云存储成为关键。案例展示了如何创建一个用户管理系统,包含登录和注册功能,确保数据持久化。通过文件存储,即便程序重启,用户信息仍能被保留,实现登录状态的延续。代码图片省略。
23 2
C语言实战 | 用户管理系统重构
|
1月前
|
C语言 数据安全/隐私保护 UED
C语言实战 | 用户管理系统
【7月更文挑战第2天】近期推出的青少年防沉迷系统采用统一标准,管控未成年人上网时段、时长及内容。用户管理系统是其基础,包含登录和注册功能。代码示例展示了用户管理的流程,通过保存用户信息到文件实现持久化,避免重复注册,确保在限制游戏时间的同时提供更好的用户体验。
21 1
C语言实战 | 用户管理系统
|
1月前
|
存储 编译器 C语言
C语言实战 | “贪吃蛇”游戏
【7月更文挑战第5天】在C语言实战中,本文档介绍了如何构建一个简单的“贪吃蛇”游戏。游戏的核心是控制蛇移动并增长,当吃掉食物时,蛇的身体变长。数据结构使用固定大小的数组表示蛇的位置,变量存储食物位置和蛇的长度。初始化后,利用非阻塞式`getKey()`函数实现WASD键盘控制蛇的运动方向。虽然蛇的边界检测和吃食物后的增长尚未详细说明,但提到了这些问题作为练习留给读者解决,并预告将在后续章节讨论模块化编程以简化复杂代码。
64 0
C语言实战 | “贪吃蛇”游戏
|
1月前
|
存储 Java 程序员
【C语言入门】C语言入门:探索编程世界的基础概念
【C语言入门】C语言入门:探索编程世界的基础概念
46 2
|
1月前
|
前端开发 C语言 C++
C语言入门02---环境搭建
C语言入门02---环境搭建