【C语言】自定义类型——结构体
目录
一、结构体的类型的声明
二、结构体变量的创建和初始化
三、匿名结构体类型
四、结构体自引用
五、结构体内存对齐
(1)对齐规则
(2)计算结构体大小练习
(3)需要内存对齐的原因
(4)修改默认对齐数
六、结构体传参
七、结构体实现位段
(1)什么是位段
(2)位段的内存分配
(3)位段的跨平台的问题
(4)位段的应用
(5)位段不能使用取地址符&
一、结构体的类型的声明
形式如下:
struct tag
{member - list; // 成员列表
}variable - list; // 变量列表,属于全局变量,也可以没有
例如,定义一个学生结构体类型,并创建了全局变量s1:
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}s1;
二、结构体变量的创建和初始化
// struct Stu 类型的定义
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};
int main()
{//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的顺序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex ="⼥" };printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);return 0;
}
三、匿名结构体类型
省略掉结构体标签 (tag):
struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], * p;
因为匿名结构体类型没有名字,所以如果没有对匿名结构体重命名的话,只能使用一次(创建一次变量,即声明结构体类型的时候就创建)。并且两个成员相同的匿名结构体类型不相同,如下:
四、结构体自引用
结构体中不能包含同类型的结构体成员。因为结构体类型还没完全声明结束就开始使用同类型是不行的(不清楚它的大小),相当于一个类型还不存在的时候就开始使用这个类型,并且仔细想想,这样声明的结构体的大小是无穷大的,如下:
如果结构体想自引用同类型,只能定义为指针类型。指针类型是内置类型,本来就存在,大小也可知(4 或 8字节),如下:
struct Node
{int data;struct Node* next;
};
如果是对匿名结构体重命名,就算是包含同类型的指针类型,也是不行的,因为在重命名 Node 之前都还不知道这个匿名函数叫啥,就使用 Node 的指针,如下:
因此,结构体自引用不能使用匿名结构体,改为如下就正确了:
typedef struct Node
{int data;struct Node* next;
}Node;
五、结构体内存对齐
是计算结构体大小的规则。
(1)对齐规则
① 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
② 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值。
VS 中默认的值为 8。
Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小。
③ 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的) 的
整数倍。
④ 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
(2)计算结构体大小练习
练习一:
结构体S1的大小:
结构体S2的大小:
S1 和 S2 类型的成员一模一样,但是 S2 比 S1 占的空间更小,是因为变量 c1 和 c2是放在一起的。因此,让占用空间小的成员尽量集中在一起,更节省空间。
练习二:
结构体S3的大小:
结构体S4的大小:
(3)需要内存对齐的原因
① 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,比如 int 类型数据只能在固定地址处存取,否则抛出硬件异常。为了提高代码的可移植性(对所有硬件平台都适用),需要内存对齐。
② 性能原因
访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。比如对于结构体 s:
struct s{char c;int i;
};
如果内存不对齐:
如果内存对齐:
32位机器上,数据总线是32根,读、写数据的时候,一次就读/写32位(4个字节)。如果不对齐,要读两个字节,才能拼凑出 i;如果对齐,发现第一个字节没有,直接跳到第二个字节,读取一次就可以得到 i 。因此,内存对齐更能节省读取时间(用空间换时间)。
(4)修改默认对齐数
使用 #pragma 预处理指令,示例:
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//还原为默认对齐数
对齐数通常是 2 的 x 次方,如 1,2,4,8,不能随意设置。
六、结构体传参
一种是传结构体本身,一种是传结构体的地址,如下:
struct S
{int data[1000];int num;
};
struct S s = { {1,2,3,4}, 1000 };//结构体传参
void print1(struct S s)
{printf("%d\n", s.num);
}//结构体地址传参
void print2(struct S* ps)
{printf("%d\n", ps->num);
}int main()
{print1(s); //传结构体print2(&s); //传地址return 0;
}
因为函数传参,参数要压栈,会有空间和时间上的系统开销。如果结构体比较大,传结构体本身,开销就比较大;如果传结构体地址,指针只有 4 个字节,开销就比较小。因此,结构体传参,最好传地址。
七、结构体实现位段
(1)什么是位段
与结构体类似,但有两个不同:
- 成员类型必须是 int、unsigned int、signed int、char,C99 标准中也可以是其它类型。
- 成员名后是 冒号 + 数字。
形式如下:
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
位段 A 大小是?如果按照结构体的内存对齐的方法计算,4 * 4 = 16 个字节。看看运行结果:
显然不是按结构体的方式计算内存大小,实际上位段的位表示二进制位,冒号后面的数字是该成员的大小,比如 _a 占 2 bit 。但是位段 A 的所有成员的大小加起来是 2+5+10+30 = 47,用 6 个字节(68 bit)就够了,为什么是8 字节呢?请看下节。
(2)位段的内存分配
- 位段每次开辟 4 个字节(int)或者 1 个字节(char)。
- 位段的不确定因素很多(比如每次开辟从左还是右存储;每次开辟的空间不够下一个成员使用,剩余的空间要不要接着使用),不可跨平台,注重可移植性的代码要避免用位段。
如下例子:
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };printf("%d\n", sizeof(s));s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}
VS中的规则:
- char 类型,每次开辟一个字节。
- 一个字节内,从右向左使用。
- 一个字节内剩余的 bit 不够下一个成员使用,浪费掉并开辟新的一个字节存放。
定义结构体变量 s 并初始化位段成员值为 0,开辟如下的空间(因为位段是 char 类型,每次开辟 1 个字节空间):
一共是3个字节。再给所有位段成员赋值(超出的截断,不够的补0):
调试验证,每 4 bit 是一个十六进制数,那么上面的值用十六进制表示就是(62 03 04):
调试结果与理论一致:
运行结果:
解决(1)中遗留的问题:
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
因为位段成员是 int 类型,所以每次开辟 4 个字节(一共开辟了 8 个字节):
(3)位段的跨平台的问题
- int 位段被当成有符号还是无符号数是不确定的。
- 位段中最大的数目不确定。(32位机器最大位是32,16位机器最大位是16。如果是16位机器,像下面这样写就是错误的,因为 30 位已经超过了最大的 16 位;但在 32 位机器上是正确的。)
struct S
{int a : 30;
};
- 每次开辟的空间,是从左向右,还是从右向左使用是不确定的。
- 每次开辟的空间,剩余的空间不够下一个位段成员使用时,是浪费掉还是接着使用是不确定的。
总结:位段(可以设置使用的位)比结构体(固定的字节)更节省空间,但存在跨平台的问题。
(4)位段的应用
数据在网络上传输,需要遵守网络协议,网络协议中有个IP数据报的概念(相当于快递包裹上的各种邮寄信息,有发件人、收件人,才知道包裹从哪发、发给谁),下面就是IP数据报的格式:
如果不用位段,版本(4位)是整型,分配 4 个字节空间,就会浪费 28 位空间。可以发现每一行信息需要的空间加起来刚好是 32 位(4 个字节),刚好是一个整型的大小,设计成位段,将会没有一点空间浪费。
IP数据报追求节省空间,因为使用更小的空间,网络越通畅。
(5)位段不能使用取地址符&
位段中几个成员共用一个字节,而内存中是按字节编址的,所以一个字节内的 bit 没有地址,就不能对位段成员取地址,如下会报错:
因此,不能使用 scanf 直接给位段成员输入值,只能先输入值放在变量中,变量再赋值给位段成员:
相关文章:

