烫烫烫手的结构体大小计算来咯,很烫哦,慢慢消化。自定义类型(一)

2024-03-30 22:28

本文主要是介绍烫烫烫手的结构体大小计算来咯,很烫哦,慢慢消化。自定义类型(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

emmm,在这炎热的夏天在宿舍吹着空调写着博客也是一件不错的事呢,今天就来来好好盘一下C语言中的自定义类型。

                 常常会回顾努力的自己,所以要给自己的努力留下足迹。

为今天努力的自己打个卡,留个痕迹吧

                                                                      2024.03.29     小闭


目录

结构体的简单使用

 结构体的对齐规则

结构体的大小计算

结构体的简单使用

在 C 语言中,结构体(Structure)是一种用户自定义的数据类型,用于将不同类型的数据组合在一起形成一个复合的数据结构。结构体可以用来表示具有多个相关属性的对象,例如学生信息、员工信息、图书信息等。
 
以下是一个简单的 C 语言结构体代码示例:
 
 

struct Student {char name[50];int age;float marks;
};

这个结构体定义是正常的结构体命名,也有一些特殊的,如下:匿名结构体类型

struct
{int a;char b;float c;
}s;


 
 
上面代码中:我们定义了一个名为 Student 的结构体它包含了三个成员:一个长度为 50 的字符数组 name 用于存储学生的姓名,一个整数 age 用于存储学生的年龄,以及一个浮点数 marks 用于存储学生的成绩。
 
要使用结构体,可以创建结构体变量并访问其成员。例如:
 
 

struct Student student1;
strcpy(student1.name, "Alice"); // 复制字符串到 name 成员
student1.age = 20; // 给 age 成员赋值
student1.marks = 85.5; // 给 marks 成员赋值


 
 
通过” . “操作符,可以访问结构体变量的成员。在这个例子中,我们使用 student1.name 、 student1.age 和 student1.marks 来访问和操作结构体的各个成员。
 

注意:结构体还可以用于定义结构体数组,以及结构体指针等。它们提供了一种灵活的方式来组织和处理相关数据。其中数据结构中的链表和队列等就是用结构体指针完成。


 结构体的对齐规则
 

结构体的大小是结构体知识中是的小奥妙,还是直接写一段代码让大家感受一下,就可以更好的让人理解

int main()
{struct Test{char a;char b;int c;};struct Test T;printf("%d\n", sizeof(T));return 0;
}

 大家是否知道上面结构体的大小为何值,是不是觉得就是两个char和一个int加起来就是6个字节呢,相信初学者都这么认为,因为当初的我也是这样,那么正确答案是什么呢,答案却是8

 

至于为什么呢,这里我们就必须知道一个东西,那就是 ”结构体的内存对齐“。

何为结构体的内存

 那我们就需了解一个对齐规则,那么对齐规则是怎样的呢。

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

二、 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值VS里的默认对齐数为8.

注意:有些编译器没有默认对齐数,此时成员的对齐数就是成员变量大小

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

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

 那么了解完后,我们该如何计算大小呢,那么我们现在就跟着对齐规则来,自己算一下上面代码结构体T大小为何为8吧。


结构体的大小计算

首先第一次见到对齐数很好奇,对齐数是什么?偏移量是什么?上面虽然已经解释了对齐数,但还是想知道如何实际用到对齐数来对结构体大小来进行计算。那么下来我将会把我的理解一步一步来解释。

首先我们先将一块用来储存此结构体的内存地址拿出来这里是一个字节代表一个单位,然后给这块内存从0开始标上序号,然后0数字即是0偏移量处。

int main()
{struct Test{char a;char b;int c;};struct Test T;printf("%d\n", sizeof(T));return 0;
}

然后我们再来看看这个结构体大小如何计算 

先我们把char a开始放在偏移量为0处如图红色部分,之后的成员从自身对齐数的整数倍处开始储存。

char类型的大小为1,与默认对齐数8,较小故此成员的对齐数为1
这里1是1的整数倍,所以放在从数字1处开始放char b,
如图黄色部分

这里如上int c的对齐数还是自身的大小,VS的默认对齐数是8,那么此时int c的自身大小为4,故取小的那么这里int c的对齐数就为4,往下找到最近的对齐数4的倍数处开始往下存放。如图绿色部分

完成成员的分布还没有结束,这里最后才是判断结构体大小的时候,按照上面规则:我们最后结构体大小是用全部成员中的最大的对齐数的整数倍,且是选最后一位成员后的整数倍数字,如这里变量a,b,c中c的对齐数最大为4,由于4处的位置已被分布需在最后位成员往后选4的倍数。即:8。那么此时结构体就是8了。


那么拿一题举例肯定是不准确的,我在拿一个例子来举例:

int main()
{struct R{int a;char b;int arr[2];}s;printf("%d\n", sizeof(s));return 0;
}

那么这里我就继续按照步骤来进行计算大小

int a 继续从偏移量为0处放,然后char b的对齐数为1,那么往后放在对齐数1的整数倍处即:数字4处,再然后int arr[2],这里注意我们不能把int arr[2]当作一个成员而应该当成两个int类型成员(这个要尤其注意),然后先将一个int放在对齐数4的整数倍处即:数字8处,然后继续将第二个元素也是放在对齐数4的整数倍处即:数字12处,。最后完成分布后选出成员中最大的对齐数即4,那么结构体大小为最后一位成员后面最大对齐数4的整数倍,即16.


然后再举一个结构体镶嵌的例子

int main()
{struct R{int a;char b;int arr[2];};struct Test{int a;struct R s;char b;short c;}t;printf("%d\n", sizeof(t));return 0;
}

这里要求的是结构体struct Test t 的大小,那么就看到R的成员,按照规则,将int a放在偏移量为0处,

然后就是镶嵌结构体struct R s;,其自身成员的最大对齐数为4,那么其对齐数为4,那找4的整数倍处即:数字4处。然后是char b对齐数为1,往后就是到数字20处,最后short c 对齐数为2,往后数字20处.完成分布我们找最大对齐数,为4那么其结构体大小为4的整数倍,最后取24为结构体大小。


讲到这里肯定是很疑惑为什么会存在内存对齐,对齐之后有些空间是浪费的。那么它存在就会有它存在的意义。简单来说就是:

结构体内存对齐是为了提高计算机的存储和访问效率。在现代计算机中,内存空间通常按照字节(byte)来划分,但各个硬件平台对存储空间的处理方式存在很大差异。
 
以32位的CPU为例,它一次能处理32bit(4字节)的数据,那么CPU就命令地址总线一次性读取4字节的数据,即每次的步长都为4字节,只对地址是4的整倍数的地址进行寻址,比如:0、4、8、100等。如果一个变量的地址刚好在一个寻址步长内,那么一次寻址就可以读取到该变量的值。但如果变量跨了步长存储,就需要寻址两次甚至多次,然后再进行拼接才能获取到变量的值,效率明显就低了。
 
为了避免这种情况,编译器会进行内存对齐,以保证变量的地址在一个寻址步长内,从而提高程序的运行效率。

总的来说:就是用空间换效率。


那么文章到这就结束了。

为今天努力的自己打个卡,留个痕迹吧

                                                                      2024.03.29     小闭

                                                    

这篇关于烫烫烫手的结构体大小计算来咯,很烫哦,慢慢消化。自定义类型(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/862747

相关文章

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

Python中Json和其他类型相互转换的实现示例

《Python中Json和其他类型相互转换的实现示例》本文介绍了在Python中使用json模块实现json数据与dict、object之间的高效转换,包括loads(),load(),dumps()... 项目中经常会用到json格式转为object对象、dict字典格式等。在此做个记录,方便后续用到该方

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

MySQL中查询和展示LONGBLOB类型数据的技巧总结

《MySQL中查询和展示LONGBLOB类型数据的技巧总结》在MySQL中LONGBLOB是一种二进制大对象(BLOB)数据类型,用于存储大量的二进制数据,:本文主要介绍MySQL中查询和展示LO... 目录前言1. 查询 LONGBLOB 数据的大小2. 查询并展示 LONGBLOB 数据2.1 转换为十

Python文本相似度计算的方法大全

《Python文本相似度计算的方法大全》文本相似度是指两个文本在内容、结构或语义上的相近程度,通常用0到1之间的数值表示,0表示完全不同,1表示完全相同,本文将深入解析多种文本相似度计算方法,帮助您选... 目录前言什么是文本相似度?1. Levenshtein 距离(编辑距离)核心公式实现示例2. Jac

SQL Server 查询数据库及数据文件大小的方法

《SQLServer查询数据库及数据文件大小的方法》文章介绍了查询数据库大小的SQL方法及存储过程实现,涵盖当前数据库、所有数据库的总大小及文件明细,本文结合实例代码给大家介绍的非常详细,感兴趣的... 目录1. 直接使用SQL1.1 查询当前数据库大小1.2 查询所有数据库的大小1.3 查询每个数据库的详