【C语言进阶(8)】自定义数据类型1:结构体

文章目录

  • 前言
  • Ⅰ 结构体的声明和定义
    • ⒈结构体声明
    • ⒉结构体定义
    • ⒊特殊的声明
  • Ⅱ 结构体的自引用
  • Ⅲ 结构体初始化
  • Ⅳ 访问结构体成员
  • Ⅴ 结构体内存对齐
    • ⒈结构体内存对齐规则
    • ⒉分析结构体大小
    • ⒊嵌套结构体内存大小
    • ⒋内存对齐存在的原因
  • Ⅵ 修改默认对齐数
  • Ⅶ 结构体传参

前言

  • C 语言本身提供了一些基础的内置数据类型,例如:
char		//字符型数据
short		//短整型数据
int 		//整型数据
long		//长整型数据
long long	//更长的整型数据
float		//单精度浮点型数据
double		//双精度浮点型数据
......
  • 但是很多时候需要描述的对象并没有那么简单,这些对象不是简单的一个 int 或者 char 就能完整描述出来的。
  • 因为当我们描述这些复杂对象的时候,就像自定义函数一样,也会有自定义数据类型

结构的基础知识

  • 结构是一些值的集合,这些值称为成员变量
  • 结构的每个成员可以是不同类型的变量

Ⅰ 结构体的声明和定义

⒈结构体声明

1. 语法格式

struct 结构体名称
{结构体成员 1;结构体成员 2;结构体成员 3;......结构体成员 n;
};

2. 结构体的嵌套

  • 在声明结构体时,结构体成员即可以是任何一种基本的数据类型,也可以是另一个结构体,如果是后者,那么就相当于是结构体的嵌套。

3. 声明结构体的位置

  • 结构体声明即可以在所有函数的外面,也可以单独放在一个函数内部使用。如果是后者,则该结构体就指针在该函数中被定义。

4. 声明结构体实例

  • 一本书的结构体声明应该是这样的:
struct Book
{char title[120];char author[40];float price;unsigned int date;char publisher[40];
};

⒉结构体定义

  • 根据自定义的结构体类型来创建一个结构体变量,称为结构体的定义。
  • 结构体声明只是进行一个框架的描述,定义一个真正的结构体类型变量之前,它并不会在内存中分配空间存储数据。

1. 定义结构体变量的语法

struct 结构体名称 结构体变量名;

2. 定义结构体变量的位置

  1. 在声明时定义结构体变量:在声明结构体同时定义一个结构体变量,此时的结构体变量就是一个全局变量

  2. 在函数内部定义结构体变量:在函数内部根据声明的结构体类型定义一个结构体变量,此时的结构体变量就是一个局部变量

struct Book
{char title[120];char author[40];float price;
}book;					//1. 此时 book 被定义为全局变量int main()				//别把 main 函数不当函数
{struct Book book;	//2. 此时 book 被定义为局部变量......return 0;
}

⒊特殊的声明

  • 在声明结构体的时候,也可以选择进行不完全声明。即省略结构体类型名。

匿名结构体类型

  • (只能在声明时定义结构体变量)(该结构体变量只能用一次)
struct 
{char title[120];char author[40];float price;
}book;//book 这个结构体变量只能被使用一次

Ⅱ 结构体的自引用

  • 结构体自己找到一个与自己同类型的另一个数据,称为结构体的自引用。
  • 在数据结构中的链表中就会使用这种方式。

链表的基础知识

  • 在内存中,并不是所有相同类型的数据都是连续存放在内存中的。
  • 有时想要找到那些不知道藏在那个角落里的数据就要使用链表的形式了。
  • 链表:除了链尾结点外,每个结点都包含着指向下一个同类型结点的地址。

在这里插入图片描述

链表结点的设计

struct Node
{int data;			//数据域:该结点本身应该存储的数据struct Node* next;	//指针域:存储下一个结点的地址
};

Ⅲ 结构体初始化

  • 在定义一个变量或数组的时候可以对其进行初始化。
int a = 520;
int arr = { 5,2,0 };
  • 同样的,在定义结构体变量的时候也可以对其进行初始化。
struct Book
{char title[128];char author[40];float price;
};int main()
{struct Book book = { "《C primer plus》","史蒂芬·普拉达",108};return 0;
}

