当前位置: 首页 > news >正文

C语言详解《位段+联合体+枚举》

文章目录

  • 自定义类型
  • 一、位段
    • 1、位段概念
    • 2、位段类型
    • 3、位段空间开辟使用和创建
    • 4、位段变量具体如何占用空间
    • 5、位段优缺点
  • 二、联合体
    • 1、联合体概念
    • 2、联合体创建和性质
    • 3、计算联合体大小
    • 4、联合体判断主机大小端序
      • (1)、用联合体来判断
      • (2)、非联合体来判断
    • 5、联合体优缺点
  • 三、枚举
    • 1、枚举概念
    • 2、枚举创建和性质
    • 3、计算枚举大小
    • 4、 枚举优点

自定义类型

一、位段

1、位段概念

位段就是C语言允许一个结构体以比特位来指定其成员所占内存长度,这种以比特位为单位的成员是位段。

2、位段类型

类型可以是 int unsigned int signed int 或者是 char (属于整形家族)类型。

3、位段空间开辟使用和创建

  • 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟。创建就像创建结构体一样,只是里面的成员变量不同。
  • 成员的组成结构式是类型+变量名+冒号+比特位,这里的比特位是在所开辟空间里只能用那么多比特位。如果下一个比特位满足不了现有空间,那就再开辟一个某类型的空间
  • 如下列各式及组成:
struct S
{
char a:5;
char b:4;
char c:4;
char d:3;
};
struct S s = {0};
s.a = 8;
s.b = 5;
s.c = 7;
s.d = 6;

4、位段变量具体如何占用空间

如下代码,看看a,b,c,d的:

struct S
{
char a:5;
char b:4;
char c:4;
char d:3;
};
struct S s = {0};
s.a = 8;
s.b = 5;
s.c = 7;
s.d = 6;

如图解析:
在这里插入图片描述

1、按定义的位段,先分配好所需比特位占的空间,a只能占用5个,b占用4个,c占用2个,d占用3个。
2、如果下一个所需比特位位数满足不了此空间,那就再开辟一个char类型空间
3、按刚才所分配好的空间,对赋的值进行截断或者不够补0:a=8,二进制位是1000,不够5个,就补0即01000;b=5,二进制位是101,不够4个,补0即0101;c=7,二进制位是111,所需比特位是2,需要截断,即11,;d=6,二进制位是110,整好是3个。
4、把这些比特放到刚才的空间里得到:0000 1000 0011 0101 0000 0110 按每组4位换成16进制是0x 08 35 06。
验证结果如下图:
在这里插入图片描述

5、位段优缺点

优点:

  • 可以使数据单元节省储存空间,当程序需要成千上个数据单元时,这种方法就显得很重要。
  • 位段可以很方便的访问一个整数值得部分内容从而简化程序源代码。

缺点:

  • 位段中最大位的数目不能确定。16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  • int 位段被当成有符号数还是无符号数是不确定的。
  • 分配方向尚未定义:刚才的例子内存是从左向右分配的但是不能保证其他编译器是这种情况
  • 不可移植:内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,导致位段是不可移植的。
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。钢刚才的例子是空间如果不够直接浪费掉,开辟下一个。

二、联合体

1、联合体概念

联合体所有成员都共用一块内存,使用的时候,这些数据在同一个内存位置,关键字是union。

2、联合体创建和性质

创建声明:

union Un//联合类型的声明
{
short a;
int i;
};
union Un un;//联合变量的定义

性质:

  • 它们的成员共用同一块地址 。如下代码:
union Un
{
	double i;
	int a;

};
union Un un;
int main()
{
	printf("%d\n", &(un.i));
	printf("%d\n", &(un.a));
}

验证结果:
在这里插入图片描述

  • 所站占空间大小至少是最大类型的字节数。
  • 不会同时使用:因为是共用一块空间,用这个成员时不能用另一个成员,互相之间改变一个成员就会改变另一个。

3、计算联合体大小

计算联合体大小需同时满足如下条件:

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
    练习计算如下大小:
union Un1
{
	char a[6];
	int i;
};
union Un2
{
	char b[9];
	short i;
};

int main()
{
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;
}
  • 1、Un1中计算对齐数,char对齐数是1,int对齐数是4,最大对齐数是4。
    2、最大成员大小是6。
    3、最大成员大小不是最大对齐数的整数倍,要对齐,到8满足条件,所以Un1大小是8。
  • 1、Un2中计算对齐数,char对齐数是1,short对齐数是2,最大对齐数是2。
    2、最大成员大小是9。
    3、最大成员大小不是最大对齐数的整数倍,要对齐,到10满足条件,所以Un1大小是10。
    验证如图:
    在这里插入图片描述

4、联合体判断主机大小端序

