位段 联合体 枚举

news/2024/5/18 23:03:34 标签: 算法, C语言, 联合体, 结构体, 位段, 枚举

在这里插入图片描述

Hello好久不见,今天分享的是接上次结构体没有分享完的内容,这次我们讲讲位段 枚举联合体的概念以及他们的用法。

2.1 什么是位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

总的来说,位段结构体很相似,但是不同的地方就是它表示的大小是位,大小是比特位,不是字节,这样的作用就是减少内存大小,还有就是增加效率。

那接下来让我们来看一个位端吧

struct A
{
	 int _a:2;
	 int _b:5;
	 int _c:10;
	 int _d:30;
};

这就是我们位段的基本样子,一个冒号加数字,数字表示的就是大小,单位是比特位。
我们也可以将它放入函数中来看一下它的大小。
我们可以将它们放入我们函数当中,用sizeof来测试,看下面的代码

#include<stdio.h>
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	printf("%zd ", sizeof(struct A));
	return 0;
}

我们可以看到上代码在我们控制台上的结果是8
在这里插入图片描述
位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
//一个例子
#include<stdio.h>

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return  0;
}
//空间是如何开辟的?

我们来看上面的一个例子,来了解它在VS上是怎么开辟空间的

因为Vs是小端,我们从低地址开始往后动,在假设我们先用数据的低位开始往后动。
在这里插入图片描述
我们这是根据它的大小进行猜,因为它所占的字节大小就是三个字节,所以我们进行猜设它是从数据的低位开始,而且我们不是每个字节都要用到,当一个字节中不能存储我们后面比特位大小的时候我们跳到后面的字节开始使用,我们可以在VS中用内存来查看我们的结果

在这里插入图片描述
转换到16进制是620304这个数字,我们现在来看一下在内存的存储吧

在这里插入图片描述

我们可以看到的结果是和我们编译器上是一样的,但是还是会有问题,那就是位段时不能跨平台的,第一是因为它有很多不确定性,比如我们不知道它对数据的使用是怎么使用的,是从小到大还是从左到右,这都是不确定的,所以它有很多的不确定性

2.3 位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
    舍弃剩余的位还是利用,这是不确定的。

总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
在这里插入图片描述
这就是我们位段的使用场景,相当于是包装数据,比如我们微信发消息的时候要表白一位女生,首先我们要确定这位女生的用户名,这就相当于我们图中的IP地址,如果没有这个Ip地址,我们就可能不知道发给谁,可能就发给自己的好兄弟,那这样可要产生大误会了,而我们的数据也不是直接发过去的,要进行我们最后的包装才可以,我们进行包装的时候,如果用的不是位段的话,上面的版本好每个都需要一个整型大小,那这样不是浪费,我们所占的空间越大,运行速度就越慢,我们发数据是通过网络协议栈的,这就是相当于我们的高速公路如果用int包装的话,那就是相当于大卡车,但是如果是用位段进行包装的话,那就是相当于小汽车,通行速度也就快了。

3. 枚举

枚举顾名思义就是一一列举。
把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。

性别有:男、女、保密,也可以一一列举。
月份有12个月,也可以一一列举
这里就可以使用枚举了。

那我们来使用一下枚举吧(枚举关键字是enum)
在这里插入图片描述
我们可以看到其实枚举就是整型常量,说到常量大家还记得有多少,一个是字面常量,还有就是const修饰的长常变量,还有就是枚举常量了
3.2 枚举的优点
为什么使用枚举
我们可以使用 #define 定义常量,为什么非要使用枚举
枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

这里在讲一下我们经常哟美好的define,我们也可以讲上面的三元素写成

#define GREEN 0
#define RED 0
#define BLUE 0

但是为什么枚举方便调试呢,这里是有原因的 当我们define的时候,我们调试就直接替换掉了,所以会不方便调试,枚举你叫方便调试,还有就是方便,比如我们要枚举一个周,那我们直接写就行了,但是如果我们define,要写七次,看起来就有点麻烦,所以不建议这样写。

enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;               //

我们在C语言中可以这样写,但是在语法检查严格的C++就不能这样写了
4. 联合(共用体)

  • 4.1 联合类型的定义

联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。

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

#include<stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};

