【C语言进阶篇】自定义类型:结构体(上)

目录

1. 结构体类型的声明

    ​编辑

    1.1 结构体的创建和初始化

    1.2 结构体的特殊声明

    1.3 结构体的自引用

  2. 结构体内存对齐

    2.1 对齐规则

    2.2 为什么存在内存对齐

    2.3 修改默认对齐数


在我们描述简单对象的时候,使用已有的类型就足够了,比如:

  但是当我们想要描述复杂对象的时候,光靠已有的这些变量似乎没法描述清楚,比如我们没法简单地用一个整型变量描述一个人:人有名字,需要用字符串描述;人还有年龄,需要用一个整型描述;人还可能有电话号码,也需要用一个字符串来描述。这个时候,就需要用到结构体类型了。

1. 结构体类型的声明
    
    1.1 结构体的创建和初始化

    

  可以看到,这里我们创建了结构体变量s,并将其中的元素初始化。这里的初始化方式我们是按照结构体中成员的顺序进行初始化的,如果想要按照自己想要的顺序进行初始化就可以按照下面的方式:

  在创建结构体变量时也可按照下面的方式:

  

  也可以同时创建多个变量:

    1.2 结构体的特殊声明

  前面我们声明结构体的时候属于完全声明,我们也可以不进行完全声明,也可以称之为匿名结构体:

这里在声明结构体的时候省去了标签(tag)。不完全声明和完全声明有什么区别呢?

  不完全声明的结构体在没有重命名的情况下是一次性的,只能使用一次。

  在知道上面这个的情况下,来看看下面的代码是否正确:

  

  由于 struct 是一次性的,在创建完 之后就无法使用,因此,s 和 *p 会被编译器认为是不同的两个类型,因此这段代码是非法的!

    1.3 结构体的自引用

    链表:

  

  这里在结构体内创建了结构体指针实现自引用的操作,从而实现链表。

  注意:如果这里的Node是使用了重命名后产生的,但是在结构体内部提前使用了Node类型来创建变量,则是非法的,如下:

    因此,尽量避免使用匿名的结构体。

  2. 结构体内存对齐

  我们已经学会了结构体的使用,随后我们深入来讨论一个问题:如何结构体的大小。

  是类型就有大小,那么结构体类型的大小也像 int double char 这些一样是的吗?答案是否。

  想要计算结构体的大小,我们首先需要知道一些规则:

    2.1 对齐规则

  1.结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处

  

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  首先要明白什么是对齐数:

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

 

  除了第一个成员以外,剩下的成员都要遵守这条规则,则应该是如下存储模式:

在上面我们已经将 c1ac2 存入了内存,那么结构体的总大小这样就可以知道了吗?答案是否,下面是第三条规则:

3.结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的
整数倍

  

  在上面的存储中我们不难发现,当前结构体的总大小为 9 个字节,但是由于这条规则的存在,内存会继续向下开辟,知道结构体的内存为最大对齐数的整数倍:

 

当开辟出的空间大小是最大对齐数的整数倍时,就是结构体的大小了,这里结构体的大小为 12 ,是最大对齐数的 3 倍。同时不难发现,这里在开辟内存空间的时候是会产生浪费的,这是结构体设计的必然结果(至于为什么要这样设计,我们后面会讲)。既然空间会浪费,我们可不可以尽量使空间浪费得少一点呢?答案是可以的,看下面两段代码:

  

  可以看到,这里我们创建的两个结构体变量中的结构体成员都是相同的,但是为什么大小却不相同呢?第一个结构体变量我们上面已经画图探究过了它的内存,现在我们来画图探究第二个:

可以看到,第二个结构体在开辟内存时,浪费的空间只有两个字节,这是因为当 存进内存后,结构体的大小正好是最大对齐数的整数倍,不用再继续向后开辟内存看空间导致浪费。

  因此,我们在创建结构体变量的时候,可以尽量将小字节的变量放在一起创建,这样可以减少空间的浪费。

    2.2 为什么存在内存对齐

  1. 平台原因

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

  2. 性能原因

  数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。我们画图理解:

  而如果我们按照内存对齐的方式来存储:

  
 

  浪费了一些空间,但计算机能够一次读完 ,使得效率大大提升。

  总的来说:结构体的内存对齐就是用空间来换取时间的做法。

    2.3 修改默认对齐数

  前面说过,VS编译器的默认对齐数是 8 ,但其实VS的默认对齐数是可以修改的,需要用到:#pragma 这个预处理指令:

  设置默认对齐数后结构体 s1 的大小是多少大家可以思考一下,这里就不再讲解了。

                                                   创作不易,点个赞再走呗,谢谢啦~

     

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

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

相关文章

堆排序(数据结构)

本期讲解堆排序的实现 —————————————————————— 1. 堆排序 堆排序即利用堆的思想来进行排序,总共分为两个步骤: 1. 建堆 • 升序:建大堆 • 降序:建小堆 2. 利用堆删除思想来进行排序. 建堆和堆删…

代码随想录|Day21|回溯01|77.组合

77.组合 组合问题不考虑顺序,例如 [1, 2] 和 [2, 1] 是同一个组合。其中 n 为取数的范围,每个组合包含 k个 元素数量,所以我们嵌套 k 个 for循环 可以很容易写出暴力解法。但如果 k 的值过大,代码将会非常冗长。 我们考虑回溯&…

基于”Python+”多技术融合在蒸散发与植被总初级生产力估算中的应用教程

