内存对齐到底是怎么回事?

作者: null122 | 来源:发表于2016-05-26 20:49 被阅读2270次
内存对齐问题是各种开发类面试中最热门的问题,面试管一般认为这个问题可以考察被面试者对内存细节的了解
情况,确实这个问题对于C++初学者来说是个十足的难题因为它不仅涉及了pragma pack(n) 设定的内存对齐系数
还涉及了相关内存分配的细节。

内存对齐:

我们知道现代计算机体系中CPU按照双字、字、字节访问存储内存,并通过总线进行传输,若未经一定规则的对齐,CPU的访址操作与总线的传输操作将会异常的复杂,所以现代编译器中都会对内存进行自动的对齐。

1.内存对齐系数

说道内存对齐,就不得不说内存对齐系数, 对齐系数最简单的设置方法是使用 #pragma pack(n)进行设置,这部分点进链接在我的文章内有详细说明!

2.sizeof

说到内存对齐第二个不得不说的就是sizeof,它的基本作用是判断数据类型或者表达式长度,要注意的是这不是一个函数,而是一个C++中的关键字!字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来!

3.类型的长度与数据成员对齐

你的计算机中,数据类型的长度指的就是在你的计算机中对数据类型使用sizeof得到的结果,当然这个在各种不同的编译环境下得到的结果是不同的。
比如在32Visual Studio环境下:

cout << sizeof(char) << endl;  // 1
cout << sizeof(short) << endl;  // 2
cout << sizeof(int) << endl;  // 4
cout << sizeof(long) << endl;  // 4
cout << sizeof(double) << endl;  // 8

而在64G++编译环境下:

cout << sizeof(char) << endl;  // 1
cout << sizeof(short) << endl;  // 2
cout << sizeof(int) << endl;  // 4
cout << sizeof(long) << endl;  // 8
cout << sizeof(double) << endl;  // 8

