匿名结构体类型、结构体的自引用、结构体的内存对齐以及结构体传参

请添加图片描述

文章目录

  • 🚀前言
  • 🚀结构体
    • ✈️结构体类型的声明
    • ✈️结构体变量的创建与初始化
    • ✈️结构体类型的特殊声明
    • ✈️结构体的自引用
    • ✈️结构体的内存对齐
      • 🚁修改默认对齐数
    • ✈️结构体传参

🚀前言

在C语言中有着各种数据类型,这些类型有配划分为内置类型自定义类型两大类(如下图)。铁子们,今天阿辉要分享的就是自定义类型中的结构体联合体和枚举将在下篇文章分享,至于数组阿辉之前的文章数组篇中已经详细讲到,铁子们感兴趣的话可以点击跳转😘,不多bb直接开始我们今天的学习👊
请添加图片描述

🚀结构体

铁子们是否有这样的疑问——C语言为什么要引入结构体这一自定义类型?
别急,听阿辉一一道来👇

其实结构体数组有一点类似,数组是存储同一种数据类型的集合,而结构体是存储不同类型的集合,比如当你想描述一个学生时,你得有姓名、年龄、学号等等一系列特征
可是我们发现这不是某一种单一的数据类型能够描述的,这时引入结构体这一自定义类型是非常有必要的

对于结构体有何用想必铁子们有了初步的认识,咱们接着往下看👇

✈️结构体类型的声明

声明结构体的语法结构:

struct tag 
{member_list; 成员列表,
}variable_list;  变量列表,在结构体声明时就创建的变量
struct tag 这个整体属于类型名,和intchar等等类型名一样

我们来创建一个描述学生的结构体类型:

struct stu
{char name[20];名字int age;年龄int id;学号char sex[5];性别
};//注意这里的分号不能丢了

结构体类型的声明同样分为全局声明和局部声明,结构体全局声明以及声明时创建的变量作用域都是整个程序,而结构体的局部声明以及声明时创建的变量的作用域在该大括号内部{}
在这里插入图片描述

✈️结构体变量的创建与初始化

结构体变量有两种创建方式,一种在结构体类型声明时就创建,与结构体类型声明具有相同的作用域;另一种在结构体类型声明后创建,作用域与结构体类型声明无关,咱直接上代码👇

struct stu
{char name[20];int age;
}s1;//s1属于全局变量int main()
{struct stu s2;//s2局部变量,作用域在main函数内return 0;
}

结构体变量初始化:

#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};
int main()
{//按照结构体成员的顺序初始化struct Stu s1 = { "张三", 20, "男", "20230818001" };//按照指定的顺序初始化struct Stu s2 = { .age = 18, .name = "lisi",.id = "20230818002", .sex = "⼥};return 0;
}

✈️结构体类型的特殊声明

铁子们,结构体还有一种特殊的声明,这种声明把结构体的标签tag给干掉了,这种特殊声明的结构体被称为匿名结构体类型
我们来上一组例子:

struct
{int a;int b;
}a;struct
{int a;int b;
}*p;int main()
{p = &a;return 0;
}

上⾯的两个结构在声明的时候省略掉了结构体标签tag
那么问题来了?
p = &a这样写是否合法?

对于匿名结构体类型,上述两个结构体类型看似一样,实则不同,匿名结构体的变量只能在声明时创建且只能有一个变量,上述编译器会把匿名结构体指针变量p与&a当作两个不同的类型

✈️结构体的自引用

结构体的自引用本质是结构体的递归定义,但是这会存在很大问题,如下面这个代码

struct node
{int data;struct node next;
}

在编译期间,编译器需要知道结构体变量大小为结构体变量分配空间,但是上述这个结构体我们仔细想一下会发现这个结构体无限递归,根本无法确定其大小,为解决上述问题,我们可以通过指针来间接引用结构体,如下:

struct node
{int data;struct node* next;
}

上述就是正确的结构体自引用,通过结构体自引用我们可以创建具有互相关联关系的数据结构,如链表、树等,在数据结构中结构体尤为重要。

✈️结构体的内存对齐

有了上面对于结构体的理解,铁子们对结构体的基本使用应该不成问题了,接下来咱们来研究一个深入的问题——结构体类型的大小

有的老铁可能会说:不是很简单吗❓直接把所有变量所占字节空间大小全都加起来就完事了

但是真有这么简单吗?我们接着看👇
其实结构体存在内存对齐这一规则:

  • 第一个成员在与结构体变量偏移量为0的地址处
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
    VS中默认的值为8,Linux中gcc没有默认对齐数对齐数就是该成员大小
  • 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

在VS中char的对齐数就是1int对齐数就是4double对齐数就是8
我们来看看一个是否如此

#include <stdio.h>
struct S1
{char c1;int i;char c2;
};
int main()
{printf("%d\n", sizeof(struct S1));return 0;
}

