自然对齐
自然对齐即各个类型自然对齐,各类型变量的内存地址必须是其类型本身的整数倍,结构体对齐到其中成员最长长度类型的整数倍。
对齐原因和意义
关于内存字节对齐的原因和意义,在网上的资料中大致是2种说法
1. 平台原因:不是所有的平台都能够访问任意地址上的数据的,某些硬件平台只能在某些地址出取某些特定类型的数据,否则将抛出异常。
2. 性能原因:简单来讲就是以空间换取时间。因为对于访问内存而言,访问一个对齐了的内存,只需要一个周期,而访问未对齐的内存,则需要两个周期。
内存对齐的方式
常见的内存对齐常见于结构体和类中。此文以结构体为例。
构体数据对齐,是指结构体内的各个数据对齐。在结构体中的第一个成员的首地址等于整个结构体的变量的首地址,而后的成员的地址随着它声明的顺序和实际占用的字节数递增。为了总的结构体大小对齐,会在结构体中插入一些没有实际意思的字符来填充(padding)结构体。
关于使用__align进行内存对齐的方式,在此不进行学习讨论,将在下次补上
在结构体中,成员数据对齐满足以下规则:
* 结构体中的第一个成员的首地址也即是结构体变量的首地址。
* 结构体中的每一个成员的首地址相对于结构体的首地址的偏移量(offset)是该成员数据类型大小的整数倍。
* 结构体的总大小是对齐模数(对齐模数等于#pragma pack(n)所指定的n与结构体中最大数据类型的成员大小的最小值)的整数倍。
下面为具体例子下结构体的对齐方式及其大小的计算。
下面的测试环境为win10 64位系统+GNU4.9.2
首先先分别测试在C++和C下各数据类型的大小,结果如下:
接下来是结构体的大小计算,结构体中变量声明的顺序有时会影响到结构体的大小.
首先是结构体中只包含内置类型的数据结构
struct A
{
int a;
double b;
short a;
};
struct B
{
int b;
short a;
double c;
};
然后在main函数中输出2个结构体的大小
cout << "sizeof(A) is " << sizeof(A) << endl;
cout << "sizeof(B) is " << sizeof(B) << endl;
输出结果分别是24和16
此时,我们再把short删除掉,或者说注释掉。
运行后得到的结果都为16.可以看出,结构体A的大小变了,结构体B的大小却还是一样的.为什么呢?
原因在于内存对齐中结构体的对齐规则结构体中的每一个成员的首地址相对于结构体的首地址的偏移量(offset)是该成员数据类型大小的整数倍。
该规则一开始可能比较难理解,我们先对刚才上述4个例子画图解释一下.
在一开始,结构体A有3个变量,他在内存中的存储方式如下,a占4个字节,b占8个字节,c占2个字节。
|aaaa - - - -|
|bbbbbbbb|
|cc - - - - - -|
而结构体B的存储方式与A不同,B的如下:此处abc与A中类型不同,请注意下.就不再专门修改.
|bbbbaa- -|
|cccccccc|
而在对short注释掉后,A和B的内存储存方式分别如下:
|aaaa - - - -|
|bbbbbbbb|
和
|bbbb - - - - |
|cccccccc|
接下来是结构体中包含数组的情况
struct A
{
int a;
double b;
char c[15];
};
结构体A的声明如上,此时sizeof(A) = 32;把c的大小改为17后sizeof(A) = 40;从上述结果,我们可以看出,包含数组时,只需把数组当成多个变量计算即可。
在结构体中只包含基本类型的讨论后,我们来测试下结构体嵌套的情况.
struct A
{
char a;
double b;
};
struct B
{
int b;
short a;
};
结构体A、B的声明如上。结构体C的声明依次如下:
struct C
{
char c;
A a;
};
struct C
{
B b;
char c;
};
struct C
{
B b;
char c;
A a;
};
上述三种情况,sizeof(C)的大小分别为24,12,32.
三种情况内存分配情况为
|c - - - - - - -|
|a - - - - - - -|
|bbbbbbbb|
|bbbb|
|aa - -|
|c - - -|
|bbbbaa - -|
|c - - - - - - -|
|a - - - - - - -|
|bbbbbbbb|
可以看到第三种情况时,结构体B的内存位置变为了一行(即大小为8),原因是结构体内存对齐要以其成员中的最大为准。即A中的double b.
以上!如有错误,欢迎指出交流