结构体嵌套初始化

  • 当结构体 A 中包含另一个结构体 B 时,对其 B 要在对 A 初始化的大括号内嵌套一个大括号用以初始化 B。
struct Name
{char title[128];char author[40];
};struct Book
{struct Name name;float price;
};int main()
{struct Book book = { {"《C primer plus》","史蒂芬·普拉达"},108 };return 0;
}

Ⅳ 访问结构体成员

  • 访问结构体的内容有两种方式:
    1. 结构体变量 . 结构体成员:结构体变量使用 " . " 操作符访问结构体成员的内容。
    2. 结构体指针 -> 结构体成员:指向结构体变量的结构体指针使用 " -> " 操作符访问结构体成员的内容。

在这里插入图片描述

Ⅴ 结构体内存对齐

计算结构体的大小

  • 结构体的大小和结构体内的每个结构体成员的类型有关。
  • 但是不同数据类型的结构体成员在结构体内的位置也影响着整个结构体的大小。
  • 结构体的大小并不是把所有结构体成员的大小加起来
struct S1
{char a;	//1 字节int  b;	//4 字节char c;	//1 字节
};struct S2
{char a;	//1 字节char b;	//1 字节int  c;	//4 字节
};

在这里插入图片描述

  • S1 和 S2 的结构体成员都是相同的,只是成员位置的不同就直接导致结构体的大小相差了 4 个字节。
  • 结果不一样的原因是编译器对结构体的成员进行了对齐,说白了,对齐是为了让 CPU 可以更快的读取和处理数据。

⒈结构体内存对齐规则

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

偏移量

  • 开辟空间时,某个字节的地址相对于起始地址所偏移的字节个数称之为偏移量。例如:
    • 第一个字节的的地址相较于起始位置的地址,一个字节都没偏移,所以第一个字节处为偏移量 0。
    • 第二个字节的地址相较于起始位置的地址,偏移了一个字节,第二个字节的空间为偏移量 1 处。

对齐数

  • 编辑器默认(VS 中的对齐数默认为 8)的一个对齐数与该成员大小的较小值。例如:
    • int 变量的对齐数为 4,比默认对齐数 8 小,所以此时的对齐数为 4。
    • char 变量的对齐数为 1,比默认对齐数 8 小,所以此时的对齐数为 1。
  • 其他编译器上,对齐数就是变量的自身大小。

⒉分析结构体大小

在这里插入图片描述

1. 分析 s1 的大小

在这里插入图片描述

2. 分析 s2 的大小

在这里插入图片描述

⒊嵌套结构体内存大小

结构体嵌套时的内存对齐规则

  1. 如果嵌套了结构体的情况,嵌套的结构体对齐到字节的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。
struct S3
{double a;		//8 字节char   b;		//1 字节int    c;		//4 字节
};struct S4
{char      d;	//1 字节struct S3 s3;	//16字节double    e;	//8 字节
};

在这里插入图片描述

  • s3 的大小看完 s1 和 s2 的分析之后后应该能自己分析出来,就不过多赘述了。
  • 主要是分析嵌套了 S3 的 s4 的大小是怎么来的。

分析 s4 的大小

在这里插入图片描述

⒋内存对齐存在的原因

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

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

2 性能原因

  • 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
  • 因为为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次访问。

如何同时满足(内存对齐,节省空间)

  • 设计结构体的时候,将占用空间小的成员放在一起

在这里插入图片描述

Ⅵ 修改默认对齐数

  • 使用 #pragma 这个预处理指令,可以修改默认对齐数。
struct S		//此时默认对齐数为 8
{int		i;	//偏移量 0 ~ 3double	d;	//偏移量 8 ~ 15
};int main()
{printf("%d\n", sizeof(struct S));	//16return 0;
}

1. 修改默认对齐数

#pragma pack(4)	//将默认对齐数改为 4
struct S		
{int		i;	//偏移量 0 ~ 3double	d;	//偏移量 4 ~ 11
};
#pragma pack()	//将默认对齐数回复成 8int main()
{printf("%d\n", sizeof(struct S));	//12return 0;
}

在这里插入图片描述

