【C语言】自定义类型:结构体

一、结构体类型的声明

我们前面学习操作符的时候已经接触过结构体了,下面我们回顾一下结构体的基本内容。

创建结构体的语法如上所示:

struct是创建结构体的关键字,然后tag就是我们结构体的名称,member-list是结构体的成员列表,列表包含了对结构体进行描述的变量,variable-list是创建结构体时需要同时创建的结构体变量。

下面我们通过一个例子来理解:

例如我们现在需要描述一个学生,那么学生的描述就有姓名、年龄、学号、性别等。那么此时单个变量的描述就没办法完整描述了。

那么我们就可以创建一个关于学生的结构体变量:

如下:
 

上面是我们常规的结构体的声明,结构体还有一些特殊的声明

结构体的特殊声明:

在结构体的声明中,我们还可以不完全的声明,可以不写结构体的名字,不过此时这个结构体就只能使用一次,这种声明方式也叫匿名结构体。

而且这种声明的方式只能在创建结构体的时候就创建好结构体的变量,即只能创建为全局变量,不可以在main函数中创建局部变量。

下面我们来尝试创建一个匿名结构体:

如上面所示,结构体变量x就只能使用一次。

结构体的自己引用:
我们在结构体中再包含一个类型为该结构体本身的成员是否可以呢?

例如:定义一个链表节点:

上面的结构体中,data是节点的数据,然后struct  Node next 就是下一个节点了。

那么我们思考一下上述的代码是否正确呢?如果正确的话那么sizeof( struct Node)的结果是多少呢?

其实上面的表达方式是不正确的,因为一个结构体中再包含一个结构体变量,这样结构体变量的大小就会无穷大了。

正确的自引用方式应该是将下一个节点的地址存储起来,因为地址的大小要不就是四字节要不就是八字节,不会导致大小无穷大的情况出现。

如下:

二、结构体变量的创建和初始化 

结构体变量的创建

上面我们学习了结构体的声明,那么我们创建好一个结构体后,该如何创建结构体变量呢?

有两种方法:
1、在创建结构体的时候,在variable-list这个位置创建结构体变量

2、我们在创建变量的时候的语法:变量类型   +  变量名,那么我们创建结构体变量也得知道结构体变量的类型,那么结构体变量的类型就是:struct   +   结构体的名称。

比如我们前面创建了一个关于学生的结构体:
那么其我们创建一个关于学生的结构体变量可以如下:

上面我们就将学生结构体struct  stu 当做结构体变量类型来创建了一个s1变量,那么这种变量的创建方式和在创建结构体的时候创建的变量有啥区别呢?

其实和创建变量有点类似,我们变量的创建有局部变量和全局变量,一个创建在main函数外,那么此时为全局变量,反之则是局部变量。

结构体变量的初始化:

1、按照成员列表的顺畅初始化

当我们直接成员列表的顺序初始化的时候,可以直接使用一个大括号按照顺序进行初始化。

例如我们对下学生结构变量s1初始化:


 

如上我们给结构体变量s1赋值了姓名:zhangsan  年龄:18  性别:nan  学号:"2315304125";

2、按照指定的结构体成员顺序初始化:
我们前面学习操作符的时候,了解过结构体操作符,(.) 就一个点号。

我们可以使用这个操作符对结构体变量进行初始化操作:

如下:

 

三、结构体内存对齐

经过上面的复习,我们对结构体的内容应该有了基本理解,那么我们结构体的变量的大小怎么计算的呢?那么结构体的计算需要先理解下面的知识

1、内存对齐规则: 

结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处,对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量大小的较小值。

VS 中默认对齐数的值为8。

Linux中 gcc 没有默认对⻬数,对齐数就是成员自身的大小
结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍
如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最⼤对齐数(含嵌套结构体中成员的对齐数)的整数倍

是不是一脸懵逼,下面我们直接通过几个练习来理解:
练习1:

首先我们看第一个:结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。

这个规则的意思就是这个结构体的第一个成员存放在结构体的最开始的字节处,不会存储在后面的位置。

偏移量就是这个空间和结构体变量最开始的第一个字节的距离。

画个图解释一下这个规则:
 

