C语言----结构体

一.结构体是什么?

(1)是一种数据类型

首先我们需要知道的是结构体是一种数据类型,它本质上是用于将不同类型的数据组合在一起形成的一个新的数据类型。

(2)是变化的

当不同的类型组合在一起的时候,会产生不同的结构体,例如用char和float组成的与用int和char组成的实际上不是同一种结构体

(3)是自定义的

结构体是用户自定义的一种类型,它使用关键字struct来定义结构体

二.结构体的格式(声明)

struct tag
{member-list;member-2ist;member-3ist;...
}variable-list;variable-2ist;variable-3ist;...;

特殊声明

结构体变量的声明可以实名也可以是匿名声明,也就是把结构体的名称(如上方tag)给删除掉,进行匿名声明。但是这个时候这个结构体就只能使用一次。匿名结构体无法被其他代码块引用,也无法定义指向匿名结构体的指针,限制了其在复杂程序中的使用。

三.结构体变量的创建和初始化

首先我们需要说明一下结构体变量的访问操作符

a.

.”:用于直接访问结构体中的变量

例如:

struct Person person1;
strcpy(person1.name, "Alice");
person1.age = 25;
person1.height = 1.65;

b.

->”:用于访问结构体变量的地址

接下来我们就可以说明结构体变量的创建和初始化

#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[6];//性别
};
int main()
{//按照结构体成员的顺序初始化struct Stu s1 = { "小明", "20" ,"男"};printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);//按照指定的顺序初始化struct Stu s2 = { .age = 16, .name = "xiaohua",  .sex = "⼥"};printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);return 0;
}

四.结构体的自引用

错误用法: 

struct Node
{int data;struct Node next;
};//这样自引用是错误的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤
⼩就会⽆穷⼤

正确用法:

struct code
{int data;struct Node* next;
};//这样自引用是正确的,直接对其解引用就可以避免重复包含

五.结构体的内存对齐

结构体既然作为一种类型,那么它肯定也是占有内存的。而且由于结构体不同,其内存也会跟着不同。一般正常的内存存放方式就是按照类型本身所占字节来直接存放,由于它们类型统一,所以在存放的时候也是规律的,一般不会导致内存的溢出等问题。但是结构体包含多种不同的类型,所占的内存大小也就各不相同。要想保证内存的正常占用以及不出现溢出等问题,就需要进行内存对齐。

(1)内存对齐的规则

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

意思就是第一个成员是从起始位置开始对齐,它与正常类型的存放方式无差别。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

这里介绍对齐数的概念:

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

请注意,对齐数有时候并不是默认的对齐数,当该变量的大小小于默认对齐数时对齐数就是该成员变量的大小。

vs的默认对齐数是8bit。Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。

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

举例:

struct MyStruct 
{char a;int b;char c;
};//根据内存对齐规则,结构体MyStruct的内存布局如下://a的大小为1字节,偏移量为0。
//b的大小为4字节,由于前一个成员a的大小为1字节,所以b的偏移量为4的整数倍,即4。
//c的大小为1字节,由于前一个成员b的大小为4字节,所以c的偏移量为4的整数倍,即8。
//因此,结构体MyStruct的总大小为8字节。

 4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对⻬数)的整数倍。

举例:

struct InnerStruct 
{char x;int y;
};struct OuterStruct 
{char a;struct InnerStruct inner;double b;
};/*根据内存对齐规则,结构体InnerStruct的内存布局如下:x的大小为1字节,偏移量为0。
y的大小为4字节,由于前一个成员x的大小为1字节,所以y的偏移量为4的整数倍,即4。
因此,结构体InnerStruct的总大小为8字节。接下来,根据内存对齐规则,结构体OuterStruct的内存布局如下:a的大小为1字节,偏移量为0。
inner的大小为8字节(由上面计算得出),由于前一个成员a的大小为1字节,所以inner的偏移量为8的整数倍,即8。
b的大小为8字节,由于前一个成员inner的大小为8字节,所以b的偏移量为8的整数倍,即16。
因此,结构体OuterStruct的总大小为24字节。(既是4的整数倍又是8的整数倍)*/

(2)关于内存对齐原因的补充

1.硬件需求: 许多处理器在读取内存时要求数据按照特定的字节对齐方式存储,否则可能导致性能下降甚至错误。例如,某些处理器要求整型数据按4字节对齐,双精度浮点数按8字节对齐。不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.效率需求:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。如果出现某个变量内存恰好是卡在两个内存块中,同时占用了两个内存块,那么这样实际上就需要访问两次,大大降低了效率和性能;如果我们实现内存对齐就不会出现这种情况,减少了所需要消耗的时间,达到用空间换时间的目的。

(3)内存对齐的优化

既然内存对齐会浪费一定的空间,我们在排序变量的时候就应该尽可能的去减少浪费。

举例:

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
//这样排序s1只占了8个,而s2占了12个,所以s1既节省了时间又没有浪费过多空间

 (4)默认对齐数的修改