在这里插入图片描述

我们可以看到对于两个char和一个int应该是6个字节,但是通过sizeof却打印出了12

这里我用用图为铁子们解释:
请添加图片描述
上图一个方块代表一个字节,对于第一个成员c1就存在偏移量为0的地址处,而对于第二个成员i它是int类型对齐数为4,要存在为4的倍数的偏移量处也就是上图位置出,第三个成员c2char类型对齐数为1存在i成员后面,现在整个大小只有9个字节并非最大对齐数4的整数倍所以还要补3个字节分配给它,上图中蓝色方块代表浪费的内存
知道了结构体内存对齐的计算之后,问题又来了:为什么存在内存对齐❓
有两个原因:

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

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

结构体的内存对齐是一种拿空间换时间的做法

在设计结构体的时候,我们既要满足对齐,又要节省空间,这该如何做呢?
在我们声明结构体时尽量让占用空间小的成员集中在一起,如:

#include <stdio.h>
//例如:
struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

在这里插入图片描述
struct s2就要比struct s1的空间小4个字节

🚁修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对齐数
例子:

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

输出为6当默认对齐数为1时也就不存在对齐了,直接把所有变量所占字节空间大小全都加起来就完事了

✈️结构体传参

与其他类型变量传参一样,同样可以传址和传值

#include <stdio.h>
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;
}

上述代码都可以帮我们打印,但是传址调用更好

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

SO结构体在传参时要传递结构体地址


感谢老铁能看到这,到这里结构体的分享就到此为止了,如果觉得阿辉写得不错的话,记得给个赞呗,你们的支持是我创作的最大动力🌹
请添加图片描述

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

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

相关文章

Linux部署HDFS集群

&#xff08;一&#xff09;VMware虚拟机中部署 ps、其中node1、node2、node3替换为自己相应节点的IP地址&#xff0c;或者host文件中配置过的主机名&#xff0c;或者看前置准备 或者查看前置准备&#xff1a;Linux部署HDFS集群前置准备 1.下载压缩包 https://www.apache.or…

ChatGPT 问世一周年之际,开源大模型能否迎头赶上?

就在11月30日&#xff0c;ChatGPT 迎来了它的问世一周年&#xff0c;这个来自 OpenAI 的强大AI在过去一年里取得了巨大的发展&#xff0c;迅速吸引各个领域的用户群体。 我们首先回忆一下 OpenAI和ChatGPT这一年的大事记&#xff08;表格由ChatGPT辅助生成&#xff09;&#x…

【模型报错记录】‘PromptForGeneration‘ object has no attribute ‘can_generate‘

通过这个连接中的方法解决&#xff1a; “PromptForGeneration”对象没有属性“can_generate” 期刊 #277 thunlp/OpenPrompt GitHub的 问题描述&#xff1a;在使用model.generate() 的时候报错&#xff1a;PromptForGeneration object has no attribute can_generate 解决方法…

MySQL安全相关——TDE和数据脱敏功能介绍

MySQL作为一款广泛使用的开源关系型数据库管理系统(RDBMS)&#xff0c;其安全性一直是开发者和企业关注的重点。在MySQL中&#xff0c;有一些与安全相关的功能&#xff0c;其中包括Transparent Data Encryption(TDE)和数据脱敏。本文将对这些功能进行介绍。 一、Transparent Da…

Python一键采集京*东商品数据,保存表格,零基础也能学会

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 开发环境: python 3.8 pycharm 专业版 模块使用&#xff1a; requests >>> 发送请求 第三方库 (需要安装) parsel >>> 第三方库 用来提取网…

RHCSA学习笔记(RHEL8) - Part1.RH124

Chapter Ⅰ 入门 - Linux 开源系统&#xff0c;命令行&#xff0c;模块化&#xff08;软件包的形势&#xff09; - Windows 闭源Linux是类UNIX系统&#xff0c;mac系统也是类UNIX系统&#xff0c;所以二者的图形化界面比较相似 开源许可证&#xff1a;公共版权&#xff1b;宽…

学校图书管理系统设计

基于ASP.NET MVC技术的图书管理系统的设计与实现 摘要&#xff1a;图书管理系统是一套高新科学技术和图书知识信息以及传统历史文化完美结合的体现。它改变了传统图书收藏的静态书本式图书服务特征&#xff0c;实现了多媒体存取、远程网络传输、智能化检索、跨库无缝链接、创造…

《地理信息系统原理》笔记/期末复习资料(8. 数字高程模型)

目录 8. 数字高程模型 8.1 概述 8.1.1 数字高程模型概念 8.1.2 数字高程模型特点 8.2 DEM数据分布特征 8.2.1 格网状数据 8.2.2 离散数据 8.3 DEM的表示方法 8.3.1 数学方法 8.3.2 图形方法 8.3.3 DEM三维表达方法 8.4 TIN的生成方法 8.4.1 人工方法 8.4.2 程序自…