2. 取消内存对齐

  • 将默认对齐数改为 1,这样不管是什么类型数据的对齐数和默认对齐数比较,都是默认对齐数小,就会直接使用默认对齐数为当前对齐数。
#pragma pack(1)	//只要是 1 的倍数都可以存放
struct S		
{char a;//0int  b;//1 ~ 4char c;//5
};
#pragma pack()	//将默认对齐数回复成 8int main()
{printf("%d\n", sizeof(struct S));//6return 0;
}

Ⅶ 结构体传参

  • 结构体传参分为(传结构体变量)和(传结构体地址)。
  • 结构体传参时,尽量选择传址调用(传结构体地址)
struct Book
{char title[128];char author[40];
};void print1(struct Book book)
{printf("|-----传结构体变量-----|\n");printf("书名:%s\n", book.title);printf("作者:%s\n", book.author);printf("|----------------------|\n");
}void print2(struct Book* p)
{printf("|-----传结构体地址-----|\n");printf("书名:%s\n", p->title);printf("作者:%s\n", p->author);printf("|----------------------|\n");
}int main()
{struct Book book = { "《C primer plus》","史蒂芬·普拉达"};print1(book); //传结构体变量print2(&book);//传结构体地址return 0;
}

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

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

相关文章

围棋与育种

最近因为娃子报名了围棋课,我本着交一次课学两个人的态度,入门围棋,买了些书,听了些课,有所得。学了两个多月,现在6级水平,了解了基本的攻杀技巧,会判断输赢。 下面截图是今天的一盘…

Mr. Cappuccino的第64杯咖啡——Spring循环依赖问题

Spring循环依赖问题 什么是循环依赖问题示例项目结构项目代码运行结果 Async注解导致的问题使用Lazy注解解决Async注解导致的问题开启Aop使用代理对象示例项目结构项目代码运行结果 Spring是如何解决循环依赖问题的原理源码解读 什么情况下Spring无法解决循环依赖问题 什么是循…

MyBatis分页查询与特殊字符处理

目录 目录 一、引言 1.1 简介Mybatis 1.2分页查询的重要性 1.3MyBatis特殊字符处理的挑战 挑战1:SQL注入漏洞 挑战2:查询结果异常 挑战3:数据完整性问题 挑战4:跨平台兼容性 挑战5:用户体验 如何应对挑战 二…

4.14 HTTPS 中 TLS 和 TCP 能同时握手吗?

目录 实现HTTPS中TLS和TCP同时握手的前提: 什么是TCP Fast Open? TLS v1.3 TCP Fast Open TLSv1.3 HTTPS都是基于TCP传输协议实现的,得先建立完可靠得TCP连接才能做TLS握手的事情。 实现HTTPS中TLS和TCP同时握手的前提: 1、…

uniapp国际化npm install vue-i18n报错

npm install vue-i18n //npmyarn add vue-i18n //yarn在vue2环境下,默认安装 npm install vue-i18n 的版本是 vue-i18n9.1.9,所以报错。 npm view vue-i18n versions --json 用以上命令查看版本: vue2建议5.0版本 npm install vue-i1…

JAVA-编程基础-10-集合

Lison <dreamlison163.com>, v1.0.0, 2023.04.23 JAVA-编程基础-10-集合 文章目录 JAVA-编程基础-10-集合List、Set、Map、队列全面解析ListArrayList创建ArrayList 向ArrayList中添加元素 List、Set、Map、队列全面解析 Java 集合框架可以分为两条大的支线&#xff1a;…

uniapp 项目实践总结(一)uniapp 框架知识总结

导语&#xff1a;最近开发了一个基于 uniapp 框架的项目&#xff0c;有一些感触和体会&#xff0c;所以想记录以下一些技术和经验&#xff0c;在这里做一个系列总结&#xff0c;算是对自己做一个交代吧。 目录 简介全局文件全局组件常用 API条件编译插件开发 简介 uniapp 是…

【C++】开源:Box2D动力学库配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Box2D动力学库配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…

Zenity 简介

什么使 Zenity Zenity 是一个开源的命令行工具&#xff0c;它提供了一种简单的方式来创建图形化的用户界面&#xff08;GUI&#xff09;对话框&#xff0c;以与用户进行交互。它基于 GTK 库&#xff0c;可以在 Linux 和其他 UNIX-like 系统上使用。 Zenity 可以通过命令行或脚…