下面我将在32Visual Studio环境下讲解数据成员对齐:
  首先我们要清楚结构体struct中的成员在内存中的分配是连续的,struct内的首地址也就是struct内第一个数据成员的地址,换句话说struct内第一个数据成员离struct开始的距离offset = 0
  数据成员对齐的规则就是,而在第一个成员之后,每个成员距离struct首地址的距离 offset, 都是struct内成员自身长度(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍,如果未经对齐时不满足这个规则,在对齐时就会在这个成员前填充空子节以使其达到数据成员对齐。

默认n8时:

struct {
    char a;
    double b;
} myStruct;
cout << sizeof myStruct << endl;  // 16
cout << (int *)&myStruct.a << endl;  // 0024F898
cout << &myStruct.b << endl;  // 0024F8A0(因运行时而异)

当设置n为4也就是min(sizeof(double), n) = 4 时:
#pragma pack(4)
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 12
cout << (int *)&myStruct.a << endl; // 0046F76C
cout << &myStruct.b << endl; // 0046F770

第一个例子时,最小值为8,填充7个字节到char a 之后。
第二个例子时,最小值为4,填充3个字节到char a之后。

4.整体对齐

编译器在进行过数据成员对齐之后,还要进行整体对齐。与数据对齐相似但不是完全相同, 如果数据对齐完成时struct的大小不是 struct内成员自身长度最大值(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍。(注意这里是成员中长度最大的那个与n比较,而不是特定的一个成员。)就要在struct的最后添加空字节直到对齐。

当设置n为4也就是min(sizeof(short), n) = 2 时:

#pragma pack(4)
struct {
    char a;
    short b;
    char c;
} myStruct;
cout << sizeof myStruct << endl;  // 6
cout << (int *)&myStruct.a << endl;  // 003DFED0
cout << &myStruct.b << endl;  // 003DFED2
cout << (int *)&myStruct.c << endl;  // 003DFED4

在上面的例子中,char a offset为0 因成员对齐占据[D0]填充[D1]共两个字节,short b是最大长度成员无需对齐占据[D2-D3]两个字节,它的offset2,而char coffset4占据[D4]无需成员对齐,但此时struct的大小是2+2+1 = 5字节,不是2的整数倍,所以我们要填充空子节在最后直到struct大小达到2的整数倍,这就是整体对齐。

经过了数据成员对齐与整体对齐之后内存对齐就完成了,如果深入思考上述规则还会发现:即使是同样数目与数量的数据成员,在摆放的顺序不同时struct的大小也会不同,下面就是一个例子:

这样摆放是12字节:

12字节.png 结果是12字节.png

摆放方式改变时结果确变成了8字节:

8字节.png 却变成了8字节.png

由于这种特性,如果在网络编程或相关内存操作时如果不加以注意的话,就会造成隐秘而难以纠正的错误,请大家务必小心!

简书●null122转载请注明出处

相关文章

  • 内存对齐到底是怎么回事?

    内存对齐: 我们知道现代计算机体系中CPU按照双字、字、字节访问存储内存,并通过总线进行传输,若未经一定规则的对齐...

  • iOS底层 - 结构体内存对齐

    内存大小 在工作中,或多或少都会接触到内存对齐这个概念,而内存对齐到底是什么呢,今天来探索一下这个神秘的东西。话不...

  • 2.iOS底层学习之内存对齐

    学习了内存对齐之后的疑问?? 1.为啥要内存对齐?2.内存对齐的规则?3.内存对齐实例分析。 内存对齐的目的 上网...

  • 内存对齐

    本次主要讨论三个问题: 什么是内存对齐 内存对齐的好处 如何对齐 内存对齐 内存对齐是一种提高内存访问速度的策略。...

  • 结构体内存对齐

    对象内存对齐 探讨的问题 1.什么是内存对齐?2.为什么要做内存对齐?3.结构体内存对齐规则4.源码内存对齐算法 ...

  • 内存对齐

    内存对齐 什么叫内存对齐内存对齐就是按照特定的规则对数据进行存储,一般编译器按照8字节对齐标准处理。内存对齐一般用...

  • iOS内存对齐

    这篇文章我们来探索一下iOS内存对齐的原理,在探索完内存对齐原理之后,你就会明白内存对齐的好处。 在讲述内存对齐时...

  • iOS 开发 内存对齐(练习)

    目录 内存对齐规则 对齐系数 面试题演练 一、内存对齐规则 (关于面试题中结构体内存对齐计算总结) 1.1、数据成...

  • 内存对齐

    在C语言柔性数组一文中,提到了内存对齐,于是想写篇文章总结总结内存对齐。 内存对齐 为什么需要内存对齐 计算机系统...

  • iOS底层探究 - 内存对齐

    目录1:内存对齐的原因2:内存对齐的规则3:结构体内存分配演练以及在iOS中对象成员的内存分配探索 一 :内存对齐...

网友评论

  • 片片碎:感觉整体对齐还是不对呀。
    比如,n = 4,结构体如下:
    struct{
    double k;
    int a;
    short b;
    char c;
    int d;
    char buffer[3];
    } Feng;

    实际是:sizeof:24
    double: 0x10069a6a0
    int: 0x10069a6a8
    short: 0x10069a6ac
    char: 0x10069a6ae
    int: 0x10069a6b0
    char[3]: 0x10069a6b4

    按照你的计算,那么min = 4
    sizeof:总共为28
    double: 0x10069a6a0
    int: 0x10069a6a8
    short: 0x10069a6ac (这里需要补成4 就是需要补2个字节 )
    char: 0x10069a6ae (那么这里是 ac+4+1 = b0 ,然后在部3字节)
    int: 0x10069a6b0 (那么这里就是b4)
    char[3]: 0x10069a6b4 (往后推就是 b8)
    片片碎:@墨升空。 嗯嗯 后来弄懂了
    2049cd27dcb1:short: 0x10069a6ac (这里需要补成4 就是需要补2个字节 ) <-不用補
    数据成员对齐的规则就是,而在第一个成员之后,每个成员距离struct首地址的距离 offset, 都是struct内成员***自身长度***(sizeof) 与 #pragma pack(n)中的n的最小值的整数倍
  • ca15d01c0a07:还是不懂为什么调换顺序就会有不一样的结果。每个成员不是都占用固定大小的内存麽。?调转一下顺序,占用空间小的仍然需要补完啊。为什么会出现差别。?
    null122:@会飞噶pig 成员对齐后 4 4 2 1
    整体对齐 最小值为4 所以在后面补一
    文章里已经说的很清楚了,你再仔细看看
    ca15d01c0a07:@null122 那12字节的那个例子是不是有点问题。由8字节的例子可以看出n=1。那么12字节的例子的判定应该为char a占用1字节,补3字节(成员对齐);int b占用4字节;short c占用2字节;char d占用1字节,补5字节(整体对齐)。总共struct16字节。?
    null122:@会飞噶pig 仔细看下成员对齐你就懂了。
  • 蚕游世界:不错,这下弄懂了
  • 阿群1986:vc调试模式debug与release的字节对齐
  • ZJ_Lavender:一直都不知道结构体有这种隐秘,学习了
    null122:@ZJ_Lavender 希望你喜欢我的文章!

本文标题:内存对齐到底是怎么回事?

本文链接:https://www.haomeiwen.com/subject/aqsxdttx.html