上面0开始的位置就是结构体变量开始的位置,且也是偏移量为0的位置,然后格子旁边的数字就是偏移量。

然后我们现在将练习1的第一个成员放进去:

那么第一个结构体变量字符c1已经成功放进了。

那么我们看看第二个成员是如何放进的,第二个成员是一个整型变量,此时不是开始的位置的,此时需要看规则二了,首先就是对齐数,首先我们要知道对齐数啥干嘛用的,就是我们在存储的时候,存储的这个成员的时候,它的位置必须在对齐数整数倍的位置。

我们VS中默认的对齐数为8,然后整型数据的对齐数是4 ,然后对齐数是取其中小的那个,所以此时的对齐数为4。那么此时我们的第二个成员要如何存呢?

 那么我们找4的倍数的位置开始存,那么第二个成员就应该从偏移量4的位置开始存:

 如上所示,第二个成员就从其对齐数的倍数的位置存。

那么同理,对于第三个成员,其是字符型,那么其对求数是1,那么其就在i1的后面存储即可,那么我们计算这个结构体的大小就为9个字节了。

那么我们求其大小看看:
 

可以看到其结果并不是9,而是12,那么此时就是我们第三个规则了:

结构体的总大小为最大对齐数的倍数,那么我们这个结构体的最大对齐数为4,然后我们现在已经使用了9个字节,那么最小的4的倍数就是12了。所以结构体的大小就是12了。

此时有同学就会觉得这样的存储方式,有点浪费空间,因为你看我们上面三个成员的实际大小才6个字节,但是我们的结构体却是用了12个字节的空间足足浪费了一半的空间,不用怕,后续我们也有相应的方法来解决这个问题的。

下面我们看练习2:
 

这里和练习1是一样的,只需要用到前三个规则。

首先成员1,和上面的练习1是一样的,放在开始的位置,然后我们看成员2,其对齐数是1,那么其存放的位置就可以挨着第一个成员存放,然后我们看第三个成员,其是一个整型,其对齐数为4,那么其的偏移量应该为4的倍数,那么其应该在第五个位置开始存放。

如下图所示:
 

那么此时刚刚好使用了8个字节的空间,然后最大对齐数为4,也刚刚好是4的倍,那么此时的大小为8。

运行结果:

 

可以看到虽然两个结构体的成员的数量和类型是一样的,但是其放置的顺序的不同也就导致了结构体的大小不同,那么为了节省内存,我们在创建结构体的时候可以将字节小的成员放在前面,将大的放在后面,这样可以提高内存的使用效率。

练习3:

 

先看第一个成员,其为double类型,其大小为8,那么其对齐数为8,不过因为它是第一个成员,所以它的偏移量为0,然后占8个字节。

然后第二个成员是字符型,然后其对齐数为1,那么其可以在d的后面接着存放,那么其偏移量为8。

然后就是第三个成员,其为整型,那么其对齐数为4,然后往后找4的倍数,就是偏移量为12的位置开始存放了,此时就使用了16个字节的内存。然后我们看16是不是8的倍数,很巧就是8的倍数,那么这个结构体的大小就是16字节了。

运行结果:
练习4:
 

这里和上面的不同的就是其这个结构体S4中嵌套了结构体S3

我们先按照上面的分析来分析:
第一个成员是字符型:从偏移量为0的位置开始存放。

第二个成员是结构体类型,那么这种情况该咋样存放呢?
那么此时就需要了解第四条规则了:
如果是嵌套了结构体的情况,嵌套的那个结构体成员的对齐数应该为其成员中最大对齐数。

那么S3的对齐数就应该为8,但是要注意的是S3的大小是16字节,然后其在偏移量为8的位置开始存放。

最后一个成员为double类型,那么其对齐数为8,然后刚刚好第二个成员的下一个位置是24,刚刚好为8的位置,所以可以接着成员二存下去。

那么此时使用了32个字节的空间,然后三个成员最大的对齐数是8,然后32是8的倍数,那么这个结构体的大小就是32字节。

运行结果:

 

为什么存在内存对齐: 

1、平台原因(移植原因)

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

2、性能原因

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

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:

前面我们做的练习1和练习2中:

他们的成员是一样的,但是两个结构体的大小却是不一样的,他们的区别就是成员放置的顺序不同。那么技巧就是先将小的成员放在前面,然后大的放在后面。

