解析编程中不可或缺的基础:深入了解结构体类型

精琢博客,希望可以给大家带来收获~

博主主页:17_Kevin-CSDN博客

收录专栏:《C语言》


引言

在编程中,结构体是一种自定义的数据类型,它允许开发人员将不同类型的数据组合在一起,并为其定义相关属性和行为。结构体提供了一种灵活的方式来表示复杂的数据结构,使得程序设计更加模块化和可读性更高。

结构体类型的声明 

结构的声明
 

声明格式如下:

struct 结构体类型名
{成员名-list;}直接声明变量-list;

结构体变量的声明和使用

下面是一个程序。首先创建了一个结构体类型Stu,里面包括了成员变量name、age、sex和 id。在主函数中创建了结构体变量s,并打印。

结构体变量创建格式:

① 按成员顺序初始化:结构体类型名 + 自定义变量名(+ 初始化内容);

② 按指定顺序初始化:结构体变量名 + 自定义变量名 (+ .成员名);

 变量的使用:结构体变量名 . 成员名

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

结构的不完全声明

在声明结构体类型的时候可以不完全声明,直接在结构体类型后声明变量,这样创建的变量就是一次性变量,之后只能一次性使用。

声明如下:

struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], * p;

结构的⾃引⽤

结构的自引用典型例子就是链表中对节点的定义,用于连续节点连接,具体有关链表的知识可以点击这段蓝字阅读博主另一篇博客。

自引用结构声明格式如下:

struct Node
{int data;struct Node next;
};

自定义结构体

声明格式如下:

格式:typedef + struct 结构体类型名 

        {

                成员变量;

        }自定义类型名;

 示例声明如下:

typedef struct Node
{int data;struct Node* next;
}Node;

结构体内存对⻬(热门考点)

引子

我们经常会用sizeof运算符计算各个变量的字节大小,例如:

#include<stdio.h>int main()
{printf("%d\n", sizeof(int));printf("%d\n", sizeof(short));printf("%d\n", sizeof(long long));return 0;
}

得到结果:

如文所示,我们可以用sizeof来计算各个类型的大小,那么结构体变量计算会得到什么结果呢?

对⻬规则

结构体变量在内存中会遵循结构体对齐规则,对齐规则如下:

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

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

   对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。

    VS 中默认的值为
    Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

结构体内存对齐练习

1.非嵌套结构体

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

按照内存对齐规则,从编译器行数从上到下进行内存存储。逐个对比各个成员和VS的默认对齐数8对比,取二者最小对齐数作为对齐数。

根据对齐数从0开始偏移计算每个变量开始存储的内存地址,成员变量要对⻬到对⻬数的整数倍的地址处,如图所示。

最后计算结构体总共大小是需要按照结构体中成员变量的最大对齐数进行对齐,最终结构体大小是最大对齐数的整数倍,产生的浪费空间也要计入总大小。

内存存储图示如下:

2.嵌套结构体

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

计算嵌套结构体的字节大小时对待被嵌套的结构体时,就相当于把嵌套结构体当做结构体的一个成员进行内存对齐。最终计算字节总大小的时候用所有成员中最大对齐数(包括被嵌套结构体中的成员)进行整数倍的计算。

下图即为上述代码的演示图例:

为什么存在内存对⻬?

1. 硬件访问要求: 计算机硬件对于访问内存通常有一定的要求,例如某些硬件可能只能从特定地址开始读取数据,或者只能按照特定的字节长度进行读取。通过内存对齐,可以保证结构体中的字段在内存中按照一定的规则排列,满足硬件访问的要求。

2. 性能优化: 在数据结构中,尤其是在涉及栈这种数据结构时,我们应该尽可能使数据在自然边界上对齐。这样做的原因在于,处理器访问未对齐的内存时需要进行两次内存访问,而对齐的内存访问只需要一次访问。举例来说,如果一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能够保证所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。因此,通过合理地对数据进行内存对齐,我们可以提高程序的执行效率和性能表现。