原文链接:基于”Python”多技术融合在蒸散发与植被总初级生产力估算中的应用教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247598050&idx5&sn70fd3f5946d581ad9c1363295b130ef5&chksmfa823e05cdf5b713baf9cf1381bfb2455ad675a0b21e194…

Unity类银河恶魔城学习记录11-2 p104 Inventoty源代码

此章节相对较难理解,有时间单独出一章讲一下 Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili InventoryItem.cs…

C++ Qt开发:QUdpSocket网络通信组件

Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QUdpSocket组件实现基于UDP的网络通信…

Java安全 反序列化(1) URLDNS链原理分析

Java安全 反序列化(1) URLDNS链原理分析 文章目录 Java安全 反序列化(1) URLDNS链原理分析前置知识应用分析payload1.新建HashMap类2.新建URL类3.获取URL 的 Class对象4.通过反射访问URL内部变量5.通过反射为URL中类赋值6.调用HashMap#put方法传入key和value7.再次通过反射为UR…

基于51单片机PT100温度检测LCD1602显示(程序+原理图+PCB+仿真+全套资料)

功能介绍:采用51单片机和MAX13865模块PT100传感器LCD1602显示屏,通过PT100传感器感知稳定,MAX13865模块进行转换,采用SPI协议和单片机进行通信,将温度传送到单片机,然后单片机将数据显示到LCD1602屏幕上。 …

ModbusTCP转Profinet网关高低字节交换切换

背景:在现场设备与设备通迅之间通常涉及到从一种字节序(大端或小端)转换到另一种字节序。大端字节序是指高位字节存储在高地址处,而小端字节序是指低位字节存储在低地址处。在不动原有程序而又不想或不能添加程序下可选用ModbusTC…

深入探讨Python中的文件操作与文件IO操作【第141篇—Python实现】

👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 深入探讨Python中的文件操作与文件IO操作 在Python编程中,文件操作和文件IO操作…

【Swing】Java Swing实现省市区选择编辑器

【Swing】Java Swing实现省市区选择编辑器 1.需求描述2.需求实现3.效果展示 系统:Win10 JDK:1.8.0_351 IDEA:2022.3.3 1.需求描述 在公司的一个 Swing 的项目上需要实现一个选择省市区的编辑器,这还是第一次做这种编辑器&#xf…

第四百一十一回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"给geolocator插件提交问题的结果"相关的内容,本章回中将介绍自定义标题栏.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我…

Java基础-IO流

文章目录 1.文件1.基本介绍2.常用的文件操作1.创建文件的相关构造器和方法代码实例结果 2.获取文件相关信息代码实例结果 3.目录的删除和文件删除代码实例 2.IO流原理及分类IO流原理IO流分类 3.FileInputStream1.类图2.代码实例3.结果 4.FileOutputStream1.类图2.案例代码实例 …

全国农产品价格分析预测可视化系统设计与实现

全国农产品价格分析预测可视化系统设计与实现 【摘要】在当今信息化社会,数据的可视化已成为决策和分析的重要工具。尤其是在农业领域,了解和预测农产品价格趋势对于农民、政府和相关企业都至关重要。为了满足这一需求,设计并实现了全国农产…

Apache SeaTunnel MongoDB CDC 使用指南

随着数据驱动决策的重要性日益凸显,实时数据处理成为企业竞争力的关键。SeaTunnel MongoDB CDC(Change Data Capture) 源连接器的推出,为开发者提供了一个高效、灵活的工具,以实现对 MongoDB 数据库变更的实时捕获和处理。 本文将深入探讨该连…

3月19日做题

[NPUCTF2020]验证🐎 if (first && second && first.length second.length && first!second && md5(firstkeys[0]) md5(secondkeys[0]))用数组绕过first1&second[1] 这里正则规律过滤位(Math.) (?:Math(?:\.\w)?) : 匹配 …

详解命令docker run -d --name container_name -e TZ=Asia/Shanghai your_image

docker run 是Docker的主要命令,用于从镜像启动一个新的容器。下面详细解释并举例说明 -d, --name, -e TZ 参数的用法: -d 或 --detach: 这个标志告诉Docker以守护进程(后台)模式运行容器。这意味着当你执行 docker ru…

②免费AI软件开发工具测评:通义灵码 VS 码上飞

前言 我又双叒叕来测评了!上次给大家带来的是iFlyCode和CodeFlying两款产品的测评,受到了大家的一致好评~ 今天咱就继续来聊聊,这次我们选的的对象是通义灵码和码上飞,从名字上也能看到出来这两款产品一定是跟软件开发有关系的&…

IPD集成产品开发:塑造企业未来竞争力的关键

随着市场竞争的日益激烈,企业对产品开发的要求也越来越高。如何在快速变化的市场环境中,既保证产品的批量生产效率,又满足客户的个性化需求,成为了企业面临的重要挑战。IPD(集成产品开发)模式,作…

Linux 常用操作命令大全

目录 一、命令大集合 1.1 whereis 1.2 which 1.3 sudo 1.4 grep 1.5 free 1.6 top 动态显示进程的状态 1.7 ps 静态显示进程信息 1.8 df 1.9 iostat 看IO性能状态 1.10 yum安装插件命令 1.11 rpm 1.12 scp远程拷贝 1.13 uname 二、linux网络命令 2.1 centos7 防火…

理论学习:with torch.no_grad()

如果不加上“with torch.no_grad():”,模型参数会发生改变吗? 如果不使用with torch.no_grad():,在进行模型推理(即计算outputs_cls net(inputs[batch_size//2:])这一步)时,模型参数不会发生改变&#xf…
最新文章