修改默认对齐数:

#pragme这个预处理命令,可以改变编译器的默认对齐数,然后#pragme pack ()可以取消#pragme的设置,将默认对齐数还原。

下面我们使用这个预处理没命令看看其效果:

运行结果:

 

四、结构体传参 

下面我们看一个例子:

上面我们分别采用了值传递和地址传递的方式传参。那么对于结构体那种方式会更好呢?

我们开始分析:

首先是传值调用,那么此时函数就会创建一个和传入的结构体一模一样的结构体形参,此时会占用空间,而且要是这个结构体很大的话,那么占用的空间就更大了,容易浪费内存。

还有就是值传递只是一个临时拷贝,无法实现对实参的修改。

而我们的地址传递是传递的地址,其最大也就8个字节的大小,而且通过地址传递可以改变实参的大小。

那么上面的例子很明显print2会更好一点。

原因:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下 降。 

 

 


 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python基本运用:类的介绍和使用

一、介绍类 类(class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例 实例化:创建一个类的实例,类的具体对象。 对象:通过类定义的数据结构实例。对象包括两个数据成员&#x…

Elasticsearch:使用 ColPali 进行复杂文档搜索 - 第 1 部分 - 8.18

作者:来自 Elastic Peter Straer 及 Benjamin Trent 这篇文章介绍了 ColPali 模型,这是一种 late-interaction 模型,可简化包含图片和表格的复杂文档搜索过程,并讨论了其在 Elasticsearch 中的实现。 在构建搜索应用时&#xff0c…

2025-03-19 学习记录--C/C++-C 库函数 - qsort() 实现快速排序

C 库函数 - qsort() 实现快速排序 ⭐️ C 标准库 - <stdlib.h> &#xff08;一&#xff09;、命名介绍 &#x1f36d; qsort 是 C 标准库&#xff08;stdlib.h&#xff09;中提供的一个快速排序函数&#xff0c;用于对数组进行排序。❀它的名字来源于 “Quick Sort”&…

04 泛型编程

1、概论 编程范式&#xff1a;面向过程编程、面向对象编程、泛型编程。 泛型编程&#xff1a;目的是编写能够适合多种数据类型的代码&#xff0c;而不是为每种特定的数据类型编写重复的代码。 模板是实现泛型的主要工具&#xff0c;主要分为函数模板和类模板。 函数模板&am…

【MySQL】架构

MySQL架构 和其它数据库相比&#xff0c;MySQL有点与众不同&#xff0c;它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上&#xff0c;插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实…

(保姆级教程)CAN总线—如何使用CANoe(VN1640)的Scaner功能测量样件的波特率

1、如何找到测试入口 &#xff08;步骤1&#xff09;前置条件 连接好被测样件和VN1640&#xff0c;连接电源。 &#xff08;2&#xff09;打开CANoe工程&#xff0c;依次点击Hardware--》NetworkHardware&#xff0c;如下图&#xff1a; &#xff08;3&#xff09;单击Netwo…

使用 PIC 微控制器和 Adafruit IO 的基于 IoT 的 Web 控制家庭自动化

使用 PIC 微控制器和 Adafruit IO 的基于 IoT 的 Web 控制家庭自动化 家庭自动化一直是我们大多数人的灵感来源。从我们舒适的椅子或任何房间的床上切换交流负载,而无需伸手去触碰另一个房间的开关,听起来很酷,不是吗!.现在,在物联网时代,多亏了 ESP8266 模块,它使从世界…

MySQL原理:逻辑架构

目的&#xff1a;了解 SQL执行流程 以及 MySQL 内部架构&#xff0c;每个零件具体负责做什么 理解整体架构分别有什么模块每个模块具体做什么 目录 1 服务器处理客户端请求 1.1 MySQL 服务器端逻辑架构说明 2 Connectors 3 第一层&#xff1a;连接层 3.1 数据库连接池(Conn…

Excel Script Lab学习笔记

注意 The Excel JavaScript API 没有“Cell”对象或类。 相反&#xff0c;Excel JavaScript API 将所有 Excel 单元格定义为 Range 对象。 Excel UI 中的单个单元格转换为 Excel JavaScript API 中包含一个单元格的 Range 对象。 单个 Range 对象也可以包含多个连续的单元格。…

【第14节】windows sdk编程:进程与线程介绍

目录 一、进程与线程概述 1.1 进程查看 1.2 何为进程 1.3 进程的创建 1.4 进程创建实例 1.5 线程查看 1.6 何为线程 1.7 线程的创建 1.8 线程函数 1.9 线程实例 二、内核对象 2.1 何为内核对象 2.2 内核对象的公共特点 2.3 内核对象句柄 2.4 内核对象的跨进程访…

数据结构中的引用管理对象体系

数据结构中的引用管理对象体系 &#xff08;注&#xff1a;似复刻变量即实例对象&#xff09; 引用管理对象的&#xff0c;有引用就能管理到它所指向的对象&#xff0c;我们拿引用最终的目的就是管理那些我们需要管理的最终直接对象&#xff0c;引用也是对象&#xff0c;同时…

Java 异常处理

一、引言 在 Java 编程中,异常处理是一个至关重要的部分。程序在运行过程中可能会遇到各种意外情况,如文件不存在、网络连接中断、数组越界等。如果不进行适当的处理,这些异常可能会导致程序崩溃,影响用户体验。Java 提供了一套完善的异常处理机制,允许开发者捕获和处理这…

数据驱动进化:AI Agent如何重构手机交互范式?

如果说AIGC拉开了内容生成的序幕&#xff0c;那么AI Agent则标志着AI从“工具”向“助手”的跨越式进化。它不再是简单的问答机器&#xff0c;而是一个能够感知环境、规划任务并自主执行的智能体&#xff0c;更像是虚拟世界中的“全能员工”。 正如行业所热议的&#xff1a;“大…

skywalking微服务链路追踪

是什么&#xff1f; skywalking是一个优秀的国产开源框架&#xff0c;2015年由个人吴晟&#xff08;华为开发者&#xff09;开源 &#xff0c; 分布式链路追踪就是将一次分布式请求还原成调用链路&#xff0c;将一次分布式请求的调用情况集中展示&#xff0c;比如各个服务节点…

DR-CAN 卡尔曼滤波笔记

Kalman Filter&#xff08;卡尔曼滤波&#xff09; Optimal(最优化) Recursive(递归) Data Processing(数据处理) Algorithm(算法) 1 递归算法_Recursive Alorithm 1.1 公式推演 1.2 案例 1.3编程实现 % 设置迭代次数 n 5000000;% 生成测量值序列 % rand(n 1, 1) 生成一个…

HyperAD:学习弱监督音视频暴力检测在双曲空间中的方法

文章目录 速览摘要1. 引言2. 相关工作弱监督暴力检测双曲空间中的神经网络 3. 预备知识双曲几何切空间&#xff08;Tangent Space&#xff09;指数映射与对数映射&#xff08;Exponential and Logarithmic Maps&#xff09;3.1 双曲图卷积网络&#xff08;Hyperbolic Graph Con…

Freeze-Omni:冻结 LLM,实现语音对话

写在前面:语音LLM 大型语言模型(LLM)的强大能力,为构建智能语音对话系统提供了无限可能。然而,将 LLM 与语音模态结合,并非易事。直接微调 LLM,容易导致灾难性遗忘,丧失其原有的知识和能力;而训练数据不足,又难以充分发挥 LLM 的潜力。 如何才能在保留 LLM 强大能力…

践行健康养生,拥抱美好人生

在当今快节奏的社会浪潮中&#xff0c;人们在忙碌奔波时&#xff0c;健康常被抛诸脑后。可一旦身体亮起红灯&#xff0c;才惊觉健康无价。其实&#xff0c;只要巧妙运用养生之道&#xff0c;就能轻松守护健康&#xff0c;让生活重回正轨。 养生始于饮食。我们要巧妙搭配食物&am…

上海亚商投顾:沪指窄幅震荡 深海科技概念持续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天窄幅震荡&#xff0c;三大指数涨跌互现。深海科技概念持续活跃&#xff0c;巨力索具、东方海洋、海洋…

Java 大视界 -- Java 大数据在智能体育赛事直播数据分析与观众互动优化中的应用(142)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…