selenium 工具 的基本使用

公司每天要做工作汇报&#xff0c;汇报使用的网页版&#xff0c; 所以又想起 selenium 这个老朋友了。 再次上手&#xff0c;发现很多接口都变了&#xff0c; 怎么说呢&#xff0c; 应该是易用性更强了&#xff0c; 不过还是得重新看看&#xff0c; 我这里是python3。 pip安装…

数字员工「取数宝」上新!4大优势,解决电商取数难题

全域电商&#xff0c;是近几年的新趋势&#xff0c;几乎所有商家都在布局全域&#xff0c;追求全域增长。但商家发现&#xff0c;随着投入成本的上涨&#xff0c;利润却没有增加。 其中最为突出的是——商家为保证全域数据的及时更新&#xff0c;通过堆人头的方式完成每日取数…

带你用uniapp从零开发一个仿小米商场_10.开发一个占剩余窗口的滚动区域

首先是一个头部的tag切换栏,这个很简单,就不多说 源码奉上 <scroll-view scroll-x class"border scroll-row" style"height: 80rpx;"><view class"scroll-row-item" style"height: 80rpx;line-height: 80rpx;" v-for"(…

二分查找边界问题——排序数组找元素第一次出现和最后一次出现

二分查找的边界逼近问题&#xff1a; 下面的代码&#xff0c;第一个函数会向左边界逼近&#xff0c;第二个函数会像右边界逼近&#xff01; 考虑left5,right6这种情况&#xff0c;如果5&#xff0c;6的值都是满足的条件的怎么办&#xff1f; 如果mid(leftright1)/2&#xff0c;…

赤峰学院师资培养管理系统的设计与实现

摘 要 随着我国国民经济建设的蓬勃发展和信息技术的越发成熟&#xff0c;各个行业都在积极使用现代化的管理工具&#xff0c;不断改善企业的服务质量&#xff0c;提高工作效率。对师资培养进行现代化的管理&#xff0c;提高工作效率是师资培养管理系统的一大优点。本文是一篇关…

opencv学习二:加载显示图片

文章目录 加载显示图片&#xff08;一&#xff09;函数1.imread()读取图片&#xff08;1&#xff09;matplotlib和opencv中imread函数的区别 加载显示图片 &#xff08;一&#xff09;函数 1.imread()读取图片 Mat imread(const string& filename, int flags1 );第一个参…

Linux系统的常见命令十二,用户管理、、权限设置和组管理

目录 用户管理权限设置组管理 本文主要介绍Linux系统的用户管理、权限设置和组管理。 用户管理 在Linux系统中&#xff0c;用户管理是一个非常重要的部分&#xff0c;它涉及到用户账号的创建、权限的管理、密码的设置等。下面是一些用户管理的基本操作&#xff1a; 添加用户&…

自己开发组件更新到npm网站上 通过npm install 安装 保姆级别教程

文章目的 在项目开发中&#xff0c;经常通过npm install安装使用各种各样的npn包。本文记录如何自己实现的一个npm包 1. 环境准备 开发环境安装好,没有准备好环境 需要先安装哦 2. 创建Vue项目 初始化Vue项目&#xff1a;vue create xwdm-test 选择手动选择功能 Manually selec…

IP地理定位技术的服务内容详解

IP地理定位技术是一种通过IP地址确定设备或用户地理位置的技术&#xff0c;广泛应用于广告定向、网络安全、位置服务等领域。本文将深入探讨IP地理定位技术的服务内容&#xff0c;解析其在不同场景中提供的多种服务。 1. 准确的地理位置信息提供&#xff1a; IP地理定位技术的…

【面试题】JavaScript高级循环方法

给大家推荐一个实用面试题库 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;web前端面试题库 除了for循环♻️&#xff0c;for-of,for-each循环♻️也是一个不错的选择 先说for-of循环♻️ 认识for-of循环♻️…

如何在手机上打开电脑端本地的网页

目录 一.手机端预览VSCode生成的网页站点二.手机端预览VS2022生成的 WebApi网页站点三.手机端预览VS2022生成的 vueapp前端四.总结 今天遇到了2个小问题&#xff1a;1.想在手机上运行VSCode上写好的网页代码。2.同样在手机上运行VS2022 WebApi生成的网页。查找了一晚上资料&…

哪些AI软件有消除笔?这四款AI软件轻松消除水印

日常生活或工作中&#xff0c;离不开对图片的处理&#xff0c;AI已经对图片视频下手了&#xff0c;处理软件我们不必在用传统的PS来一点点扣了&#xff0c;AI能瞬间消除图片中多余的杂物&#xff0c;大大提高了打工人的工作效率&#xff0c;那么哪些AI软件有消除笔的功能呢&…