C语言进阶——自定义类型:结构体
🌇个人主页:_麦麦_
📚今日名言:生活不可能像你想象的那么好,也不会像你想象的那么糟。——莫泊桑《羊脂球》
目录
一、前言
二、正文
1结构体
1.1结构体的基础知识
1.2结构的声明
1.3特殊的声明
1.4结构体变量的定义和初始化
1.5结构的自引用
1.6结构体内存对齐
1.7修改默认对齐数
1.8结构体传参
2.位段
2.1什么是位段
2.2位段的内存分配
2.3位段的跨平台问题
2.4位段的应用
三、结语
一、前言
好久不见,今天为小伙伴们带来C语言中有关结构体的详细知识,干货满满,图文并茂一定要看到底哦!
二、正文
1结构体
1.1结构体的基础知识
结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量
1.2结构的声明
struct tag
{
member—list;
} ;
注:分号不能丢
//结构的声明演示:描述一个学生
struct Stu
{char name[20]; //名字char sex[5]; //性别int age; //年龄char id[20]; //学号
};
1.3特殊的声明
在声明结构的时候,可以不完全声明【匿名结构体类型】,由于匿名,所以声明后就得在后面直接创建变量。
//匿名结构体类型
struct
{int a;char b;float c;
}x; //创建变量x
注:哪怕两个匿名结构体中的内容完全一样也会被编译器当成两个完全不同的类型
//匿名结构体类型1
struct
{int a;char b;float c;
}x; //匿名结构体类型2
struct
{int a;char b;float c;
}*p;//非法操作
p = &x;
1.4结构体变量的定义和初始化
结构体变量的定义共分为三类:
①全局变量定义
②局部变量定义
③在结构的声明的同时定义变量
//结构体变量的三种定义方法
struct Stu
{char name[20]; //名字char sex[5]; //性别int age; //年龄char id[20]; //学号
}Stu1; //声明结构的同时定义变量struct Stu Stu2; //全局变量定义int main()
{struct Stu Stu3; //局部变量定义return 0;
}
结构体变量的初始化即定义变量的同时赋初值。不过结构体的初始化也分为正常的初始化和嵌套初始化。
//结构体变量的初始化
struct Peo
{char name[20]; //名字char sex[5]; //性别int age; //年龄
};struct Peo Peo1 = { "陈书婷","女",35}; //结构体正常初始化struct Node
{char movie[20];struct Peo p;
}Peo2 = { "狂飙",{"高志强","男",40 }}; //结构体嵌套初始化struct Node Peo3 = { "狂飙",{"高启盛","男","28"}}; //结构体嵌套初始化
注:结构体的初始化其实可以更加灵活——乱序,按照自己想法来初始化。
struct Peo
{char name[20]; //名字char sex[5]; //性别int age; //年龄
};struct Peo Peo4 = { .sex = "男",.name = "安欣",.age = 30 };
1.5结构的自引用
采取指针的形式
在结构中包含一个类型为该结构本身的成员
//自引用1(错误示范)
struct Node
{int date;struct Node next;
};//自引用2(正确示范)
struct Node
{int date;struct Node* next;
};
1.6结构体内存对齐
在小伙伴们掌握了结构体的基本使用之后,接下来让我们深入讨论一个问题:如何计算结构体的大小?为了解决这个问题,我们就必须掌握结构体的对齐规则。
● 第一个成员在与结构体变量偏移量为0的地址处
●其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的一个对齐数与该成员大小的较小值
★VS中默认的值为8
●结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
●如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
#include<stdio.h>
//结构体内存对齐代码1
struct s1
{double d; //对齐数8char c; //对齐数1int i; //对齐数4
};//结构体内存对齐代码2
struct s2
{char c1; //对齐数1struct s1 s1; //对齐数8double d; //对齐数8
};int main()
{printf("%d", sizeof(struct s1)); //16printf("%d", sizeof(struct s2)); //32return 0;
}
结构体内存对齐代码1:首先我们将"d"成员放入内存中,由于它是第一个成员,且对齐数为8,所以从偏移量为0的地址一直放到偏移量为7的地址。其次是"c"成员,它的对齐数为1,因此可以放在偏移量为8的地址处,最后是"i"成员,它的对齐数是4,但是偏移量为9的地址并不是对齐数4的倍数,所以我们只好跳到偏移量为12的地址处,直至偏移量为15的地址处才放下"i"成员。在将所有的成员放入后,就是计算结构体s1的大小,由于15并不是最大对齐数8的倍数,所以结构体s1的大小为16个字节。
结构体内存对齐代码2:首先我们将"c1"成员放入内存中,由于它是第一个成员,且对齐数为1,所以放入偏移量为0的地址处。其次是"s1"成员,该嵌套结构体内的最大对齐数为8,因此跳到偏移量为8的地址处开始存放double类型成员,直至偏移量为15的地址处才存放完毕。接着是char类型成员,对齐数为1,存放在偏移量为16的地址处。继而是整型成员,对齐数为4,从偏移量为20的地址存放,直至偏移量为23的地址处存放完毕。最后是"d"成员,对齐数为8,从偏移量为24的地址存放,直至偏移量为31的地址处存放结束。在将所有的成员放入后,就是计算结构体s2的大小,由于31并不是最大对齐数8的倍数,所以结构体s2的大小为32个字节
最后总结一下计算结构体大小的步骤:
①计算出所有成员的对齐数并得出结构体的最大对齐数
②根据每个成员的对齐数依次存放每个成员
③所有成员存放完毕后,依据结构体的最大对齐数得出结构体大小
那么如何证明我们对结构体成员的偏移量计算是否正确呢?C语言中提供了一个宏来计算结构体成员的偏移量?(无需掌握,只是证明我们上述计算的思路无误)
#include<stddef.h>
int main()
{printf("%d\n", offsetof(struct s1,d));printf("%d\n", offsetof(struct s1,c));printf("%d\n", offsetof(struct s1,i));printf("%d\n", offsetof(struct s2, c1));printf("%d\n", offsetof(struct s2, s1.d));printf("%d\n", offsetof(struct s2, s1.c));printf("%d\n", offsetof(struct s2, s1.i));printf("%d\n", offsetof(struct s2, d));return 0;
}
在了解完结构体的内存,可能有的小伙伴会发出如下的疑问:为什么会存在内存对齐呢,这到底有什么用呢?
大部分的参考资料都是如是说的:
1.平台原因(移植原因):
不是所有的硬件平台都能任意访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些 特定类型的数据,否则就会抛出硬件异常
2.性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总的来说:结构体的内存对齐是拿空间来换取时间的做法
那么在设计结构体的时候,我们既要满足对齐,又要节省空间,该如何做呢?
让占用空间小的成员尽量集中在一起
1.7修改默认对齐数
那么默认对齐数可以修改吗?答案是肯定的。在C语言中存在#pragma这个预处理指令,通过这个我们就可以改变默认对齐数了。
//修改默认对齐数
#include <stdio.h>#pragma pack(2)
struct s1
{char c1;int i;char c2;
};
注:如果将默认对齐数设置为1,则不存在对齐效果。在平常的使用中,小伙伴们一定要根据实际需求修改默认对齐数
1.8结构体传参
在之前的指针学习中我们了解到了"传值调用"和"传址调用"这两个概念,那么在结构体传参时依旧存在以上两种方式,那么那种方式是最优选择呢?
//结构体传参
#include <stdio.h>struct s
{int date[1000];int num;
};struct s s1 = { {1,2,3,4},666 };void print1(struct s s1)
{printf("%d",s1.num);
}void print2(struct s* s1)
{printf("%d", s1->num);
}
int main()
{print1(s1); //传结构体print2(&s1); //传地址
}
其实,在结构体传参这一步中传结构体的地址是一个更好的选择。因为函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
2.位段
在结构体讲完之后就要向小伙伴们介绍结构体实现位段的能力
2.1什么是位段
位段的声明和结构是类似的,却能更加的节省空间,但是有两个地方存在差异:
①位段的成员必须是int、unsigned int 或 signed int
②位段的成员名后边有一个冒号和数字【表示占几个二进制位】
#include <stdio.h>
struct A
{int _a : 2; //a只占2个二进制位int _b : 5; //b只占5个二进制位int _c : 10; //c只占10个二进制位int _d : 30; //d只占30个进制位
};int main()
{printf("%d\n", sizeof(struct A)); //打印为8个字节,是不是更节省空间了呢return 0;
}
2.2位段的内存分配
1.位段的成员可以是int 、unsigned int、signed int、或者是char(属于整形家族)类型
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
3.位段设计很多不确定因素,位段是不跨平台的,注意可移植的程序应该避免位段
4.位段是不存在内存对齐的
注:在不同的编译器中,同一位段的大小也是不确定的,接下来我们以VS2019的环境下解释上述位段的大小为何为8个字节。
在理解完位段在VS下的内存开辟,那么内存是如何使用的呢?在VS的环境下,内存开辟后是从右向左使用的且为小端存储,依旧以上面的代码为例:
2.3位段的跨平台问题
①int位段被当成有符号数还是无符号数是不确定的
②位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出现问题)
③位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
④当一个结构包含两个位段,第二个位段成员比较大,无法容纳与第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
总结:跟结构相比,位段可以达到同样的效果,但是可以很好地节省空间,但是有跨平台的问题存在
2.4位段的应用
大家都知道彼此之间相互的交流看似简单,其实文字交流的背后是大量的网络数据,而位段的使用恰好可以对数据进行压缩,减少网络的压力和负担。形象的来说,我们可以把网络想象成高速公路,如果上面全是未经压缩的数据,也就都是大卡车的话,就会十分拥挤。而如果采取位段的方式,对没有必要使用的空间进行压缩,就可以将大卡车变成小轿车,从而缓解交通压力。
三、结语
关于结构体的讲解就已经全部结束了,下期我们会继续分享自定义类型的其他成员!
关注我 _麦麦_分享更多干货:_麦麦_的博客_CSDN博客-领域博主
大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!
相关文章:
C语言进阶——自定义类型:结构体
🌇个人主页:_麦麦_ 📚今日名言:生活不可能像你想象的那么好,也不会像你想象的那么糟。——莫泊桑《羊脂球》 目录 一、前言 二、正文 1结构体 1.1结构体的基础知识 1.2结构的声明 1.3特殊的声明 1.4结构体变量的…...
SpringSecurity学习笔记01
目录 一、课程介绍 二、框架概述 三、入门案例 四、基本原理(过滤器链) 五、基本原理(过滤器加载过程) 六、基本原理(两个重要的接口) 七、web权限方案-用户认证(设置用户名密码上) 八、…...
Python语言零基础入门教程(十一)
Python 列表(List) 序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。 Python有6个序列的内置类型,但最常见的是列表和元组。 序列都可以…...
现货白银基础知识
任何活动,任何项目,任何工作都离不开基础知识,这是肯定的。万丈高楼平地起,要想要简称百层高楼,首先得把低级打好!现货白银投资也是一样的道理,现在我们就来一起聊聊现货白银基础知识的问题&…...
数据库原理及应用基础知识点
数据库原理基础知识点大全数据库原理及应用1、数据库系统概述1.1 基本概念1.2 数据模型1.3 数据库系统的结构2、实体 -- 联系模型2.1 基本概念2.2 实体-联系图2.3 弱实体集3、关系数据模型3.1 关系数据库的结构3.2 从ER模型到关系模型3.3 关系操作、完整性约束、关系代数4、关系…...
【数据结构】栈(stack)
写在前面本篇文章开始讲解栈的有关知识,其实把顺序表和链表学好,那么这一章便不在话下,栈实际上就是顺序表或链表的一些特殊情况。用顺序表实现的栈叫做顺序栈用链表实现的栈叫做链栈文章的内容分为几个部分,希望读者能快速了解文…...
初识shell
文章目录一、shell基本知识1.1为什么学习和使用Shell编程1.2 什么是Shell1.2.1 shell的起源1.2.2 shell的功能1.3 shell的分类1.4 作为程序设计的语言——shell1.5 如何学好shell1.6 shell脚本的基本元素1.7 shell脚本编写规范1.8shell脚本的执行方式1.9 执行脚本的方法1.10 sh…...
程序员如何编写好开发技术文档 如何编写优质的API文档工作
编写技术文档,是令众多开发者望而生畏的任务之一。它本身是一件费时费力才能做好的工作。可是大多数时候,人们却总是想抄抄捷径,这样做的结果往往非常令人遗憾的,因为优质的技术文档是决定你的项目是否引人关注的重要因素。无论开…...
二级C语言操作例题(四十)
一、程序填空题 在此程序中,函数fun的功能是:在形参s所指字符串中寻找与参数c相同的字符,并在其后插入一个与之相同的字符,若找不到相同的字符则不做任何处理。 例如,若s所指字符串”baacda”,中c的字符为…...
vue-router 源码解析(二)-创建路由匹配对象
文章目录基本使用导语createRouterMatcher 创建匹配路由记录addRoute 递归添加matchercreateRouteRecordMatcher 创建matchertokenizePath 解析pathtokensToParser 记录打分insertMatcher 将matcher排序总结基本使用 const routes [{path:"/",component: Demo2,nam…...
分布式新闻项目实战 - 10.Long类型精度丢失问题
怒发冲冠,凭阑处、潇潇雨歇。抬望眼,仰天长啸,壮怀激烈。三十功名尘与土,八千里路云和月。莫等闲、白了少年头,空悲切。 靖康耻,犹未雪。臣子恨,何时灭。驾长车,踏破贺兰山缺。壮志饥…...
如何将本地jar包安装到maven仓库
mvn install:install-file:主要是将本地自定义jar安装到maven仓库,然后在pom中可以直接通过dependency的方式来引用。 此命令有如参数: 命令说明-DgroupId自定义groupId设置groupId 名-DartifactId自定义artifactId设置该包artifactId名-Dversion自定义…...
C++:map和set的认识和简单使用/关联式容器
关联式容器 关联式容器即是用来存储数据的,并且存储的是<Key,Value>结构的键值对,在数据检索时效率比序列式容器高。 序列式容器也就是vector、list、queue等容器,因为其底层为线性序列的数据结构,里面存储的是…...
网络工程师一定要学会的知识点:OSPF,今天给大家详细介绍
1. OSPF 概念OSPF(Open Shortest Path First 开放式最短路径优先)是一种动态路由协议,属于内部网关协议(Interior Gateway Protocol,简称 IGP),是基于链路状态算法的路由协议。2. OSPF 的运行原理(1)OSPF 的…...
企业管理的三大基石及其关系
企业管理的三大基石三大基石是什么三大基石的关系制度:管理:文化:三大基石是什么 一个企业,不管它是属于哪种类型,影响员工行为的都有三种力量——制度、管理和文化,这是管理的三大基石。 三大基石的关系 …...
6个月软件测试培训出来后的感悟 —— 写给正在迷茫是否要转行或去学软件测试的学弟们
本人刚从某培训机构学习结束,现在已经上班一个月了。这篇文章我不会说太多的知识点,或噱人去培训机构学习的话语,仅作为一个普通打工者的身份,来写给那些对于软件测试未来发展、薪资待遇等不清楚的正在为家庭,解决信用…...
IoU Loss综述(IOU,GIOU,CIOU,EIOU,SIOU,WIOU)
边界框回归(BBR)的损失函数对于目标检测至关重要。它的良好定义将为模型带来显著的性能改进。大多数现有的工作假设训练数据中的样本是高质量的,并侧重于增强BBR损失的拟合能力。 一、L2-norm 最初的基于回归的BBR损失定义为L2-norm…...
Node=>Express中间件 学习3
1.概念: 例:在处理污水的时候,一般都要经过三个处理环节,从而保证处理过后的废水,达到排放标准 处理污水的这三个中间处理环节,就可以叫中间件 2.中间件调用流程 当一个请求到达Express的服务器之后&#x…...
【STM32笔记】HAL库UART串口配置及重定向(解决接收中断与scanf不能同时工作的问题)
【STM32笔记】HAL库UART串口配置及重定向(解决接收中断与scanf不能同时工作的问题) 首先 要使用printf和scanf 必不可少的就是 #include <stdio.h>这里需要做的就是配置单片机的UART 并且使其能够被printf和scanf调用 打开异步工作模式 并且选择…...
【前端CSS面试题】2023前端最新版css模块,高频15问
🥳博 主:初映CY的前说(前端领域) 🌞个人信条:想要变成得到,中间还有做到! 🤘本文核心:博主收集的CSS面试题 目录 一、CSS必备面试题 1.CSS3新特性 2.CSS实现元素两个盒子垂…...
Linux命令大全,赶紧收藏!
新的一年 新的征程 新的课程开班 等你来学! 本文为Linux命令大全,从A到Z都有总结,建议大家收藏以便查用,或者查漏补缺! A 命令 描述 access 用于检查调用程序是否可以访问指定的文件,用于检查文件…...
大数据入门怎么学习
大数据学习不能停留在理论的层面上,大数据方向切入应是全方位的,基础语言的学习只是很小的一个方面,编程落实到最后到编程思想。学习前一定要对大数据有一个整体的认识。 大数据是数据量多吗?其实并不是,通过Hadoop其…...
用于异常检测的深度神经网络模型融合
用于异常检测的深度神经网络模型融合 在当今的数字时代,网络安全至关重要,因为全球数十亿台计算机通过网络连接。近年来,网络攻击的数量大幅增加。因此,网络威胁检测旨在通过观察一段时间内的流量数据来检测这些攻击,…...
游戏服务器如何选择合适的服务器配置
游戏服务器如何选择合适的服务器配置 大家好,今天给大家分享一下游戏服务器配置的选择,为什么特别的说明一下服务器呢?服务器是决定服稳定性和安全性最重要的一个程序,如果是服务器配置不够,可能会导致服掉线、卡顿的…...
01-幂等性解释,问题及常用解决方案
目录 1. 幂等性简介 2. 后端如何解决幂等性问题 2.1 数据库层面 -> 2.1.1 防重表 -> 2.1.2 数据库悲观锁(不建议,容易出现死锁情况) -> 2.1.3 数据库乐观锁 -> 2.1.4 乐观锁CAS算法原理 2.2 锁层面 2.3 幂等性token层面 -> 2.3.1 简介文字描述: …...
SpringBoot配置文件
配置文件有两种格式: .properties .yml .properties是老版配置文件,.yml是新版配置文件 一、properties详解 IDEA社区版不支持 properties格式的日志的提示,需要安装相应插件。 3.1properties 基本语法 (ps:小技巧࿰…...
基于蜣螂算法改进的DELM分类-附代码
蜣螂算法改进的深度极限学习机DELM的分类 文章目录蜣螂算法改进的深度极限学习机DELM的分类1.ELM原理2.深度极限学习机(DELM)原理3.蜣螂算法4.蜣螂算法改进DELM5.实验结果6.参考文献7.Matlab代码1.ELM原理 ELM基础原理请参考:https://blog.c…...
FPGA纯verilog代码实现图像对数变换,提供工程源码和技术支持
目录1、图像对数变换理论2、log系数的matlab生成3、FPGA实现图像对数变换4、vivado与matlab联合仿真5、vivado工程介绍6、上板调试验证并演示7、福利:工程代码的获取1、图像对数变换理论 对数变换可以将图像的低灰度值部分扩展,显示出低灰度部分更多的细…...
【Python百日进阶-Web开发-Vue3】Day516 - Vue+ts后台项目3:首页
文章目录 一、首页头部1.1 element-plus中找到适合的Container布局容器1.2 头部容器Layout 布局1.3 src/views/HomeView.vue二、侧边菜单栏2.1 element-plus中找到适合的Menu侧栏2.2 src/views/HomeView.vue三、侧边栏的动态路由3.1 src/views/HomeView.vue3.2 src/views/Goods…...
分析了 200 个 DeFi 项目,我发现了这些规律
作者:Ren & Heinrich翻译:dongdong在这篇文章中,我分享了我通过分析当前排名前 200 的 DeFi 加密项目的见解。这不是一项学术研究。尽管如此,这些发现对加密货币投资者来说具有附加值。我使用 https://defillama.com/ 的公共数…...
最专业的网站建设价格/看广告收益最高的软件
矩阵分析之 实矩阵分解(1)特征分解与奇异值分解前言特征分解(又称谱分解、对角化)奇异值分解SVDSVD的进一步理解前言 本篇开始学习记录矩阵分解内容。需要说明的是,目前我所触及的矩阵基本上没有出现复数域࿰…...
wordpress免费插件下载/郑州网站关键词排名技术代理
BDLocation类,封装了定位SDK的定位结果,在BDLocationListener的onReceive方法中获取。通过该类用户可以获取error code,位置的坐标,精度半径等信息。具体方法请参考类参考。 获取error code: public int getLocType (…...
网站反链如何做/百度收录网站需要多久
自8月份顺利获得富士康注资以来,夏普在重振道路上开始加足马力展开系列动作。 21日,夏普接连发出两则公告,一则是确定将买回原本在大阪市中心、原总公司所在地的“田边大楼”,公告指出,公司已与NTT集团旗下的不动产公司…...
网站制作价格表/东莞网站推广运营公司
新文章移至 http://cffile.sinaapp.com/?p22tomcat5.0版本的时候,由于context是直接配置在server.xml中的(最终是生成$CATALINA_HOME/conf /[enginename]/[hostname]目录下的应用名称.xml文件,启动顺序是按照目录下生成的应用名称的字典顺序…...
衢州建设职业学校网站/北京官网优化公司
今天早上在写一个.scala文件时,控制台一直出现这个错误。 原本以为这个错误是找不到URL地址,不能使用local[*]这种模式,后来发现这个错误的根本是在上一行,也就是: SparkContext: Error initializing SparkContext 原…...
山东建设企业网站/百度投流运营
Hibernate的核心组件在基于MVC设计模式的JAVA WEB应用中,Hibernate可以作为模型层/数据访问层。它通过配置文件(hibernate.properties或hibernate.cfg.xml)和映射文件(***.hbm.xml)把JAVA对象或PO(Persistent Object,持久化对象)映射到数据库中的数据库,…...