3. 内存空间利用: 内存对齐可以使数据结构更加紧凑,减少内存空间的浪费。如果结构体中的字段按照对齐规则排列,编译器可以合理地利用内存空间,避免由于未对齐而导致的内存浪费。

4. 平台移植性: 不同的计算机架构可能对内存对齐有不同的要求。合理地处理结构体的内存对齐可以增强程序在不同平台上的移植性,使程序更容易地在不同平台上移植和运行

针对于性能优化,我们可以了解到结构体对齐是为了优化性能,用空间换时间,那么有没有什么办法让我们尽量的减少浪费的空间呢?

我们可以利用结构体对齐的规则,将小的结构体尽量的凑在一起,这样他们会在空间上连续存储,因为对齐数小的和大的之间会存在大对齐数所造成的空间浪费,,所以将小的放一块这样就可以将其中的浪费空间给合理利用起来。

 具体如下代码示例及图示:

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};

修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

例如,我们要将编译器的默认对齐数修改为1,那么勇以下代码实现:

#pragma pack(1)

如果需要取消修改的默认对齐数,使用以下代码即可实现:

#pragma pack()


 

位段结构体

当我们需要在C或C++中表示一些具有特定位长度的数据时,位段(bit fields)结构体就成为了一种非常有用的工具。位段结构体允许我们将数据按位组织,并且可以更加高效地使用内存空间。

什么是位段结构体?

位段结构体是C和C++中的一种特殊结构,它允许我们定义结构体的成员为特定位长度的字段,从而更为灵活地管理数据。通过位段结构体,我们可以精确地控制每个字段的位数,从而在内存中节约空间。

如何定义位段结构体?

在C和C++中,我们可以使用结构体来定义位段。

位段的声明和结构是类似的,有两个不同:

1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字

下面是一个简单的例子:

struct BitFieldStruct 
{unsigned int flag1 : 1;unsigned int flag2 : 2;unsigned int flag3 : 3;
};

位段的内存分配
 

分配规则:
1. 位段的成员可以是 int  unsigned int  signed int 或者是 char 等类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
 

通过上文已经得知位段结构体如何创建,下面请通过示例代码和图示来了解位段结构体再内存中的分配原理。

代码如下:

struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
struct S s = { 0 };s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

代码中定义了一个结构体类型S,在main函数中创建S型变量s并初始化为0。重点在于,在已经规定的位段情况下,后面的a,b,c,d赋值后在内存中是如何存储的呢?

图示操作如下:

最后的d由于在第二个字节段中无法存储,所以会直接存到下一个字节中,大小位4比特。