Unity Alembic闪烁问题

最近在做项目时&#xff0c;发现Clo3D导出的服装abc动画&#xff0c;导入到Unity中后(已提前导入Alembic插件)&#xff0c;运行时屏幕会闪烁(变黑)。 经过几轮测试&#xff0c;发现是切线的问题。解决办法很简单。将abc文件上的Tangents属性值改为None即可。

【自适应稀疏度量方法和RQAM】疏度测量、RQAM特征、AWSPT和基于AWSPT的稀疏度测量研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

“之江创客”走进美洲 助推电商国际合作交流

“之江创客” 2023全球电子商务 创业创新大赛 美洲赛区决赛 当地时间8月24日下午&#xff0c;“之江创客”2023全球电子商务创业创新大赛美洲赛区决赛在墨西哥蒙特雷科技大学墨西哥城校区圆满落幕。浙江省商务厅党组成员、副厅长张钱江作视频致辞&#xff0c;蒙特雷科技大学…

849. 到最近的人的最大距离(JavaScript)849. Maximize Distance to Closest Person

给你一个数组 seats 表示一排座位&#xff0c;其中 seats[i] 1 代表有人坐在第 i 个座位上&#xff0c;seats[i] 0 代表座位 i 上是空的&#xff08;下标从 0 开始&#xff09;。 至少有一个空座位&#xff0c;且至少有一人已经坐在座位上。 亚历克斯希望坐在一个能够使他与…

Qt xml解析之QXmlStreamReader

文章目录 背景QXmlStreamReader简单介绍使用QXmlStreamReader添加头文件<QXmlStreamReader>toString()toInt()完整代码 背景 项目中遇到需要解析某个方法返回的xml字符串&#xff0c;奈何C/C中没有原生的方法可供调用&#xff0c;只能使用第三方库&#xff0c;搜了一圈资…

基于闪电连接过程算法优化的BP神经网络(预测应用) - 附代码

基于闪电连接过程算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于闪电连接过程算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.闪电连接过程优化BP神经网络2.1 BP神经网络参数设置2.2 闪电连接过程算法应用 4.测试结…

linux搭建minIO对象存储服务,springBoot整合

minIO 服务搭建 1. 创建安装目录 mkdir -p /usr/local/minio2. 进入安装目录 cd /usr/local/minio3.下载安装包 (wget 如果下载太慢,可以手动下载并上传安装包) wget https://dl.minio.io/server/minio/release/linux-amd64/minio4.创建数据存储文件夹 mkdir -p /usr/loca…

恒流电路的三种设计方案

作为硬件研发工程师相信对恒流电路不会陌生&#xff0c;本文介绍下三种恒流电路的原理图。 三极管恒流电路 三极管恒流电路 三极管的恒流电路&#xff0c;主要是利用Q2三极管的基级导通电压为0.6~0.7V这个特性&#xff1b;当Q2三极管导通&#xff0c;Q1三极管基级电压被拉低而…

大数据课程K12——Spark的MLlib概述

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Spark的MLlib概念; ⚪ 掌握Spark的MLlib基本数据模型; ⚪ 掌握Spark的MLlib统计量基础; 一、Spark MLlib介绍 1. 概述 MLlib是Apache Spark的可迭代机器学习库。 2. 易于使用 …

【优选算法】—— 二分查找

序言&#xff1a; 本期&#xff0c;我们将要介绍的是有关 二分查找算法 并通过题目帮组大家更好的理解&#xff01; 目录 &#xff08;一&#xff09;基本介绍 1、基本思想 2、解题流程 3、复杂度以及注意事项 &#xff08;二&#xff09;题目讲解 1、在排序数组中查找…

Sql Server导出数据库到另一个数据库

1.打开sql server数据库&#xff0c;连接到服务器后&#xff0c;找到需要导出的数据库&#xff0c;右击后选择 任务->导出数据。 2.点击 下一步。 3.身份验证可以使用SQL Server身份验证&#xff0c;就是当时建立连接时的用户名和密码&#xff0c;数据库名称使用默认的&…