我们可以通过一个指令来修改默认对齐数满足自己的需要

#pragma 预处理指令

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{printf("%d\n", sizeof(struct S));//输出结果还原为默认的对齐数return 0;
}

六.结构体的传参

来看两种传参的方式

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;
}

这里我们选择传地址的方式会比直接传结构体的方式更佳。

因为函数传参的时候是会涉及到压栈概念的,形参和实参都有各自的内存空间,如果我们直接传结构体的话,会新开辟一个空间;而乳沟我们传地址的话就不会,所耗的内存空间仅占一个地址的字节。所以我们通常选择传地址

七.结构体与位段

位段是一种特殊的结构体成员,即在一个字节或多个字节中指定位数的字段。位段的作用是可以有效地利用内存空间,将多个标志位或状态信息存储在一个整数类型(4字节)或一个字符类型(1字节)的变量中,从而节省内存空间。所以它与内存对齐的效果是相反的,它的第一标准是空间的节省。

格式:

struct 
{unsigned int flag1 : 1; // 1比特位的位段unsigned int flag2 : 2; // 2位的位段unsigned int flag3 : 5; // 5位的位段
} flags;

位段相对于结构体格式上的差别就是多了数字,用来表示所占有的比特位

但是需要注意的是,位段的使用可能会导致代码的可移植性问题,因为位段的存储顺序和字节对齐方式可能在不同的编译器和平台上有所不同。例如,vs的规则是:

1.内存从右向左使用 2.若剩余空间不够下一成员使用就浪费

但是在其他编译器中规则可能就不是这样。

原因在于:

1. int 位段被当成有符号数还是无符号数是不确定的。 2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。 3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。 4. 当⼀个结构包含两个位段,第二个位段成员比较大,无法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

需要注意的第二个点就是位段的成员是无法被取地址的,因为取地址是以字节为单位,从起始位置取,但是位段成员不一定在起始位置,那么根据内存中每个字节分配⼀个地址,⼀个字节内部的bit位就是没有地址的。

要想取到地址只能先将成员放入某个变量中,再赋值给位段成员。

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

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

相关文章

reDOS攻击

正则表达式回溯&#xff1a; 当涉及到正则表达式的回溯时&#xff0c;让我们来看一个具体的例子。 考虑以下正则表达式模式 ab&#xff0c;其中 a 表示匹配一个或多个连续的字符 "a"&#xff0c;b 表示匹配字符 "b"。 现在&#xff0c;假设有一个输入字…

中医师承出师考试

一.考试内容 1.临床实践技能考核 临床实践技能考核主要涉及基本操作和临床答辩。基本操作包括中医四诊、针灸、推拿、拔罐、常见急症针灸技术应用等中医临床技术。临床答辩则包括中医基本理论知识&#xff08;含中医经典有关内容&#xff09;、中药的功效、应用、用法用量、使…

P1914 小书童——凯撒密码

题目背景 某蒟蒻迷上了 “小书童”&#xff0c;有一天登陆时忘记密码了&#xff08;他没绑定邮箱 or 手机&#xff09;&#xff0c;于是便把问题抛给了神犇你。 题目描述 蒟蒻虽然忘记密码&#xff0c;但他还记得密码是由一个字符串组成。密码是由原文字符串&#xff08;由不…

蓝桥杯第十四届电子类单片机组程序设计

目录 前言 蓝桥杯大赛历届真题&#xff08;点击查看&#xff09; 一、第十四届比赛题目 1.比赛原题 2.题目解读 1&#xff09;任务要求 2&#xff09;注意事项 二、任务实现 1.NE555读取时机的问题 1&#xff09;缩短计数时间 2&#xff09;实时读取 2.温度传感器读…

Linux文件操作类命令 find

作用&#xff1a; 实时查询&#xff0c;条件很多&#xff0c;结合shell命令统一处理 格式 find 目录 条件 【shell命令】-name或-iname找具体文件名&#xff0c;-iname&#xff08;常用&#xff09;可以忽略大小写 例子find / -iname passwd找根目录下名字正正…

<网络安全>《35 网络攻防专业课<第一课 - 网络攻防准备>》

1 主要内容 认识黑客 认识端口 常见术语与命令 网络攻击流程 VMWare虚拟环境靶机搭建 2 认识黑客 2.1 白帽、灰帽和黑帽黑客 白帽黑客是指有能力破坏电脑安全但不具恶意目的黑客。 灰帽黑客是指对于伦理和法律态度不明的黑客。 黑帽黑客经常用于区别于一般&#xff08;正面…

问题:在额定电压500V以下的电路中,使用的各种用电设备,一般称为(_ _ _)用电设备 #媒体#媒体#媒体

问题&#xff1a;在额定电压500V以下的电路中,使用的各种用电设备,一般称为&#xff08;_ _ _)用电设备 参考答案如图所示

RPA岗位介绍 - RPA实施工程师

