C语言——联合和枚举

news2024/9/24 1:22:58

目录

一、联合体

1.1 联合体类型的声明

1.2 联合体的特点

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

1.4 联合体大小的计算 

1.5 联合的⼀个练习 

二、枚举类型 

2.1 枚举类型的声明

2.2 枚举类型的优点 

2.3 枚举类型的使用


一、联合体

1.1 联合体类型的声明

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

但是编译器只为最⼤的成员分配足够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共用体

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

创建联合和创建结构的方式相同,需要一个联合模板和联合变量。下面是几种定义联合体变量的方法:

方法一:先创建模板,再定义变量

// 创建联合体模板union perdata
union perdata
{
    int Class;
    char Office;
};
// 使用该联合体模板创建两个变量a, b
union perdata a,b;

此处,perdata是联合体名,该名字是由我们任意定的,但是尽量起个有意义的名称。其相当于一个模板,可以使用这个模板去定义变量a、b。定义的时候不要忘了union

方法二:同时创建模板和变量

// 创建联合体模板union perdata的同时定义两个变量a、b
union perdata
{
    int Class;
    char Office;
}a,b;

这与方法一差不多。

方法三:省略联合体名

union
{
    int Class;
    char Office;
}a,b;

相对于方法一与方法二,此处省略了联合体名。虽然更简洁了,但是因为没有了名字,后面就不能用该联合体定义新的变量。

方法四:使用typedef

// 联合体模板union perdata重新命名为perdata_U
typedef union perdata
{
    int Class;
    char Office;
}perdata_U;
// 使用新名字perdata_U创建两个变量a, b
perdata_U a,b;

此处使用typedef为联合体模板union perdata定义一个别名perdata_U

初始化联合体

联合体的初始化与结构体不同,联合体只能存储一个值。联合体有三种初始化方法:

perdata_U a;
a.Class = 10;
perdata_U b = a;				/* 1、把一个联合初始化为另一个同类型的联合; */
perdata_U c = {20};				/* 2、初始化联合的第一个成员; */
perdata_U d = {.Office = 30};   /* 3、根据C99标准,使用指定初始化器。 */
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;
}

输出的结果:


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

输出的结果:

代码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;//尺⼨
};

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

商品是图书,就不需要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 联合的⼀个练习 

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

int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;//返回1是⼩端,返回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 Day , enum Sex , enum Color 都是枚举类型。

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

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

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

枚举类型需要先定义后使用,这里的定义是类型的定义,不是枚举变量的定义。

既然枚举也是一种数据类型,那么它和基本数据类型一样也可以对变量进行声明。

方法一:枚举类型的定义和变量的声明分开

//先定义类型
enum DAY    //类型名称就是enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
 
//后声明变量
enum DAY yesterday;
enum DAY today;
enum DAY tomorrow; //变量tomorrow的类型为枚举型enum DAY
enum DAY good_day, bad_day; //变量good_day和bad_day的类型均为枚举型enum DAY

方法二:类型定义与变量声明同时进行

enum //跟第一个定义不同的是,此处的标号DAY省略,这是允许的。
{
    saturday,
    sunday = 0,
    monday,
    tuesday,
    wednesday,
    thursday,
    friday
} workday; //变量workday的类型为枚举型enum DAY
 
//变量days的类型为枚举型enum week
enum week { Mon=1, Tue, Wed, Thu, Fri Sat, Sun} days; 
 
//定义枚举类型并声明了两个枚举型变量
enum BOOLEAN { false, true } end_flag, match_flag; 

第二种方式的变量定义是一次性的,后面要想再定义同类型变量就不行了。

第一种方式更灵活。

方法三:用typedef关键字将枚举类型定义成别名,并利用该别名进行变量声明

typedef enum workday
{
    saturday,
    sunday = 0,
    monday,
    tuesday,
    wednesday,
    thursday,
    friday
} workday; //此处的workday为枚举型enum workday的别名
 
//或者
//enum workday中的workday可以省略
typedef enum
{
    saturday,
    sunday = 0,
    monday,
    tuesday,
    wednesday,
    thursday,
    friday
} workday; //此处的workday为枚举型enum workday的别名
 
 
//定义变量
//变量today和tomorrow的类型为枚举型workday,也即enum workday
workday today, tomorrow; 

注意:

同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的命名常量。

2.2 枚举类型的优点 

为什么使用枚举?

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

枚举的优点:

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

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

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

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

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

2.3 枚举类型的使用

1、先声明后赋值(常用)

#include<stdio.h>
 
/* 定义枚举类型 */
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
 
