自定义类型:结构体,枚举,联合

news2024/9/27 9:28:29

目录

  • 一、结构体内存对齐
  • 二、位段
    • 2.1 什么是位段
    • 2.2 位段内存分配规则
    • 2.3 位段的跨平台问题
  • 三、枚举
  • 四、联合体
    • 4.1 联合类型的定义
    • 4.2联合的特点
    • 4.3 联合大小的计算
    • 4.4 练习

一、结构体内存对齐

struct s
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct s));
}

这个程序打印的结果是多少呢?可能你会说,呀,简单,结构体大小不就是各成员大小加起来嘛,答案是1+4+1=6。如果你这样想,那你就有必要往下看了。
在这里插入图片描述
答案是12?这个答案是否出乎了你的意料?什么鬼呀?难道1+4+1=12?这什么情况,接下来我们就来探究一下这是为什么?
其实原因是结构体存在内存对齐。详情请看以下图片:
在这里插入图片描述

再来一道

struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	printf("%d\n", sizeof(struct S4));
}

在这里插入图片描述
在这里插入图片描述
为什么存在结构体内存对齐?

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
    总体来说:
    结构体的内存对齐是拿空间来换取时间的做法。
    补充:我们可以自行地修改默认对齐数,操作如下:
#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认

结论:结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。

二、位段

2.1 什么是位段

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

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

A就是一个位段类型。
那位段A的大小是多少呢?
你可能会说,简单,不就是4个整形的大小吗?16个字节!
但是真的是这样吗?显然不是,如果就是这样的话那它和结构体有什么区别啊?干脆用结构体不是更好?那我们看看结果到底是多少?
在这里插入图片描述
我们惊奇地发现,结果居然是8,那么这是为什么呢?那我们就有必要先了解一下位段的内存分配的规则了。

2.2 位段内存分配规则

  1. 位段的成员可以是 int ,unsigned int ,signed int 或者是 char (属于整形家族)类型。
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
    下面我们来看个例子:
    在这里插入图片描述
    通过上面的例子我们就不难解释第一题的答案是8了。
    在这里插入图片描述

2.3 位段的跨平台问题

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

总结
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

三、枚举

枚举的优点:

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

四、联合体

4.1 联合类型的定义

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

//联合类型的声明
union Un
{
	char c;
	int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un));

4.2联合的特点

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

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

	return 0;
}

在这里插入图片描述
在这里插入图片描述

4.3 联合大小的计算

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

union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));

在这里插入图片描述

4.4 练习

判断当前计算机的大小端存储

union un
{
	int i;
	char c;
};

