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

相关文章

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

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

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

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

Java集合中的链表与结构详解

《Java集合中的链表与结构详解》链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序的通过链表中的引用链接次序实现,文章对比ArrayList与LinkedList的结构差异,详细讲解了链表... 目录一、链表概念与结构二、当向单链表的实现2.1 准备工作2.2 初始化链表2.3 打印数据、链表长

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

SpringBoot利用树形结构优化查询速度

《SpringBoot利用树形结构优化查询速度》这篇文章主要为大家详细介绍了SpringBoot利用树形结构优化查询速度,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一个真实的性能灾难传统方案为什么这么慢N+1查询灾难性能测试数据对比核心解决方案:一次查询 + O(n)算法解决

Oracle查询表结构建表语句索引等方式

《Oracle查询表结构建表语句索引等方式》使用USER_TAB_COLUMNS查询表结构可避免系统隐藏字段(如LISTUSER的CLOB与VARCHAR2同名字段),这些字段可能为dbms_lob.... 目录oracle查询表结构建表语句索引1.用“USER_TAB_COLUMNS”查询表结构2.用“a

MySQL中的索引结构和分类实战案例详解

《MySQL中的索引结构和分类实战案例详解》本文详解MySQL索引结构与分类,涵盖B树、B+树、哈希及全文索引,分析其原理与优劣势,并结合实战案例探讨创建、管理及优化技巧,助力提升查询性能,感兴趣的朋... 目录一、索引概述1.1 索引的定义与作用1.2 索引的基本原理二、索引结构详解2.1 B树索引2.2

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho

Python循环结构全面解析

《Python循环结构全面解析》循环中的代码会执行特定的次数,或者是执行到特定条件成立时结束循环,或者是针对某一集合中的所有项目都执行一次,这篇文章给大家介绍Python循环结构解析,感兴趣的朋友跟随... 目录for-in循环while循环循环控制语句break语句continue语句else子句嵌套的循

Python+PyQt5实现文件夹结构映射工具

《Python+PyQt5实现文件夹结构映射工具》在日常工作中,我们经常需要对文件夹结构进行复制和备份,本文将带来一款基于PyQt5开发的文件夹结构映射工具,感兴趣的小伙伴可以跟随小编一起学习一下... 目录概述功能亮点展示效果软件使用步骤代码解析1. 主窗口设计(FolderCopyApp)2. 拖拽路径