内存对齐

一、内存对齐的原因

  大部分的参考资料都是如是说的:

  1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。


 


 

二、内存对齐的规定:

 
  1.数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员自身大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐数。
 
   2.结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
 
  3.结构体成员相对首地址偏移量必须是成员大小的整数倍,也就是内存对齐。
 
  4.结构体总大小必须是对齐模数的整数倍。
 


 


 

三、计算

#include <stdlib.h>
#include <iostream>
using namespace std;

int main() {
	struct AA {
		char a;
		char b;
		char c;
	};

	cout << sizeof(AA) << endl;
	system("pause");
} 

  这个比较简单,可以根据上面的表格可以直接计算出来。

 


 

  来个有点难度的

#include <stdlib.h>
#include <iostream>
using namespace std;

int main() {
    struct AA {
        char a;
        int b;
        char c;
    };

    cout << sizeof(AA) << endl;
    system("pause");
}

  猜猜结果会是多少? 会是 5 吗??

  来详细了解一下:

 

  实际上会如图所示吗???

  当以如图的方式存放数据时,系统访问char a 时,只需读取一个字节,但当访问 int b 的时候,先要读取一个字节,在读取四个字节,才能读到int b的值。这样系统在访问数据时要先判断各个数据的字节大小,在进行读取,这样就会浪费时间。为了节省时间,会如下的方式去存储数据:

  在存储数据时,会对char a 补充 3 个字节,这样在访问 char a、int b 时,每次只需读取 4 个字节就可以,以一种空间换时间方式来提高效率。

  当如图存放数据之后,整个结构体的大小为 9 个字节。这与计算输出的结果不符(还记得,通过程序计算的大小为 12 个字节),这是为什么吗??

  还记得前面说的内存对齐的第四条规则吗??

4.结构体总大小必须是对齐模数的整数倍。

  在上面给出了一个表格,上面有常见数据类型的模数,那我们如何确定一个结构提的模数呢???

  其实结构体的模数等于其内部模数最大的成员类型的模数

 struct AA {
        char a;
        int b;
        char c;
    };

  对于结构体AA来说,他的模数就是 4。

  则该结构体的大小必须是 4 的整数倍,所以在最后还会补充 3 个字节(如下图)。

  这样结构体AA的大小就是 12 个字节。


  在添加点难度:
#include <stdlib.h>
#include <iostream>
using namespace std;

int main() {
    struct AA {
        char a;
        int b;
        char c;
    };

    struct BB {
        char d;
        int e;
        double f;
        AA g;
    };

    cout << sizeof(BB) << endl;
    system("pause");
}

  先来看看答案:

  来分析一下:

 

 

  到结构AA,来看看结构体AA的情况:

  所以接下来应该如下图:

  由图可知 结构体BB一共占据32个字节的内存,到这里就完了吗??

  还记得 内存对齐的第四条规则吗??

4.结构体总大小必须是对齐模数的整数倍。

  结构体BB的模数为多少呢??

  因为double e 的模数最大为 8 ,所以BB的模数也为8。32 是 8 的倍数,所以结构体BB只占 32 个字节。



四、自定义模数大小

  可以通过#pragma pack() 自定义模数的大小。

#include <stdlib.h>
#include <iostream>
using namespace std;

#pragma pack(1)

int main() {
    struct AA {
        char a;
        int b;
        char c;
    };

    struct BB {
        char d;
        int e;
        double f;
        AA g;
    };

    cout << sizeof(BB) << endl;
    system("pause");
}

  将模数的大小自定义为 1 后,运行结果如下:

  当模数大小定义为 2 时,运行结果如下:

  可以自己去试试画图理解。

posted @ 2017-07-19 14:05  青柯  阅读(157)  评论(0编辑  收藏  举报