int main()
{
	union un u = { 0 };
	u.i = 0x1;
	//u.c被修改成1说明u.i的存储方式是01 00 00 00
	//因为u.c和u.i的共用第一个字节,如果是大端存储那应该是
	//00 00 00 01,u.i=0x1不会影响到u.c的值,u.c依然为0
	if (u.c==1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

你学会了吗??点点赞支持一下呗!

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

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

相关文章

【Hadoop】HDFS体系结构分析

文章目录1. NameNode2. Secondary NameNode3. DataNodeHDFS主要包含NameNode、Secondary NameNode和DataNode三部分,且这三部分在分布式文件系统中分属不同的机器,其中Secondary NameNode不是必须的,在HA架构中Standby NameNode可以替代它。 …

【深度学习】详解 SimCLR

目录 摘要 一、引言 二、方法 2.1 The Contrastive Learning Framework 2.2. Training with Large Batch Size 2.3. Evaluation Protocol 三、用于对比表示学习的数据增广 3.1 Composition of data augmentation operations is crucial for learning good representa…

5-2中央处理器-指令周期的数据流

文章目录一.指令周期二.数据流向1.取指周期2.间址周期3.执行周期4.中断周期三.指令执行方案1.单指令周期2.多指令周期3.流水线方案一.指令周期 指令周期:CPU从主存中每取出并执行一条指令所需的全部时间。 此处:取指周期取指令指令译码 指令周期常用若…

SSM整合(Spring + SpringMVC + MyBatis)

SSM Spring SpringMVC MyBatis 准备数据库 SET FOREIGN_KEY_CHECKS0; DROP TABLE IF EXISTS user; CREATE TABLE user (id int(11) NOT NULL AUTO_INCREMENT,username varchar(20) NOT NULL COMMENT 用户名,password varchar(255) NOT NULL COMMENT 密码,real_name varchar(…

Linux常用命令——startx命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) startx 用来启动X Window 补充说明 startx命令用来启动X Window,实际上启动X Window的程序为xinit。 语法 startx(参数)参数 客户端及选项:X客户端及选项;服务器及选项&a…

[LeetCode周赛复盘] 第 329 场周赛20230122

[LeetCode周赛复盘] 第 329 场周赛20230122 一、本周周赛总结二、 [Easy] 6296. 交替数字和1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6297. 根据第 K 场考试的分数排序1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6298. 执行逐位运算使字符串相等1. 题目描述2. 思路…

深入理解 OpenMP 线程同步机制

深入理解 OpenMP 线程同步机制 前言 在本篇文章当中主要给大家介绍 OpenMP 当中线程的同步和互斥机制,在 OpenMP 当中主要有三种不同的线程之间的互斥方式: 使用 critical 子句,使用这个子句主要是用于创建临界区和 OpenMP 提供的运行时库…

连续系统的数字PID控制仿真-1

被控对象为一电机模型传递函数:式中,J 0.0067;B0.10。采用M函数的形式,利用ODE45的方法求解连续对象方程,输入指令信号为yd(k)0.50sin(2*3.14*t),采用PID控制方法设计控制器,其中kp20.0 ,kd0.50。PID正弦跟…

12个开源的后台管理系统

1. D2admin 开源地址:https://github.com/d2-projects/d2-admin 文档地址:https://d2.pub/zh/doc/d2-admin/ 效果预览:https://d2.pub/d2-admin/preview/#/index 开源协议:MIT 2. vue-element-admin 开源地址:htt…

Kettle(3):快速入门

1 需求 有一个txt文件,内容如下: id,name,age,gender,province,city,region,phone,birthday,hobby,register_date 392456197008193000,张三,20,0,北京市,昌平区,回龙观,18589407692,1970-8-19,美食;篮球;足球,2018-8-6 9:44 267456198006210000,李四,2…

Vue3 – Composition API

1、认识CompositionAPI 1.1、Options API的弊端 在Vue2中,我们编写组件的方式是Options API: Options API的一大特点就是在对应的属性中编写对应的功能模块;比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性…

【快速简单登录认证】SpringBoot使用Sa-Token-Quick-Login插件快速登录认证

一、解决的问题 Sa-Token-Quick-Login 可以为一个系统快速的、零代码 注入一个登录页面 试想一下,假如我们开发了一个非常简单的小系统,比如说:服务器性能监控页面, 我们将它部署在服务器上,通过访问这个页面&#xf…

学习字符串函数和内存函数必看

字符串函数 1.strlen函数 strlen库函数 #include<stdio.h> #include<string.h> int main() {char arr[] "abc";char arr1[] { a,b,c };int len strlen(arr);int len1 strlen(arr1);//没有\0就无法停止printf("%d\n",len);printf("%…

VUE中的provide和inject用法

一、Vue中 常见的组件通信方式可分为三类 父子通信 父向子传递数据是通过 props&#xff0c;子向父是通过 events&#xff08;$emit&#xff09;&#xff1b; 通过父链 / 子链也可以通信&#xff08;$parent / $children&#xff09;&#xff1b; ref 也可以访问组件实例&…

XLSReadWriteII 写了一个DBGrdiEh创建EXCEL表的函数

XLSReadWriteII 写了一个DBGrdiEh创建EXCEL表的函数 自己通过XLSReadWriteII&#xff0c;写了一个由DBGridEh数据集&#xff0c;通过参数调用&#xff0c;创建EXCEL表格的函数&#xff0c;通过调用的参数设置&#xff0c;可以较为方便地&#xff0c;创建指定数据集的常用EXCEL表…

自动化和Selenium

作者&#xff1a;~小明学编程 文章专栏&#xff1a;测试开发 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 什么是自动化&#xff0c;我们为什么需要自动化的测试&#xff1f; 为什么选择selenium来作为我们的web自动化测试的工具&#xff1f; 定位元素…

【C进阶】找单身狗

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C语言进阶 ⭐代码仓库&#xff1a;C Advanced 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们…

Spring事务(事务的实现、隔离级别、传播机制)

目录 一、事务的定义和意义 二、事务的实现 1、MySQL事务的回顾 2、Spring声明式事务&#xff08;利⽤注解⾃动开启和提交事务&#xff09; 前置知识及概念 实例分析&#xff1a;事务的回滚 &#x1f514;特殊情况&#xff08;无自动回滚&#xff09; &#x1f514;事务…

VueJs中如何使用provide与inject

前言在vue2.0里面provide与inject是以选项式(配置)API的方式在组件中进行使用的,解决的是跨组件(祖孙)间通信的一种方式也就是父子组件间的通信,父组件上通过自定义属性,而子组件间通过props这种方式接收,如果想要一层一层的传递,这种方式就会比较麻烦,不灵活provide与inject就…

Linux下的进程信号

目录 信号背景&#xff1a; 信号产生前 Core Dump 信号产生中 信号产生后 其他概念 不可重入函数 volatile关键字 SIGCHLD 17号信号 信号背景&#xff1a; 在生活中处处都存在的信号&#xff0c;比如信号灯&#xff0c;要想处理信号&#xff0c;我们就必须具备两种…