我们知道,数据在存储的时候超过一个字节,会涉及到顺序的问题。
分为两种:大端序和小端序。

  • 大端序是低位放在高地址,高位放在低地址
  • 小端序是高位放在高地址,低位放在地址
  • 如图在这里插入图片描述

有如下两种判断主机哪种存储方式

(1)、用联合体来判断

int check_sys()
{
	union Un
	{
		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;
}

解析:
1、联合体创建一字节的char类型和4字节int 类型。
2、把int类型i变量赋为1(便于判断。
3、1是4字节int类型,小端16进制是0x 01 00 00 00 ,大端16进制是0x 00 00 00 01,所以在check_sys()函数里直接返回1字节char c即u.c,因为它是一字节,如果c拿到的是01说明是小端,如果c拿到的是00说明是小端。
验证结果:
在这里插入图片描述

(2)、非联合体来判断

int check_sys()
{
	int num = 1;
	return *(char*)#
}
int main()
{
	
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

解析:
1、把int类型i变量赋为1(便于判断。
2、1是4字节int类型,小端16进制是0x 01 00 00 00 ,大端16进制是0x 00 00 00 01,所以在check_sys()函数里直接返回1取地址num,再强制转为char*类型,以便解引用拿到的是1字节的内容,如果c拿到的是01说明是小端,如果c拿到的是00说明是小端。

5、联合体优缺点

优点:

  • 内存使用更为精细灵活,节省内存空间。
    缺点:
  • 变量共存的,不能同时使用,不够包容。

三、枚举

1、枚举概念

C语言提供了一种枚举类型,能够列出所有可能的取值,也叫枚举常量,并给它们取一个名字,关键字是enum,专门来定义枚举类型。
例如:
一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
一年有12个月,也可以一一列举

2、枚举创建和性质

 enum week//枚举类型的声明
{
      Mo= 1//枚举的可能取值
	  Tues,
	  Wed,
	  Th,
	  Fi,
	  Sa,
	  SU,
};
int main()
{
	enum Week we = 5;//定义创建变量
	printf("%d\n", Sa);
	printf("%d\n", Th);
	printf("%d\n", Wed);
	printf("%d\n", Mo);
	printf("%d\n", we);
	return 0;
}

创建时里面的内容用逗号隔开。
性质:
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
如下打印:在这里插入图片描述

3、计算枚举大小

因为枚举的内容是未来可能的取值,要存的是一个值,而这个值是整型的存在,所以大小是4。平台不一样,大小也不一样。

 enum Week
{
      Mo,
	  Tues=5,
	  Wed,
	  Th,
	  Fi,
	  Sa,
	  SU,
	
};
int main()
{
	printf("%d\n", sizeof(enum Week));
	return 0;
}

验证如下在这里插入图片描述

4、 枚举优点

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

相关文章:

  • 2.ROS2笔记-ROS2命令行操作
  • 【Proteus】:入门学习
  • SpringBoot启动流程简析
  • 如何在网站上安装 WordPress
  • java毕业设计社区健康管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
  • 17、Java——汽车租赁系统
  • SpringSecurity系列 - 15 SpringSecurity 记住我 RememberMe
  • 2022出海东南亚:越南电商市场现状及网红营销特点
  • 测试用例设计专栏
  • Intel汇编-间接寻址
  • 测试工程师面试不会做的2门功课,你是不是也是呢?
  • 365天深度学习训练营-第5周:运动鞋品牌识别
  • Centos 7 Kubernetes-1.23 Kubesphere v3.3 Docker Harbor Git 安装过程
  • transformer系列应用于CV论文理解
  • Spring Cloud Alibaba 学习笔记
  • 收藏网友的 源程序下载网
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 《Java编程思想》读书笔记-对象导论
  • CentOS 7 防火墙操作
  • css选择器
  • ECMAScript6(0):ES6简明参考手册
  • export和import的用法总结
  • Java 多线程编程之:notify 和 wait 用法
  • Laravel 中的一个后期静态绑定
  • leetcode讲解--894. All Possible Full Binary Trees
  • mongodb--安装和初步使用教程
  • PAT A1120
  • React16时代,该用什么姿势写 React ?
  • Sass Day-01
  • spring boot下thymeleaf全局静态变量配置
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • Terraform入门 - 1. 安装Terraform
  • Web标准制定过程
  • 初识 beanstalkd
  • 聊聊directory traversal attack
  • 浏览器缓存机制分析
  • 前端工程化(Gulp、Webpack)-webpack
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 微信开放平台全网发布【失败】的几点排查方法
  • 为什么要用IPython/Jupyter?
  • 写给高年级小学生看的《Bash 指南》
  • 阿里云服务器购买完整流程
  • ​马来语翻译中文去哪比较好?
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (超详细)语音信号处理之特征提取
  • (规划)24届春招和25届暑假实习路线准备规划
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .Mobi域名介绍
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .NET 表达式计算:Expression Evaluator