如何进行C++对齐的数组分配?

Pee*_*oot 8 c++ arrays alignment

我想修改一个数组分配:

 float * a = new float[n] ;
Run Code Online (Sandbox Code Playgroud)

使用对齐的分配器。我倾向于尝试使用placement new 和posix_memalign(或新的c++11 等价物),但看到placement new with arrays 在数组分配方面存在问题,因为编译器可能需要为计数或其他元数据提供额外的存储空间

我试过:

int main()
{
   float * a = new alignas(16) float[3] ;

   a[2] = 0.0 ;

   return a[2] ;
}
Run Code Online (Sandbox Code Playgroud)

但编译器似乎表明 alignas 被忽略:

$ g++ -std=c++11 t.cc -Werror
t.cc: In function ‘int main()’:
t.cc:4:39: error: attribute ignored [-Werror=attributes]
    float * a = new alignas(16) float[3] ;
                                       ^
t.cc:4:39: note: an attribute that appertains to a type-specifier is ignored
Run Code Online (Sandbox Code Playgroud)

看起来使用 alignas 的正确方法是 结构声明中声明一个结构,但这仅适用于固定大小。

还有一个aligned_storage 模板,但我认为它也只适用于固定大小。

是否有任何标准方法可以进行对齐的数组分配,以在所有元素上调用构造函数?

Rev*_*lot 5

正如其他人所说,不需要支持过度对齐的类型。使用前请检查编译器文档。

您可以尝试使用以下方法之一来解决您的问题:

1)过度分配数组(通过(desired aligment / sizeof element) - 1)并使用std::alignlibstdc++实施链接。

2) 声明一个包含元素数组desired aligment / sizeof element并按所需对齐方式对齐的结构。如果您使用此类结构的数组,它应该在内存中为您提供紧凑的表示,但您将无法使用正常的数组表示法或指针算术(因为它(a)未定义的行为,(b)它们有很小的机会不是按照你想要的方式放置)

3) 编写自己的对齐分配函数。请注意,您可以添加自己版本的运算符newdelete

namespace my
{
    struct aligned_allocator_tag {};
    aligned_allocator_tag aligned;
}

void* operator new( std::size_t count, my::aligned_allocator_tag, std::size_t aligment);
void* operator new[]( std::size_t count, my::aligned_allocator_tag, std::size_t aligment)
{
    return ::operator new(count, my::aligned, aligment);
}
//Usage
foo* c = new(my::aligned, 16) foo[20];
Run Code Online (Sandbox Code Playgroud)

您将需要分配内存,保留足够的空间来存储原始指针(由 malloc/其他返回)或指针被移位的字节量,因此后续删除将释放正确的指针,将指针对齐到所需的大小并返回它。

这是一个答案还有另一个答案,它展示了如何对齐内存。

请注意,这两个答案都使用实现定义的行为,即对转换为整数的指针进行按位算术并将其转换回来。唯一真正完全标准的方法是将内存转换为char*并添加其值与下一个对齐地址之间的差异。

如果您可以使用一些非标准内存分配函数,您new也可以将它们包装到自定义运算符中。


Bar*_*rry 3

基本上,你被困住了,因为在 [expr.new] 中:

是否支持过度对齐类型是由实现定义的。

有一项提案可以更好地支持这一点。在那之前,如果您想做您想做的事情,aligned_alloc 则必须使用new.


如果将数组粘贴在结构中:

struct S {
    alignas(16) float _[3];
};
Run Code Online (Sandbox Code Playgroud)

thennew S将为您提供正确的对齐方式_,但不一定适合S其本身。这可能就足够了。如果没有,那么您可以重载operator new()andoperator delete()本身S以保证正确的行为。