使用位段的注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位
置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先⼊放在⼀个变量中,然后赋值给位段的成员。

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{struct A sa = { 0 };//这是错误的scanf("%d", &sa._b);//正确的⽰范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}


 

位段结构体的优势

  1. 节省内存空间:位段结构体可以将多个字段压缩到一个字节中(或者更少),这样可以减少内存使用量。在一些嵌入式系统或需要高效利用内存的场景中,位段结构体可以发挥重要作用。

  2. 更好的可移植性:位段结构体可以帮助开发者更好地处理不同机器上的字节顺序问题和对齐方式。因为位段结构体的字段是按照位来处理的,所以不受机器的字节顺序和对齐方式的影响。

  3. 方便地操作位数据:位段结构体可以方便地处理二进制数据,例如一些硬件寄存器中的位标志。使用位段结构体可以使得代码更加简洁和易读,降低出错的风险。

  4. 更好的兼容性:位段结构体的语法与普通结构体非常相似,因此可以很容易地与其他代码进行交互和集成。此外,C++11标准中还引入了新的标准化位字段类型,称为 std::bitset,可以更加方便地处理位数据。


我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1g0z83rjgi6dj

I'm Kevin, and we'll see you in the next blog

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

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

相关文章

uniapp 跳转返回携带参数(超好用)

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.返回界面 uni.$emit(enterPeople, this.entryList)uni.navigateBack({delta: 1}) 2.返回到的界面&#xff08;接收数据界面&#xff09; onShow() {let that thisuni.$on(enterPeople,function(enterPeopledata){console.…

YOLOv8 | 添加注意力机制报错KeyError:已解决,详细步骤

目录 添加注意力机制报错 报错的原因 注意事项 解决错误流程 代码分享 ⭐欢迎大家订阅我的专栏一起学习⭐ &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; YOLOv5涨点专栏&#xff1a;h…

重学SpringBoot3-ServletWebServerFactoryAutoConfiguration类

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-ServletWebServerFactoryAutoConfiguration类 工作原理关键组件以TomcatServletWebServerFactory为例ServletWebServerFactory会创建webServer的时机关键…

25考研数据结构复习·3.2队列

队列&#xff08;Queue&#xff09;基本概念 定义 队列&#xff08;Queue&#xff09;时只允许在一端进行插入&#xff0c;在另一端删除的线性表。 特点&#xff1a;先进入队列的元素先出队 先进先出 First In First Out(FIFO) 重要术语 队头、队尾、空队列 基本操作 创、销 I…

页面配置、网络数据请求

1. 页面配置文件的作用 小程序中&#xff0c;每个页面都有自己的 .json 配置文件&#xff0c;用来对当前页面的窗口外观、页面效果等进行配置。 2. 页面配置和全局配置的关系 小程序中&#xff0c; app.json 中的 window 节点&#xff0c;可以全局配置小程序中每个页面的窗口…

网络编程-套接字相关基础知识

1.1. Socket简介 套接字&#xff08;socket&#xff09;是一种通信机制&#xff0c;凭借这种机制&#xff0c; 客户端<->服务器 模型的通信方式既可以在本地设备上进行&#xff0c;也可以跨网络进行。 Socket英文原意是“孔”或者“插座”的意思&#xff0c;在网络编程…

Java初阶数据结构二叉树实现+练习完整(工程文件后序会进行上传)

i1.二叉树的概念 1.二叉树的定义 &#xff08;1&#xff09;二叉树可以是一个节点的有限集合 &#xff08;2&#xff09;可以为空 &#xff08;3&#xff09;或者是由一个根节点加上两棵分别称为左子树和右子树的二叉树组成的 &#xff08;4&#xff09;二叉树的每一个节点…

python爬虫实战——抖音

目录 1、分析主页作品列表标签结构 2、进入作品页前 判断作品是视频作品还是图文作品 3、进入视频作品页面&#xff0c;获取视频 4、进入图文作品页面&#xff0c;获取图片 5、完整参考代码 6、获取全部作品的一种方法 本文主要使用 selenium.webdriver&#xff08;Firef…

手机网络连接性能API接口:查询手机网络连接性能状态

手机在网状态查询服务是一项非常方便的服务&#xff0c;可以帮助我们随时了解一个手机号码的在网状态。不论是查询自己的手机号码&#xff0c;还是查询他人的手机号码&#xff0c;这个服务都可以帮助我们获取准确的信息。今天&#xff0c;我想和大家介绍一个非常好用的手机在网…

力扣100题—持续更新

目录 LC141环形列表(easy)题目描述方法1&#xff1a;快慢指针&#xff08;1&#xff09;思路&#xff08;2&#xff09;python代码&#xff08;3&#xff09;复杂度分析 LC881救生艇&#xff08;medium&#xff09;题目描述方法1&#xff1a;双指针-对撞指针&#xff08;1&…

柚见第十二期(随机匹配)

随机匹配 目的 为了帮大家更快地发现和自己兴趣相同的朋友 问题 匹配 1 个还是匹配多个&#xff1f; 答&#xff1a;匹配多个&#xff0c;并且按照匹配的相似度从高到低排序 怎么匹配&#xff1f;&#xff08;根据什么匹配&#xff09; 答&#xff1a;标签 tags 还可以根据 us…

分享一下自己总结的7万多字java面试笔记和一些面试视频,简历啥的,已大厂上岸

分享一下自己总结的7万多字java面试笔记和一些面试视频&#xff0c;简历啥的&#xff0c;已大厂上岸 自己总结的面试简历资料&#xff1a;https://pan.quark.cn/s/8b602fe53b58 文章目录 SSMspringspring 的优点&#xff1f;IoC和AOP的理解**Bean 的生命周期****列举一些重要…

一命通关差分

本章节是前缀和的延申 一命通关前缀和-CSDN博客https://blog.csdn.net/qq_74260823/article/details/136530291?spm1001.2014.3001.5501 一命通关前缀和 公交车 引入 还是利用我们在前缀和中所采用的例子——公交车。 有一辆公交车&#xff0c;一共上下了N批乘客&#xff1a…

【Vue3】源码解析-Runtime

文章目录 系列文章packages/runtime-dom/src/index.ts初始化创建renderermount \src\runtime-core\component.jsh.tspackages/runtime-core/src/renderer.ts挂载及卸载DOM节点render packages/runtime-dom/src/nodeOps.tspackages/runtime-core/src/apiCreateApp.ts创建appmoun…

全国农产品价格分析预测可视化系统设计与实现

全国农产品价格分析预测可视化系统设计与实现 【摘要】在当今信息化社会&#xff0c;数据的可视化已成为决策和分析的重要工具。尤其是在农业领域&#xff0c;了解和预测农产品价格趋势对于农民、政府和相关企业都至关重要。为了满足这一需求&#xff0c;设计并实现了全国农产…

RabbitMQ 面试题及答案整理,最新面试题

RabbitMQ的核心组件有哪些&#xff1f; RabbitMQ的核心组件包括&#xff1a; 1、生产者&#xff08;Producer&#xff09;&#xff1a; 生产者是发送消息到RabbitMQ的应用程序。 2、消费者&#xff08;Consumer&#xff09;&#xff1a; 消费者是接收RabbitMQ消息的应用程序…

哥斯拉流量webshell分析-->ASP/PHP

哥斯拉流量webshell分析 哥斯拉是继菜刀、蚁剑、冰蝎之后的又一个webshell利器&#xff0c;这里就不过多介绍了。 哥斯拉GitHub地址&#xff1a;https://github.com/BeichenDream/Godzilla 很多一线师傅不太了解其中的加解密手法&#xff0c;无法进行解密&#xff0c;这篇文章…

Web3社交项目UXLINK零撸教程

简介&#xff1a;UXLINK是Web3行业中首个主打双向、熟人社交关系的产品&#xff0c;与其他社交基础设施类项目相比&#xff0c;类似Twitter Vs Facebook、微博 Vs 微信的社交结构区别。UXLINK的愿景&#xff1a;1、社交中心&#xff1a;成为最大的社交平台&#xff0c;专注于真…

goctl-swagger 生成json接口文件

参考&#xff1a; GitHub - dyntrait/goctl-swagger: 通过 api 文件生成 swagger 文档 GitHub - Bluettipower/goctl-swagger 一:编译 执行go install 前一般需要设置环境&#xff0c;不然资源经常会下载不下载 go env -w GOPROXYhttps://goproxy.cn,direct 执行完 go in…

IO流(主要是记住四大类InputStream,OutputStream、Reader和Writer,其他都是他们的子类)

IO流 1、文件 &#xff08;1&#xff09;文件概念 文件就是保存数据的地方。例如word文档&#xff0c;txt文件&#xff0c;execl文件等等。 &#xff08;2&#xff09;文件流 文件在程序中是以流的形式来操作的。 流&#xff1a;数据在数据源&#xff08;文件&#xff09;…