C++ 中内存对齐原理及作用

struct/class/union内存对齐原则有四个:

1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。

2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部”最宽基本类型成员“的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。

3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的”最宽基本类型成员”的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。

4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。

实例解释:下面以class为代表

No.1

class Data
{
    char c;
    int a;
};

cout << sizeof(Data) << endl;

No.2

class Data
{
    char c;
    double a;
};

cout << sizeof(Data) << endl;

显然程序No.1 输出的结果为 8 No.2 输出的结果为 16 .

No.1最大的数据成员是4bytes,1+4=5,补齐为4的倍数,也就是8。而No.2为8bytes,1+8=9,补齐为8的倍数,也就是16。

No.3

class Data
{
    char c;
    int a;
    char d;
};

cout << sizeof(Data) << endl;

No.4

class Data
{
    char c;
    char d;
    int a;
};

cout << sizeof(Data) << endl;

No.3运行结果为 12 No.4运行结果为 8

class中的数据成员放入内存的时候,内存拿出一个内存块来,数据成员们排队一个一个往里放,遇到太大的,不是把自己劈成两半,能放多少放多少,而是等下一个内存块过来。这样的话,就可以理解为什么No.3,No.4两端的代码输出结果不一样了,因为左边是1+(3)+4+1+(3)=12,而右边是1+1+(2)+4=8。括号中为补齐的bytes。

No.5

class BigData
{
    char array[33];
};

class Data
{
    BigData bd;
    int integer;
    double d;
};

cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

No.6

class BigData
{
    char array[33];
};

class Data
{
    BigData bd;
    double d;
};

cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

No.5和No.6运行结果均为: 48

在默认条件下,内存对齐是以class中最大的那个基本类型为基准的,如果class中有自定义类型,则递归的取其中最大的基本类型来参与比较。在No.5和No.6中内存块一个接一个的过来接走数据成员,一直到第5块的时候,BigData里只剩1个char了,将它放入内存块中,内存块还剩7个bytes,接下来是个int(4bytes),能够放下,所以也进入第5个内存块,这时候内存块还剩3bytes,而接下来是个double(8bytes),放不下,所以要等下一个内存快到来。因此,No.5的Data的size=33+4+(3)+8=48,同理No.6应该是33+(7)+8=48。

顺便提一下Union: 共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。

No.7

class A
{
    int i;
    char c1;
}

class B:public A
{
    char c2;
}

class C:public B
{
    char c3;
}

sizeof(C)结果是多少呢,gcc和vs给出了不同的结果,分别是8、16

gcc中:C相当于把所有成员i、c1、c2、c3当作是在一个class内部,(先继承后对齐)

vs中:对于A,对齐后其大小是8;对于B,c2加上对齐后的A的大小是9,对齐后就是12;对于C,c3加上对齐后的B大小是13,再对齐就是16 (先对齐后继承)

关于c++对象继承后的内存布局,更详细的分析可以《深度探索参考c++对象模型》第三章

内存对齐的主要作用是:

1、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。

这里写图片描述

这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。

这里写图片描述

CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。

假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:

1、数据从0字节开始

2、数据从1字节开始

再次假设内存读取粒度为4。

这里写图片描述

当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。

当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。

这里写图片描述

此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。

