自定义类型详解(结构体,位段,枚举,联合体)

目录

结构体

结构体的自引用

匿名结构体

结构体内存对齐

结构体内存对齐的意义

修改默认对齐数

位段

位段的内存分配

位段的跨平台问题

位段的应用

枚举

枚举类型的定义

枚举和#define的区别

联合体(共用体)

联合体大小的计算


结构体

C语言学习笔记之结构体(一)-CSDN博客

结构体的自引用

错误的自引用方式:

这种自引用方式无法确定结构体的大小。


正确的自引用方式:

匿名结构体

        声明时没有指定结构体标签(tag)的结构体称为匿名结构体,匿名结构体只能在声明的时候定义,通常只使用一次。

声明了两个相同的匿名结构体时,编译器会把它们当成两个不同的类型:

结构体内存对齐

示例:

#include <stdio.h>

int main()
{
	//练习1
	struct S1
	{
		char c1;
		int i;
		char c2;
	};
	printf("%d\n", sizeof(struct S1)); //按照常理来说,大小是1 + 4 + 1 = 6字节
	//练习2
	struct S2
	{
		char c1;
		char c2;
		int i;
	};
	printf("%d\n", sizeof(struct S2)); //按照常理来说,大小是1 + 1 + 4 = 6字节
	//练习3
	struct S3
	{
		double d;
		char c;
		int i;
	};
	printf("%d\n", sizeof(struct S3));//按照常理来说,大小是8 + 1 + 4 = 13字节
	//练习4-结构体嵌套问题
	struct S4
	{
		char c1;
		struct S3 s3;
		double d;
	};
	printf("%d\n", sizeof(struct S4));//按照常理来说,大小是1 + 13 + 8 = 22字节
	return 0;
}

        我们可以发现,结构体真实的大小与预期大相径庭,说明结构体大小的计算并不是单纯的把所有结构成员的大小相加而得。       

        结构体大小的计算与结构体内存对齐规则有关。结构体内存对齐规则如下:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到偏移量是某个数字(对齐数)的整数倍的地址处。
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

        对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。

  • VS中默认的值为8
  • Linux中没有默认对齐数,对齐数就是成员自身的大小

偏移量:相较于起始地址的位移量。

offsetof()是一个宏,可以用来计算结构体成员相较起始位置的偏移量:

#include <stdio.h>
#include <stddef.h>

int main()
{
	//练习1
	struct S1
	{
		char c1;
		int i;
		char c2;
	};
	printf("%d\n", offsetof(struct S1, c1));//可以计算结构体成员相较于结构体起始位置的偏移量
	printf("%d\n", offsetof(struct S1, i));
	printf("%d\n", offsetof(struct S1, c2));
	//printf("%d\n", sizeof(struct S1)); //按照常理来说,大小是1 + 4 + 1 = 6字节

	return 0;
}

分析上面的示例:

#include <stdio.h>

int main()
{
	//练习1
	struct S1
	{
		char c1;	//第一个成员变量放0偏移处。0 - 1
		int i;		//成员变量大小是4,默认对齐数8,所以对齐数是4,应该放在偏移量是4的整数倍处,4 - 7
		char c2;	//成员变量大小是1,默认对齐数8,所以对齐数是1,应该放在偏移量是1的整数倍处,8
	};
	printf("%d\n", sizeof(struct S1)); //0 - 8大小为9,结构体大小是最大对齐数即4的整数倍,因此补三个字节为 12 
	//练习2
	struct S2
	{
		char c1;	//第一个成员变量放0偏移处。0 - 1
		char c2;	//成员变量大小是1,默认对齐数8,所以对齐数是1,应该放在偏移量是1的整数倍处,2
		int i;		//成员变量大小是4,默认对齐数8,所以对齐数是4,应该放在偏移量是4的整数倍处,4 - 7
	};
	printf("%d\n", sizeof(struct S2)); //0 - 7大小为8,结构体大小是最大对齐数即4的整数倍,8是4的整数倍,所以结构体大小为8
	//练习3
	struct S3
	{
		double d;	//第一个成员变量放0偏移处。0 - 7
		char c;		//成员变量大小是1,默认对齐数8,所以对齐数是1,应该放在偏移量是1的整数倍处,8
		int i;		//成员变量大小是4,默认对齐数8,所以对齐数是4,应该放在偏移量是4的整数倍处,12 - 15
	};
	printf("%d\n", sizeof(struct S3));//0 - 15大小为16,结构体大小是最大对齐数即8的整数倍,16是8的整数倍,所以结构体大小为16
	//练习4-结构体嵌套问题
	struct S4
	{
		char c1;		//第一个成员变量放0偏移处。0 - 1
		struct S3 s3;	//成员变量大小是16,最大对齐数是8,默认对齐数8,所以对齐数是8,应该放在偏移量是8的整数倍处,8 - 23
		double d;		//成员变量大小是8,默认对齐数8,所以对齐数是8,应该放在偏移量是8的整数倍处,24 - 31
	};
	printf("%d\n", sizeof(struct S4));//0 - 31大小为32,结构体大小是最大对齐数即8的整数倍,32是8的整数倍,所以结构体大小为32
	return 0;
}

