关于C++内存对齐
关于C++内存对齐
C++11从标准层面引入了一些和内存对齐相关的特性,标准库也增加了对应的组件,这里稍微总结一下。
取得某个类型的对齐值
C++中的Object(对象)是指一块满足以下条件的内存区域:
- 具有size属性,即能用
sizeof
取其大小。 - 具有alignment属性,即能用
alignof
取其对齐量。这个在稍后有详细的阐述。 - 类型
- ……(lifetime等)
现在已经知道了每个对象都有其类型,这样的类型称为“对象类型”(object type)。每个对象类型都有一个alignment值,该值的类型为std::size_t
,且总为2的整数次幂。我们可以通过关键字alignof
或标准库中的std::alignment_of
(声明位于头文件<type_traits>
)获知此值,比如:
struct S { char a; double b }
int main(void)
{
cout << alignof(S) << endl;
cout << alignment_of<int>::value << endl;
}
C++17引入了std::alignment_of_v
,允许这样的写法:
int main(void)
{
cout << alignment_of_v<double> << endl;
}
当然,只是语法糖而已。
指定类型的对齐规则
C++11引入了关键字alignas
用于修饰类型,它有以下三种使用形式:
alignas(expr)
,这里expr
必须是一个编译期常量表达式,且应为2的整数次幂。alignas(Type)
,这个写法等价于alignas(alignof(Type))
。alignas(pack...)
,这里pack是个模板参数包,相当于对参数包中的每个元素P,同时以alignas(P)
来修饰后面的类型。
下面看例子:
struct alignas(16) MyStruct
{
char ch;
};
int main(void)
{
MyStruct instanceOfMyStruct; //instanceOfMyStruct按16字节对齐
alignas(127 + 1) char charArray[128]; //charArray按128字节对齐
alignas(int) char ch; //ch按alignof(int)字节对齐
//...
}
堆上对象的内存对齐
我们已经能够指定某个类型的对齐规则了,但对于堆上申请下来的对象还缺乏控制。我们当然可以基于malloc自己写一个,不过已经有下面的几种方案:
operator new
允许通过一个std::align_val_t
类型的参数来显示给出对齐要求,然而这是C++17才引入的,暂时没法广泛使用。- 二段式构造。先调用
_aligned_malloc
(Windows)或者posix_memalign
(Posix)拿到一块内存,然后用placement new
构造对象。
其他
C++11引入的std::aligned_storage
(头文件<type_traits>
中)用于获得一块按指定规则对齐的内存块。它相当于:
template<std::size_t Len, std::size_t Align>
struct aligned_storage
{
struct type
{
alignas(Align) unsigned char data[Len];
};
};
C++11引入的std::max_align_t
:“std::max_align_t
is a trivial type whose alignment requirement is at least as strict (as large) as that of every scalar type”,也就是任何一个scalar type的默认对齐大小都不会超过alignof(max_align_t)
。