void main()
{
    /* 使用基本数据类型声明变量,然后对变量赋值 */
    int x, y, z;
    
    x = 10;
    y = 20;
    z = 30;
    
    /* 使用枚举类型声明变量,再对枚举型变量赋值 */
    enum DAY yesterday, today, tomorrow;
    
    yesterday = MON;
    today     = TUE;
    tomorrow  = WED;
 
    printf("%d %d %d \n", yesterday, today, tomorrow);
}

就像各种类型都有取值范围一样,枚举变量只能接收枚举类型中定义好的符号值(实质是一个int类型的数据)。

2、声明的同时赋值

#include <stdio.h>
 
/* 定义枚举类型 */
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
 
void main()
{
    /* 使用基本数据类型声明变量同时对变量赋初值 */
    int x=10, y=20, z=30;
 
    /* 使用枚举类型声明变量同时对枚举型变量赋初值 */
    enum DAY yesterday = MON, 
             today = TUE,
             tomorrow = WED;
 
    printf("%d %d %d \n", yesterday, today, tomorrow);
}

3、类型定义、变量声明和赋值同时进行(不推荐,会引入全局变量)

#include <stdio.h>
 
/* 定义枚举类型,同时声明该类型的三个变量,并赋初值。它们都为全局变量 */
enum DAY
{
    MON=1, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN 
}
yesterday = MON, today = TUE, tomorrow = WED;
 
/* 定义三个具有基本数据类型的变量,并赋初值。它们都为全局变量 */
int x = 10, y = 20, z = 30;
 
void main()
{
    printf("%d %d %d \n", x, y, z); //输出:10 20 30
    printf("%d %d %d \n", yesterday, today, tomorrow); //输出:1 2 3
}

 补充

对枚举型的变量可以直接赋任意整数值,如果赋值浮点数,也会自动去掉小数部分。

赋整数值时,可直接赋值,因为枚举的本质就是整型(int)数据的集合。

#include <stdio.h>
 
int main()
{	
	enum week
	{
		MON, TUE, WEN
	};
	
	enum week oneday = 100;
	
	printf("%d,Hello, World! \n", oneday); //100,Hello, World! 
}

其实,更正规的做法是进行显式的强制类型转换:

#include <stdio.h>
 
int main()
{	
	enum week
	{
		MON, TUE, WEN
	};
	
	enum week oneday = (enum week)100;
	
	printf("%d,Hello, World! \n", oneday); //100,Hello, World! 
}

但是不强制转换也可以,和第一个示例一样,不加的话就是隐式类型转换,不会出错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1409159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI】深度学习与图像描述生成——看图说话(1)

还记得我闲来无事&#xff0c;用大模型来“洗图”吗&#xff0c;就是想抄袭别人的图&#xff0c;但是又要装作原创的样子。因为洗稿大家都熟悉&#xff0c;洗图其实也是一样的。 【AIGC】今天想用AI“洗个图”&#xff0c;失败了&#xff0c;进来看我怎么做的-CSDN博客 【AIG…

【QT+QGIS跨平台编译】之八:【zstd+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、zstd介绍二、文件下载三、文件分析四、pro文件五、编译实践一、zstd介绍 ZSTD(Zstandard的缩写),是一种快速压缩算法,提供了高压缩比功能。ZSTD还为小数据提供了一种特殊的模式,称为字典压缩。ZSTD库使用BSD许可证作为开放源码软件提供的。它的格式是稳定的,…

Unity中URP下计算额外灯的方向

文章目录 前言一、为什么额外灯的方向&#xff0c;不像主平行灯一样直接获取&#xff1f;1、主平行灯2、额外灯中&#xff0c;包含 点光源、聚光灯 和 平行灯 二、获得模型顶点指向额外灯的单位向量三、Unity中的实现 前言 在上一篇文章中&#xff0c;我们获取了URP下额外灯的…

LINUX 防火墙 firewalld-cmd命令

常用命令 开启端口命令 firewall-cmd --zonepublic --add-port443/tcp --permanent –zone #作用域 –add-port80/tcp #添加端口&#xff0c;格式为&#xff1a;端口 / 通讯协议 –permanent #永久生效&#xff0c;没有此参数重启后失效 关闭端口命令 firewall-cmd --zo…

qt学习:QListWidget控件+自定义条目项+双击删除+单击获取

目录 图片 头函数 接口 显示案例 方法1 方法2 方法3 方法4 自定义 方法5 在方法4上实现 图片 头函数 #include <QListWidgetItem> 接口 //不怎么常用void addItem(const QString &label)void addItems(const QStringList &labels) //自定义条目项…

Vue2 - keep-alive 作用和原理

目录 1&#xff0c;介绍和作用2&#xff0c;原理3&#xff0c;使用场景3.1&#xff0c;效果展示3.2&#xff0c;实现思路 1&#xff0c;介绍和作用 <!-- 非活跃的组件将会被缓存&#xff01; --> <keep-alive><component :is"activeComponent" />…