结构体内存对齐的意义

  •  平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  • 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
        总体来说:结构体的内存对齐是拿空间来换取时间的做法。

        因此在设计结构体的时候,我们既要满足对齐,又要节省空间,就需要让占用空间小的成员尽量集中在一起。

修改默认对齐数

        我们可以通过预处理指令#pragma来修改默认对齐数。

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S1)); //12
	printf("%d\n", sizeof(struct S2)); //6
	return 0;
}

位段

        C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。

        位段的声明和结构是类似的,有两个不同:

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的成员名后边有一个冒号和一个数字,数字表示分配多少个bit 位的空间。

位段的内存分配

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

示例:

//一个例子
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//空间是如何开辟的?

        可以看出,在vs2013中,位段中的成员在内存中从右向左分配空间,且当一个位段剩余空间无法容纳其他位段成员时会舍弃剩余空间并重新开辟一个新的空间存储位段成员。

位段的跨平台问题

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

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

位段的应用

网络底层数据传输:

枚举

        枚举顾名思义就是一一列举,把可能的取值一一列举。比如我们现实生活中:一周的星期一到星期日是有限的7天,可以一一列举;性别有:男、女、保密,也可以一一列举;月份有12个月,也可以一一列举。

枚举类型的定义

        以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。{}中的内容是枚举类型的可能取值,也叫 枚举常量 。这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。例如:

枚举和#define的区别

  1. 作用时期和存储形式不同,#define定义的标识符是在预处理的阶段完成替换工作的,它替换代码段的文本,程序运行的过程中标识符已经不存在了。而枚举是在程序运行之后才起作用的,枚举常量存储在数据段的静态存储区里。
  2. #define定义的标识符常量类型无关,而枚举有类型检查,更加严谨。
  3. #define定义的标识符在预处理的阶段完成替换,所以不易调试;而枚举类型在运行过程中生效,所以便于调试。
  4. #define一次只能定义一个标识符,枚举一次可以定义多个常量,使用更方便。

联合体(共用体)

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

联合体大小的计算

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

比如:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

斐讯N1刷OpenWRT并安装内网穿透服务实现远程管理旁路由

文章目录 前言1. 制作刷机固件U盘1.1 制作刷机U盘需要准备以下软件&#xff1a;1.2 制作步骤 2. N1盒子降级与U盘启动2.1 N1盒子降级2.2 N1盒子U盘启动设置2.3 使用U盘刷入OpenWRT2.4 OpenWRT后台IP地址修改2.5 设置旁路由&无线上网 3. 安装cpolar内网穿透3.1 下载公钥3.2 …

08Django项目--用户管理系统--查(前后端)

对应视频链接点击直达 TOC 一些朋友加我Q反馈&#xff0c;希望有每个阶段的完整项目代码&#xff0c;那从今天开始&#xff0c;我会上传完整的项目代码。 用户管理&#xff0c;简而言之就是用户的增删改查。 08项目点击下载&#xff0c;可直接运行&#xff08;含数据库&…

3个完美恢复方案!苹果手机恢复视频就是这么简单

在日常生活中&#xff0c;我们使用苹果手机记录了许多珍贵的视频&#xff0c;包括家庭聚会、旅行经历等。但是我们不小心删除了&#xff0c;在“最近删除”文件夹中也找不到。 别担心&#xff0c;这并不困难&#xff01;无论您是意外删除了重要的视频还是遇到了其他数据丢失问…

PawSQL: 企业级SQL审核工具的新玩家

随着数据库应用在企业中的广泛使用&#xff0c;确保SQL代码质量的重要性日益凸显。现有的SQL审核工具很多&#xff0c;包括Yearning、goInception、Bytebase、爱可生的SQLE、云和恩墨的SQM等等&#xff0c;但是它们或者规则覆盖度、或者是在正确率等方面存在明显不足&#xff1…

【stm32】江科协听课笔记

[3-1] GPIO输出_哔哩哔哩_bilibili 5.GPIO输出 这里&#xff0c;寄存器就是一段特殊的存储器&#xff0c;内核可以通过APB2总线队寄存器进行读写&#xff0c;这样就可以完成输出/读取电平的功能。寄存器的每一位对应一个引脚&#xff0c;stm32是32位的&#xff0c;这里的寄存器…

K8S有了Service,为什么还要Ingress?

1、有了Service为什么还要Ingress? NodePort对外暴露端口存在的不足&#xff1a; 一个端口只能一个服务使用, 端口需要提前规划。 随着业务扩展, 端口的管理将是一个头疼的问题 只支持4层的负载均衡 LoadBalancer存在的不足&#xff1a; 贵、贵、贵。 要上云(俗话说上云…

前端Vue自定义顶部搜索框:实现热门搜索与历史搜索功能