这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。

  • 18
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第 1 章:C++ 基础知识 此模块将向您介绍 C++,包括其历史、设计理念以及几个最重要的功能。此模块简要概述几个 C++ 功能,包括 C++ 程序的一般形式、一些基本控制语句和运算符。它不会介绍太多细节,而会重点介绍对所有 C++ 程序都通用的一般概念。 第 2 章:数据类型和运算符简介 编程语言的核心在于其数据类型和运算符。不出您所料,C++ 支持大量数据类型和运算符,使其适合的编程范围非常广泛。此模块对 C++ 基本数据类型及其最常用运算符进行探讨。我们还将进一步了解变量,并研究表达式。 第 3 章:程序控制语句 此模块讨论用于控制程序执行流的语句。有三种类别的程序控制语句:选择语句,包括 if 和 switch 语句;迭代语句,包括 for、while 和 do-while 循环;以及跳转语句,包括 break、continue、return 和 goto 语句。 第 4 章:数组、字符串和指针 此模块讨论数组、字符串和指针。数组是变量的集合,这些变量具有相同的类型,由一个公用名引用。数组为创建相关变量的列表提供了一种便利方法。C++ 语言不定义内置字符串数据类型。相反,字符串作为字符数组实现。指针是包含内存地址的对象。通常,指针用于访问另一个对象的值。 第 5 章:函数简介 此模块开始深度探讨函数。函数是 C++ 的构建基块,深入理解函数是成为成功 C++ 编程人员的基础。下面,您将了解如何创建函数。您还将了解传递参数、返回值、局部变量和全局变量、函数原型和递归。 第 6 章:进一步了解函数 此模块继续探讨函数。它讨论了 C++ 的三个最重要的函数相关主题:引用、函数重载和默认参数。 第 7 章:更多数据类型和运算符 此模块返回到数据类型和运算符的主题。除了您到目前为止已在使用的数据类型,C++ 还支持其他几种数据类型。其一些数据类型由已知类型加上修饰符组成。其他数据类型包括 enumeration 和 typedef。C++ 还提供多个附加运算符,极大地扩展了 C++ 可以应用到的编程任务范围。 第 8 章:类和对象 类是 C++ 的基本封装单位。类用于创建对象。若要编写面向对象的程序,需要使用类。类和对象对于 C++ 非常重要,因此本书其余内容大部分都或多或少与它们相关。 第 9 章:进一步了解类 此模块继续探讨模块 8 谈到的类。它涉及很多与类相关的主题,包括重载构造函数、传递对象到函数以及返回对象。它还介绍一种特殊类型的构造函数(称为复制构造函数),这种函数在需要对象副本时使用。接下来介绍友元函数,然后是结构和联合,以及 this 关键字。此模块最后介绍运算符重载,这是 C++ 最吸引人的功能之一。 第 10 章:继承、虚函数和多态性 此模块讨论 C++ 与面向对象编程直接相关的三个功能:继承、虚函数和多态性。继承是允许一个类继承另一个类特性的功能。虚函数是在继承的基础上构建的。虚函数支持多态性(面向对象编程的“一个接口,多种方法”原理)。 第 11 章:C++ I/O 系统 C++ I/O 系统非常大,无法在此讨论每个类、函数或功能,不过此模块将介绍最重要和最常用的部分。具体而言,它说明如何输入或输出所设计类的对象。它还介绍如何设置输出格式以及如何使用 I/O 操纵器。此模块最后讨论文件 I/O。 第 12 章:异常、模板和其他高级主题 最后一个模块将介绍几个重要的、高级 C++ 主题,包括异常处理、模板、动态分配和命名空间。另外还介绍运行时类型 ID 和转换运算符。完成此模块后,您将掌握这种语言的核心元素,能够开始编写实际程序。 掌握检查的答案 附录 A:预处理器 预处理器是编译器的一部分,在将源代码实际转换为对象代码之前,预处理器对程序执行各种文本操作。可以为预处理器提供文本操作命令。这些命令称为预处理器指令,它们实际上不是 C++ 的组成部分,但扩展了 C++ 编程环境的范围。
回答: C语言的结构体在内存布局时会进行内存对齐内存对齐是为了提高访问效率和处理器的性能。不同的编译器和平台有不同的默认对齐规则,但通常情况下,结构体的成员会按照其大小和类型进行对齐,即每个成员的地址都是对齐的。 在引用的程序,使用了sizeof运算符来计算结构体A、B和C的大小。可以看到,结构体的大小是按照成员的大小和对齐规则来计算的。结构体A只有一个char类型成员和一个int类型成员,所以大小是5字节。结构体B有一个char类型成员、一个int类型成员和一个double类型成员,所以大小是16字节。结构体C有一个char类型成员、一个int类型成员、一个double类型成员和一个char类型成员,所以大小是24字节。 在引用的例子,结构体stu1嵌套了结构体stu2,这种嵌套的情况也会影响内存对齐。具体的对齐规则可以根据编译器和平台的不同而有所差异。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ 结构体内存对齐](https://blog.csdn.net/cpp_learner/article/details/119246994)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【C/C++内存对齐(超详细,看这一篇就够了)](https://blog.csdn.net/weixin_48896613/article/details/127371045)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值