int main()
{
	//联合变量的定义
	union Un un;
	//计算连个变量的大小
	printf("%d\n", sizeof(un));
	return 0;
}

通过运行发现我们的结果是4个字节大小那说明我们char其实和我们的int公用一个个字节。
4.2 联合的特点

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

union Un
{
 int i;
 char c;
};
union Un un;
// 下面输出的结果是一样的吗?
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);

可以看到的是其实他们的地址是一样的,这里更加说明了联合体是共用同一块空间的

那我们还记不记得之前我们来测试机器大小端是怎样计算的呢,相信有一部分人记得,另一部分又忘记了,那在写一次吧

#include<stdio.h>
int check_sys()
{
	int a = 1;
	return *((char*)&a);
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

那现在我们也可以用联合体来进行测试

#include<stdio.h>
int check_sys()
{
	union
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

联合体使用范围就是有点小,特殊情况才使用我们来看一下下面这张图

在这里插入图片描述
这里我们看图,发现每次都需要重新定义一个结构体,这样浪费空间就很大,那我们就可以这样写

在这里插入图片描述
这样写就能节省很多空间了,那今天的内容就分享到这里,我们下次再见


http://www.niftyadmin.cn/n/5043987.html

相关文章

Spring Cloud Alibaba Ribbon负载均衡器

文章目录 Ribbon 负载均衡器环境搭建1.依赖2.配置3.修改其默认的负载均衡策略3.1 验证 4.创建自定义的Rule4.1 MyRule&#xff08;&#xff09;4.2 在配置config类中配置 5.饥饿加载6.我只想访问不想被别的访问 Ribbon 负载均衡器 背景 Ribbon 是一个用于客户端负载均衡的开源…

制作游戏demo的心得

制作这个游戏demo出来的心得 https://www.bilibili.com/video/BV1cF411m7Dh/ 制作游戏demo的心得 制作游戏demo&#xff0c;主要是为了表现自己的技术&#xff0c;那就一门心思想着如何提高表现力就行了&#xff0c;在整体的画面渲染风格方面或许没有什么可选择的&#xff0c;…

Pytorch实现的LSTM、RNN模型结构

一、LSTM模型 import torch from torch import nn import torchvision.datasets as dsets import torchvision.transforms as transforms import matplotlib.pyplot as plttorch.manual_seed(1)# Hyper Parameters EPOCH 1 # 训练整批数据多少次, 为了节约时间, 我们…

算法基础--位运算

一、常见位运算总结&#xff1a; 1、基础位运算&#xff08;^&#xff09; 其中异或^有2种理解。 2、位图bitset相关&#xff08;&|&#xff09; test判断第x位是1函数0: 可以让n右移&#xff0c;也可以让1左移&#xff0c;习惯上选择第一种 (n>>x)&1 判…

如何判断用户的密码是否为强密码?

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 用户设置的密码弱&#xff0c;会导致信息安全问题&#xff0c;一般的系统都要求设置强密码。今天的文章给大家介绍如何通过一个正则表达式来判断用户的密码是否为强密码。 …

第2讲:Vue开发环境的搭建及运行

Vue开发环境搭建步骤 1、安装node http://www.nodejs.com.cn/ 一般安装在根目录下&#xff0c;直接下一步下一步安装即可。如何检测安装完毕 node -v 2、第二步&#xff1a;安装vue-cli脚手架 npm install -g vue/cli &#xff0c;查看安装版本 vue --version 3、第…

Python环境配置及基础用法Pycharm库安装与背景设置及避免Venv文件夹

目录 一、Python环境部署及简单使用 1、Python下载安装 2、环境变量配置 3、检查是否安装成功 4、Python的两种模式&#xff08;编辑模式&交互模式&#xff09; 二、Pycharm库安装与背景设置 1、Python库安装 2、Pycharm自定义背景 三、如何避免Venv文件夹 一、P…

vivo亮相博鳌科创会 自研大模型即将发布

2023年9月19日-21日&#xff0c;博鳌亚洲论坛国际科技与创新论坛第三届大会在广东珠海举行。长期以来&#xff0c;博鳌亚洲论坛与vivo保持着战略合作伙伴关系。今年3月&#xff0c;vivo X Fold2系列手机荣膺博鳌亚洲论坛官方指定手机。在本次会议中&#xff0c;vivo执行副总裁、…