【C语言】自定义类型——结构体
目录 一、结构体的类型的声明 二、结构体变量的创建和初始化 三、匿名结构体类型 四、结构体自引用 五、结构体内存对齐 (1)对齐规则 (2)计算结构体大小练习 (3)需要内存对齐的原因 (4…...

MySQL练手题--日期连续类型(困难)
一、准备工作 Create table If Not Exists Failed (fail_date date); Create table If Not Exists Succeeded (success_date date); Truncate table Failed; insert into Failed (fail_date) values (2018-12-28); insert into Failed (fail_date) values (2018-12-29); inser…...

【AD24报错】运行DRC后出现 Un-Routed Net Constraint ### Net Not Assigned 的解决方案
AD24在运行PCB设计规则检查(DRC)后报错 Un-Routed Net Constraint ### Net Not Assigned 的解决方案 一、解决方案二、可能会报错Dead Copper的因素三、可能会报错Un-Routed Net Constraint的因素 Un-Routed Net Constraint ### Net Not Assigned 的解决…...

Linux嵌入式驱动开发指南(速记版)---Linux基础篇
第一章 Ubuntu系统入门 1.1 Linux磁盘管理 1.1.1 Linux磁盘管理基本概念 关键词: Linux 磁盘管理 挂载点 /etc/fstab文件 分区 ls /dev/sd* 联系描述: Linux 磁盘管理体系通过“挂载点”概念替代了 Windows 中的“分区”概念,将硬盘部分以文…...

PDF——压缩大小的方法
方法一:QQ浏览器->格式转换->PDF转纯图PDF...

无监督神经组合优化的扩散模型框架
文章目录 Abstract1. Introduction2. Problem Description2.1 无监督神经组合优化3. Neural Probabilistic Optimization Objective for Approximate Likelihood Models3.1 具有联合变分上界的训练扩散模型Abstract 从离散集合的不可处理分布中进行采样,而不依赖相应的训练数据…...

Web前端开发
首先打开,VS code新建文件夹,命名为index.HTML,然后先对内容进行输入,也就是在波蒂里面进行输入,将社会主义核心价值观的基本内容输入好,然后在页面呈现的效果是这样的 因为有一个alert警告框标签ÿ…...

transformer模型进行英译汉,汉译英
上面是在测试集上的表现 下面是在训练集上的表现 上面是在训练集上的评估效果 这是在测试集上的评估效果,模型是transformer模型,模型应该没问题,以上的是一个源序列没加结束符和加了结束符的情况。 transformer源序列做遮挡填充的自注意力,这就让编码器的输出中每个token的语…...
python 异步读取文件,速度变快了吗
“python 异步读取文件,速度变快了吗” 当我问出这个问题,大部分人第一反应应该是python新人,不懂异步 首先说一下我对异步的理解: asyncio 是 gevent greenlet 的组合gevent 底层使用了libev、selectors 模块,这两…...

【Python】Anaconda插件:Sublime Text中的Python开发利器
上班的时候没人问我苦不苦,下班的时候总有人问为什么走这么早。 Anaconda 是一个专为Sublime Text打造的开源Python开发插件,旨在为开发者提供类似于IDE的丰富功能,提升Python编码效率。该插件提供了代码补全、语法检查、代码片段提示等多项…...

Python酷库之旅-第三方库Pandas(123)
目录 一、用法精讲 546、pandas.DataFrame.ffill方法 546-1、语法 546-2、参数 546-3、功能 546-4、返回值 546-5、说明 546-6、用法 546-6-1、数据准备 546-6-2、代码示例 546-6-3、结果输出 547、pandas.DataFrame.fillna方法 547-1、语法 547-2、参数 547-3、…...

IEEE投稿 IEEE Geoscience and Remote Sensing Letters
IEEE 应用地球观测与遥感专题杂志 journal of Selected Topics in Applied Earth Observations and Remote Sensing IEEE 文章提交流程 撰写文章并准备好图形后,您可以提交文章以供审核。请按照以下步骤完成 IEEE 文章提交流程。 选择目标期刊 如果文章超出期刊范围…...
【华为杯】2024华为杯数模研赛D题 解题思路
题目 大数据驱动的地理综合问题 问题1: 19902020年间中国范围内降水量和土地利用/土地覆被类型的时空演化特征描述? 解题思路 详细分析:此问题要求对降水量(连续变化变量)和土地利用/覆被(离散变化变量)进行时空演…...

Ubuntu20.04 搜索不到任何蓝牙设备
电脑信息 联想扬天YangTianT4900k 问题描述 打开蓝牙之后,一直转圈,搜索不到任何蓝牙设备 排查 dmesg | grep -i blue 有如下错误: Bluetooth: hci0: RTL: unknown IC info, lmp subver 8852, hci rev 000b, hci ver 000b lsusb 芯片型号如…...
【2024】MySQL账户管理
当前MySQL版本为: mysql> select version(); ----------- | version() | ----------- | 8.4.2 | ----------- 1 row in set (0.01 sec)目录 创建普通用户为用户授权查看用户权限修改用户权限修改用户密码删除用户 创建普通用户 使用CREATE USER语句创建用户…...

轻量级流密码算法Trivium
轻量级流密码算法Trivium 0x0 Trivium算法简介 Trivium算法是由C.D Canniere和B.Preneel共同设计的一套对称加密算法,Trivium密码算法采用了分组密码和非线性反馈移位寄存器的设计思路。该密码算法总共288比特的内部状态,其中有…...

MapReduce基本原理
目录 整体执行流程 Map端执行流程 Reduce端执行流程 Shuffle执行流程 整体执行流程 八部曲 读取数据--> 定义map --> 分区 --> 排序 --> 规约 --> 分组 --> 定义reduce --> 输出数据 首先将文件进行切片(block)处理ÿ…...

数据结构之栈(python)
栈(顺序栈与链栈) 1.栈存储结构1.1栈的基本介绍1.2进栈和出栈1.3栈的具体实现1.4栈的应用例一例二例三 2.顺序栈及基本操作(包含入栈和出栈)2.1顺序栈的基础介绍2.2顺序栈元素入栈2.3顺序栈元素出栈2.4顺序栈的表示和实现 3.链栈及…...
浅谈人工智能之基于HTTP方式调用本地QWen OPenAI接口(Java版)
浅谈人工智能之基于HTTP方式调用本地QWen OPenAI接口(Java版) 概述 Qwen是阿里云推出的一款超大规模语言模型,其强大的自然语言处理能力使其成为开发智能应用的热门选择。本文将指导你如何使用Java通过HTTP方式调用Qwen的OpenAI接口&#x…...

【python设计模式7】行为型模式2
目录 策略模式 模板方法模式 策略模式 定义一个个算法,把它们封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。角色有:抽象策略、具体策略和上下文。 from abc import abstractmethod, ABCMeta from datetim…...

Cilium动手实验室: 精通之旅---4.Cilium Gateway API - Lab
Cilium动手实验室: 精通之旅---4.Cilium Gateway API - Lab 1. 环境准备2. API 网关--HTTP2.1 部署应用2.2 部署网关2.3 HTTP路径匹配2.4 HTTP头匹配 3. API网关--HTTPS3.1 创建TLS证书和私钥3.2 部署HTTPS网关3.3 HTTPS请求测试 4. API网关--TLS 路由4.1 部署应用4.2 部署网关…...

20250606-C#知识:委托和事件
C#知识:委托和事件 使用委托可以很方便地调用多个方法,也方便将方法作为参数进行传递 1、委托 委托是方法的容器委托可以看作一种特殊的类先定义委托类,再用委托类声明委托变量,委托变量可以存储方法 delegate int Calculate(in…...
微服务常用日志追踪方案:Sleuth + Zipkin + ELK
在微服务架构中,一个用户请求往往需要经过多个服务的协同处理。为了有效追踪请求的完整调用链路,需要一套完整的日志追踪方案。Sleuth Zipkin ELK 组合提供了完整的解决方案 Sleuth:生成和传播追踪IDZipkin:收集、存储和可视化…...

汽车的安全性能测试:试验台铁地板的重要性
汽车的安全性能测试是非常重要的,其中试验台铁地板的设计和材料选择起着至关重要的作用。试验台铁地板是指在进行汽车碰撞、侧翻等试验时,用于支撑汽车底部和提供稳定支撑的重要部件。 在进行汽车碰撞试验时,试验台铁地板的设计和材料需要具…...

Python 3.11.9 安装教程
前言 记录一下Windows环境下Python解释器的安装过程。 安装过程 1、安装程序下载 打开Python官网: 点击Downloads,选择Windows: 页面中找到需要的3.11.9版本,点击Download Windows installer (64-bit)下载: 2、…...

重启路由器ip不变怎么回事?原因分析与解决方法
在日常生活中,我们经常会遇到网络问题,而重启路由器是解决网络故障的常用方法之一。然而,有些用户发现,即使重启了路由器,自己的IP地址却没有变化,这让他们感到困惑。那么,重启路由器IP不变是怎…...

【应用】Ghost Dance:利用惯性动捕构建虚拟舞伴
Ghost Dance是葡萄牙大学的一个研究项目,研究方向是探索人与人之间的联系,以及如何通过虚拟舞伴重现这种联系。项目负责人Cecilia和Rui利用惯性动捕创造出具有流畅动作的虚拟舞伴,让现实中的舞者也能与之共舞。 挑战:Ghost Danc…...
hbase资源和数据权限控制
hbase适合大数据量下点查 https://zhuanlan.zhihu.com/p/471133280 HBase支持对User、NameSpace和Table进行请求数和流量配额限制,限制频率可以按sec、min、hour、day 对于请求大小限制示例(5K/sec,10M/min等),请求大小限制单位如…...

兰亭妙微 | 医疗软件的界面设计能有多专业?
从医疗影像系统到手术机器人控制界面,从便携式病原体检测设备到多平台协同操作系统,兰亭妙微为众多医疗设备研发企业,打造了兼具专业性与可用性的交互界面方案。 我们不仅做设计,更深入理解医疗场景的实际需求: 对精…...
Neo4j图数据库管理:原理、技术与最佳实践
Neo4j作为领先的图数据库,其高效管理是发挥图计算潜力的关键。本文基于官方技术文档,深入探讨其管理原理、核心操作及生产环境最佳实践。 一、 管理架构与核心原理 多数据库架构 系统数据库 (system):管理元数据(用户、角色、权限、其他数据库信息)。标准数据库:存储实际…...