静态web服务器实战

准备html页面&#xff0c;包含两个页面(index.html, index2.html)和一个404(404html)页面&#xff0c;目录示意&#xff1a; 1.返回固定页面 with open("website/index.html","r") as file: import socket# # 返回固定的页面 website/index.html if __na…

深度学习笔记(九)——tf模型导出保存、模型加载、常用模型导出tflite、权重量化、模型部署

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 本篇博客主要是工具性介绍&#xff0c;可能由于软件版本问题导致的部分内容无法使用。 首先介绍tflite: TensorFlow Lite 是一组工具&#xff0c;可帮助开…

调优 mybatis saveBatch 25倍性能

调优 mybatis saveBatch 25倍性能 最近在压测一批接口&#xff0c;发现接口处理速度慢的有点超出预期&#xff0c;感觉很奇怪&#xff0c;后面定位发现是数据库批量保存这块很慢。 这个项目用的是 mybatis-plus&#xff0c;批量保存直接用的是 mybatis-plus 提供的 saveBatch…

Backtrader 文档学习-Order OCO orders

Backtrader 文档学习-Order OCO orders 主要是可以使用订单组的管理策略&#xff0c;使用订单组策略&#xff0c;则一组订单中&#xff0c;有一个符合条件的订单成交&#xff0c;订单组中其他的订单就自动被取消。 1.概述 V1.9.36.116 版本交互式代理支持StopTrail、StopTra…

Django笔记(六):DRF框架

首 前后端分离是互联网应用开发的标准使用方式&#xff0c;让前后端通过接口实现解耦&#xff0c;能够更好的进行开发和维护。 RESTful接口常见规范 在接口设计中&#xff0c;大家遵循一定的规范可以减少很多不必要的麻烦&#xff0c;例如url应有一定辨识度&#xff0c;可以…

Database__进阶

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724?spm1000.2115.3001.5343 &#x1f389; 主题&#xff1a; 数据库mysql&#xff08;高级部分&#xff09; ⏱️ 创作时间&#xff1a;2024年01月24…

MySQL-进阶-索引-结构

一、索引概述 1、介绍 2、有误索引搜索效率演示 3、优缺点 二、索引结构 1、B-Tree&#xff08;多路平衡查找树&#xff09; 2、BTree 3、Hash

初识人工智能,一文读懂机器学习之逻辑回归知识文集(4)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

GitLab入门指南:上传与下载操作一网打尽

GitLab简介&#xff1a; GitLab是一个基于Git的开源仓库管理系统&#xff0c;提供了一个Web界面的Git存储库管理器&#xff0c;并集成了多种开发工具的功能&#xff0c;如代码审查、问题跟踪、持续集成和持续部署等。GitLab可以在本地服务器上部署&#xff0c;也可以使用其提供…

Flink入门教程

使用flink时需要提前准备好scala环境 一、创建maven项目 二、添加pom依赖 <properties><scala.version>2.11.12</scala.version></properties><dependency><groupId>org.scala-lang</groupId><artifactId>scala-library<…

【服务器Midjourney】Midjourney网站0基础搭建

目录 🌺【前言】 🌺【准备】 🌺【宝塔搭建MJ】 🌼1. 给服务器添加端口 🌼2. 使用Xshell连接服务器 🌼3. 安装docker 🌼4. 安装Midjourney程序 🌼5. 绑定域名+申请SSL证书 🌼6. 更新网站

oracle vm安装ubuntu使用桥接网络不能访问外网

1. 问题描述 公司网络环境中&#xff0c;可以ping通内网中的所有电脑&#xff0c;ping不通百度域名以及百度的ip地址在热点共享时或者家里未出现此问题 2. 尝试的解决办法 设置网络共享&#xff0c;未起作用。后来测试通以后发现共享不共享都可以通 3. 最终解决办法 H3C禁…

树莓派基础应用:智能家居监控系统

引言&#xff1a; 随着智能家居的普及&#xff0c;家居安全与监控逐渐成为人们关注的焦点。树莓派作为一种功能强大的迷你计算机&#xff0c;为我们提供了实现智能家居监控系统的可能。在本篇博客中&#xff0c;我们将通过构建一个简单的智能家居监控系统&#xff0c;来探索树莓…

NRF24L01模块传输MPU6050数据,接收端数据一直为0问题记录

问题描述&#xff1a; 一、发射端 1、正确配置NRF模块&#xff0c;以及测试过能够正常通信&#xff0c;在发射端的发射线程中进行了如下操作 2、这里是获取了陀螺仪的x轴数据&#xff0c;将其而分为两个8位的数据存入发送缓冲区中。因为一个陀螺仪x轴数据是16位的&#xff0c…