内存对齐是一个数据类型所能存放的内存地址的属性。这个属性是一个无符号整数,并且这个整数必须是2的N次方(1、2、4、8、、、1024、、、)。
例如当我们说一个数据类型的内存对齐为8时,是指这个数据类型所定义出来的所有变量的内存地址都是8的倍数。
当一个基本数据类型的对齐属性和这个数据类型的大小相等时,称为自然对齐。例如一个4字节大小的int型数据,默认情况下字节对齐是4。
1) 并不是每一个硬件平台都能随便访问任意位置的内存的。某些平台(例如Alpha等)若读取的数据未内存对齐,将拒绝访问或抛出异常。
2)将一个int放在奇数内存(例如1)位置上,如果想把4个字节读出来,32位CPU需要读取两次。如下例子所示:
如果按照4字节对齐后,一次就可以读取出来了,提升了效率。
#include
using namespace std;
struct
{
int i;
char c1;
char c2;
}Test1;
struct {
char c1;
int i;
char c2;
}Test2;
struct {
char c1;
char c2;
int i;
}Test3;
int main()
{
cout << sizeof(Test1) << endl;
cout << sizeof(Test2) << endl;
cout << sizeof(Test3) << endl;
system("pause");
return 0;
}
执行结果:
8
12
8
本例测试环境为vs2015,默认4字节对齐,分析如下所示:
malloc一般使用当前平台默认的最大内存对齐数对齐内存,比如,MSVC在32位下一般为8字节对齐,64位下则是16字节对齐。如果我们自定义的内存对齐超出了这个范围,则不能直接使用malloc获取内存。
但当我们有需求需要分配一块具有特定内存对齐的内存块时,在MSVC下应使用_aligned_malloc,gcc下一般使用memalogn等函数。
实现一个简易的aligned_malloc:
inline void* aligned_malloc(size_t size, size_t alignment)
{
//检查alignment是否为2^N
assert(!(alignment & (alignment - 1)));
//计算出最大的offset,sizeof(void*)是为了存储原始指针地址
size_t offset = sizeof(void*) + (--alignment);
//分配一块带offset的内存
char *p = static_cast(malloc(offset + size));
if (!p)
return nullptr;
//通过"& (~alignment)"把多计算的offset减掉
void* r = reinterpret_cast(reinterpret_cast(p + offset) & (~alignment));
//将r当作一个指向void*的指针,在r当前地址前面放入原始地址
static_cast(r)[-1] = p;
//返回经过对齐的内存地址
return r;
}
inline void align_free(void* p)
{
//还原回原始地址,并free
free(static_cast(p)[-1]);
}
例如:
alignas(32) long long a = 0;
#define XX 1
struct alignas(XX)MyStruct {};
注意:alignas只能改大不能改小,如果需要改小,需要使用#prama pack(n),n为对齐字节数。
c++11中与#prama等价的为_Prama(微软暂不支持,vs中暂不能使用);
_Pragma("pack(1)")
struct MyStruct
{
char a;
int b;
short c;
long d;
};
_Pragma("pack()")
struct MyStruct
{
char c;
int a;
double b;
};
int main()
{
//获取内存对齐的大小
cout << alignof(MyStruct) << endl; //8
int alignSize = std::alignment_of::value;
cout << alignSize << endl; //8
system("pause");
return 0;
}
1)std::max_align_t用来返回当前平台的最大默认内存对齐类型。
cout << alignof(std::max_align_t) << endl;
2)std::align用来在一大块内存当中获取一个符合指定内存要求的地址。
std::align的原型为:
void* align( std::size_t alignment,
std::size_t size,
void*& ptr,
std::size_t& space );
char buf[] = "-----------------";
void* p = buf;
std::size_t space = sizeof(buf) - 1;
std::align(alignof(int), sizeof(char), p, space);
在buf这个大内存块中,指定内存对齐为slignof(int),找一块sizeof(char)大小的内存,并且在找到这块内存后将地址放入p,将buf从p开始的长度当入space。