yo!这里是结构体内存对齐

2023-10-20 09:10
文章标签 结构 对齐 体内 yo

本文主要是介绍yo!这里是结构体内存对齐,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

对齐规则

内存对齐举例

举例一:

举例二:

举例三:

举例四:

存在内存对齐的原因

1.平台原因

2.性能原因

其他

修改默认对齐数

函数offsetof


前言

        结构体是c语言必学知识点之一,可以为后续学习数据结构和算法打下良好的基础,在学习的过程中,当我们尝试用sizeof去计算结构体的大小时,会发现结构体的大小根本就不是简单的结构体成员大小加在一起,比如,下图中的结构体,如果简单的进行成员的加和会得到总共7个字节,但当我们去计算时就会发现此结构体的大小为8,这是为什么呢?

        这是因为结构体存储在内存之中会存在内存对齐,结构体内存对齐是大大小小的面试当中一个非常热门的考点,要想熟练的掌握这个知识点,就必须先了解一下对齐规则。

对齐规则

        简单概括一下对齐规则,能够利用其解题即可。

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

内存对齐举例

        以下举例均是在vs2019中实现,默认对齐数为8。

举例一:

计算步骤:

 

1.c1作为结构体首个成员从偏移位为0的地址处存放;

2.第二个成员为i,大小为4,与默认对齐数8相比较小,所以此成员对齐数为4,应对齐到4的整数倍的地址处,偏移位1、2、3均不是4的整数倍,因此从偏移位4的地址处开始存放;

3.第三个成员为c2,大小为1,与默认对齐数8相比较小,所以此成员对齐数为1,应对齐到1的整数倍的地址处,除0以外都是1的整数倍,所以紧接着偏移位8的地址处开始存放;

4.此时结构体大小为9,不是最大对齐数4(1、4、1中最大)的整数倍,应再浪费三个字节扩大到4的整数倍,最终的结构体大小为12。

举例二:

计算步骤:

 

 

1.c1作为结构体首个成员从偏移位为0的地址处存放;

2.第二个成员为c2,大小为1,与默认对齐数8相比 较小,所以此成员对齐数为1,因此从偏移位1的地址处开始存放;

3.第三个成员为i,大小为4,与默认对齐数8相比较小,所以此成员对齐数为4,应对齐到4的整数倍的地址处,所以浪费掉两个字节,从偏移位4的地址处开始存放;

4.此时结构体大小为8,是最大对齐数4(1、1、4中最大)的整数倍,所以最终的结构体大小为8。

举例三:

 计算步骤:

 

1.d作为结构体首个成员从偏移位为0的地址处存放;

2.第二个成员为c,大小为1,与默认对齐数8相比 较小,所以此成员对齐数为1,因此从偏移位8的地址处开始存放;

3.第三个成员为i,大小为4,与默认对齐数8相比 较小,所以此成员对齐数为4,应对齐到4的整数倍的地址处,所以浪费掉三个字节,从偏移位12的地址处开始存放;

4.此时结构体大小为16,是最大对齐数8(8、1、4中最大)的整数倍,所以最终的结构体大小为16。

举例四:

 计算步骤:

 

1.c1作为结构体首个成员从偏移位为0的地址处存放;

2.第二个成员为s3,大小为16,对齐数为其成员最大对齐数,所以此成员对齐数为8,因此浪费掉7个字节从偏移位8的地址处开始存放;

3.第三个成员为d,大小为8,与默认对齐数8相等,所以此成员对齐数为8,应对齐到8的整数倍的地址处,从偏移位24的地址处开始存放;

4.此时结构体大小为32,是最大对齐数8(1、8、8中最大)的整数倍,所以最终的结构体大小为32。

存在内存对齐的原因

1.平台原因

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

        比如说,在某些平台上,从内存中读取数据时只能从4的倍数偏移位的地址处读取,若没有内存对齐,则将读不到数据造成异常。

2.性能原因

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

        比如说,32机器下一次可以读取4个字节,不考虑对齐时,当我们需要读取i的数据,需要两次读取才能读取一个完整的i;考虑对齐时,读取第一次与i没有关系,读取第二次就能完整读取i的数据,即只需要一次读取。

         总体来说,结构体的内存对齐就是在拿空间换时间,加大读取数据的效率,但如果当我们在设计结构体时,尽量将占用空间小的成员集中在一起,这样呢,同时又能节省空间。

其他

  • 修改默认对齐数

        在上面的举例中我们是在vs2019的环境中进行的,它的默认对齐数是8,而这个默认对齐数是可以通过预处理指令#pragma更改的,比如

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8 
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认#pragma pack(1)//设置默认对齐数为1,相当于没有对齐
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认int main()
{printf("%d\n", sizeof(struct S1));   //12printf("%d\n", sizeof(struct S2));   //6return 0;
}

        虽然可以更改,但建议还是不要更改,或者说更改为2的幂次方(具体要看目的需求),因为机器在读取数据时要么一次读取4个字节或者8个字节,要尽量与读取的字长保持一致,让硬件达到一个很好的效果。

  • 函数offsetof

        在上面的计算中,我们发现结构体的每个成员都是偏移首地址某位开始存放,而offsetof这个函数就是计算结构体成员变量相对于首地址的偏移位。

1.函数语法

2.用法举例

        对于上面的举例一,如图计算其中结构体变量i的偏移量,与我们的计算思路结果一致。

        对于上面的举例四,如图计算其中结构体变量s的偏移量,与我们的计算思路结果一致。


        结构体的内存对齐的知识点就是这些啦,如果文章中有错误或者有不懂的铁子可以私信我或者写在评论区,我们一起探讨一下,希望三连,感谢。

这篇关于yo!这里是结构体内存对齐的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据

《mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据》文章主要介绍了如何从.frm和.ibd文件恢复MySQLInnoDB表结构和数据,需要的朋友可以参... 目录一、恢复表结构二、恢复表数据补充方法一、恢复表结构(从 .frm 文件)方法 1:使用 mysq

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可