一、RPA实施工程师 1.1 岗位概述 专业负责RPA机器人流程开发、实施工作的工程师,主要职责是依据客户需求,对RPA机器人流程进行设计、开发和部署实施工作,并最终协助项目经理完成项目的交付验收。 1.2 岗位职责 1.在RPA机器人自动流程化项目实施中,负责了解现有的客户流程,…

【dofile版本】实证研究Stata代码命令汇总

一、引言 在现代社会科学研究领域&#xff0c;Stata已成为欧美地区最受欢迎的计量分析软件之一。然而&#xff0c;许多研究人员在使用上仍显生疏 为了帮助研究人员更好地利用Stata&#xff0c;整理了一套Stata实证命令汇总&#xff0c;覆盖了从数据的初步处理到高级统计分析的…

Mysql第一关之常规用法

简介 介绍Mysql常规概念&#xff0c;用法。包括DDL、DCL、DML、DQL&#xff0c;关键字、分组、连表、函数、排序、分页等。 一、 SQL DCMQ&#xff0c;分别代表DDL、DCL、DML、DQL。 模糊简记为DCMQ&#xff0c;看起来像一个消息队列。 D&#xff1a;Definition 定义语句 M…

VUE面试题和详解

这里是引用 当涉及Vue.js的面试题时&#xff0c;以下是一些常见的问题和答案&#xff0c;可以帮助你准备面试&#xff1a; 什么是Vue.js&#xff1f; Vue.js是一个开源的JavaScript框架&#xff0c;用于构建用户界面。它采用了MVVM&#xff08;Model-View-ViewModel&#xff09…

【Vue前端】vue使用笔记0基础到高手第2篇:Vue知识点介绍(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论vue相关知识。Vue.js是前端三大新框架&#xff1a;Angular.js、React.js、Vue.js之一&#xff0c;Vue.js目前的使用和关注程度在三大框架中稍微胜出&#xff0c;并且它的热度还在递增。Vue.js是一个轻巧、高性能、可组件…

[ai笔记7] google浏览器ai学习提效定制优化+常用插件推荐

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第7篇分享&#xff01; 工欲善其事必先利其器&#xff0c;为了ai学习的效能提升&#xff0c;放假期间对google浏览器做了一次系统整改&#xff0c;添加了一些配置和插件&#xff0c;这里既有一些显示、主…

在Visual Studio中搭建Dynamo Python开发环境,效率飞一般的增长

最近在学习Dynamo中Python Script的用法&#xff0c;发现这个东西用起来太不友好了&#xff0c;不支持自动缩进&#xff0c;不支持自动填充和提示。用过Visual Studio做二开的都知道&#xff0c;在引用了Revit api以后&#xff0c;就可以自动填充和提示了。 本来英语就不好&am…

Netty中的内置通信模式、Bootstrap和ChannelInitializer

内置通信传输模式 NIO:io.netty.channel.socket.nio 使用java.nio.channels包作为基础–基于选择器的方式Epoll:io.netty.channel.epoll由JNI驱动的epoll()和非阻塞IO.这个传输支持只有在Linux上可用的多种特性&#xff0c;如果SO_REUSEPORT&#xff0c;比NIO传输更快&#xf…

应对.[henderson@cock.li].mkp勒索病毒:数据恢复与预防措施

尊敬的读者&#xff1a; 在数字化时代&#xff0c;勒索病毒已成为企业和个人数据安全的威胁之一。本文将深入介绍[hendersoncock.li].mkp[hudsonLcock.li].mkp [myersairmail.cc].mkp勒索病毒的特点&#xff0c;提供被其加密的数据文件的恢复方法&#xff0c;并分享一些预防措…

设计模式--职责链模式(Chain of Responsibility Pattern)

职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;它为请求创建了一个接收者对象的链。 这种模式给予请求的类型&#xff0c;对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 在职责链模式中&#xff0…

代码随想录 Leetcode435. 无重叠区间

题目&#xff1a; 代码(首刷看解析 2024年2月17日&#xff09;&#xff1a; class Solution { private:const static bool cmp(vector<int>& a,vector<int>& b) {return a[0] < b[0];} public:int eraseOverlapIntervals(vector<vector<int>&…

学习Android的第十二天

目录 Android SeekBar&#xff1a;拖动条控件 SeekBar 属性 SeekBar 事件 SeekBar 定制 范例&#xff1a; 参考文档 Android RatingBar 星级评分条 RatingBar 属性 RatingBar 样式 RatingBar 事件 范例&#xff1a; 官方文档 Android ScrollView 滚动视图 Scroll…

MessageQueue --- RabbitMQ

MessageQueue --- RabbitMQ RabbitMQ IntroRabbitMQ 核心概念RabbitMQ 分发类型Dead letter (死信)保证消息的可靠传递 RabbitMQ Intro 2007年发布&#xff0c;是一个在AMQP&#xff08;高级消息队列协议&#xff09;基础上完成的&#xff0c;可复用的企业消息系统&#xff0c;…