前端Vue自定义顶部搜索框&#xff1a;实现热门搜索与历史搜索功能 摘要&#xff1a; 随着前端开发复杂性的增加&#xff0c;组件化开发成为了提高效率和降低维护成本的有效手段。本文介绍了一个基于Vue的前端自定义顶部搜索框组件&#xff0c;该组件不仅具备基本的搜索功能&am…

Android端 可使用Yolov5训练 路标识别

相信大家对于路标识别&#xff0c;红绿灯识别&#xff0c;图形识别opencv中也是一件烦人的事情&#xff0c;其参数是及其受到现实环境因素的影响的&#xff0c;那么今天我就给大家推荐一种方式&#xff0c;缺点是周期长&#xff0c;但其优点是如果训练效果好久对于环境的各种变…

安卓开发日志采集和分析面面谈

日志面面谈 为什么需要日志 复现问题&#xff0c;回溯到问题产生时候的系统状态&#xff0c;有利于定位和分析问题。 安卓日志有哪些&#xff1f; cpu 关注的纬度&#xff1a; 单个应用使用系统cpu分配温度 有什么用&#xff1a; App卡顿、ANRApp异常退出 怎么用&…

记一次 .NET某企业数字化平台 崩溃分析

一&#xff1a;背景 1. 讲故事 前些天群里有一个朋友说他们软件会偶发崩溃&#xff0c;想分析看看是怎么回事&#xff0c;所幸的是自己会抓dump文件&#xff0c;有了dump就比较好分析了&#xff0c;接下来我们开始吧。 二&#xff1a;WinDbg 分析 1. 程序为什么会崩溃 win…

从0开始回顾ElasticSearch

1 elasticsearch概述 1.1 elasticsearch简介 官网: https://www.elastic.co/ ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的…

芯课堂 | 芯片抗干扰测试方案

MCU芯片对所在环境中存在的电磁干扰须具有一定程度的抗扰度&#xff0c;确保使用该芯片的设备能正常运行。国际电工委员会&#xff08;IEC&#xff09;制定了多项国际标准&#xff0c;其中与MCU芯片相关的有IEC61000-4-2 &#xff08;静电&#xff09;&#xff0c; IEC61000-4-…

RK3568笔记二十六:音频应用

若该文为原创文章&#xff0c;转载请注明原文出处。 一、介绍 音频是我们最常用到的功能&#xff0c;音频也是 linux 和安卓的重点应用场合。 测试使用的是ATK-DLR3568板子&#xff0c;板载外挂RK809 CODEC芯片&#xff0c;RK官方驱动是写好的&#xff0c;不用在自己重新写。…

家居的3D交互展示用什么工具比较专业?

家居的3D交互展示可以使用多种专业工具来实现&#xff0c;这些工具不仅能够在手机和电脑上查看&#xff0c;还能在手机上进行交互操作&#xff0c;如放缩、旋转等&#xff0c;并且支持高清流畅的画面展示。以下是一些推荐的3D交互展示工具&#xff1a; 1、在线3D展示软件&…

牛客热题:寻找第K大

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;寻找第K大题目链接方法一&#…

Docker基础篇之Docker入门介绍

文章目录 1. 为什么要有Docker&#xff1f;2. Docker简介3. 容器和虚拟机的区别4. Docker下载 1. 为什么要有Docker&#xff1f; 假设我们现在正在开发一个项目&#xff0c;使用的是一台笔记本电脑而且开发环境具有特定的配置&#xff0c;其他开发人员身处的环境配置也各不相同…

ZeroTier+Nomachine远程

目录 前述&#xff1a;一、Zero二、Nomachine 前述&#xff1a; 需要远程控制时&#xff0c;服务端与客户端都必须下载这两个软件&#xff01;远程主机&#xff08;被控制的主机&#xff09;和远程客户端&#xff08;控制主机的用户&#xff09;都必须具有网络连接&#xff0c;…

地铁判官:啥时候B端系统界面,也出个“判官”,讲好不准打脸。

小编所在的城市——山东青岛&#xff0c;出了个地铁判官&#xff0c;我看了视频&#xff0c;哈哈哈&#xff0c;俗世的判断标准就是那么简单直接&#xff0c;而放到B端系统那就难说啦。 如何判断B端系统的优劣&#xff0c;各位看官&#xff0c;各抒己见吧。 判断B端系统界面的…

如何深入理解、应用及扩展 Twemproxy?no.15

Twemproxy 架构及应用 Twemproxy 是 Twitter 的一个开源架构&#xff0c;它是一个分片资源访问的代理组件。如下图所示&#xff0c;它可以封装资源池的分布及 hash 规则&#xff0c;解决后端部分节点异常后的探测和重连问题&#xff0c;让 client 访问尽可能简单&#xff0c;同…

揭秘:如何使用Python统计女友生日还剩几天?

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;为何需要统计生日天数&#xff1f; 二、需求分析与准备 1. 用户输入格…