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

8

我想修改一个数组的分配:

 float * a = new float[n] ;

使用对齐分配器。我本来想尝试使用放置 new 和 posix_memalign(或新的 c++11 等效物),但看到 数组放置 new 存在问题,因为 编译器可能需要额外的存储空间来存储计数或其他元数据

我尝试了:

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

   a[2] = 0.0 ;

   return a[2] ;
}

但编译器似乎表明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

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

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

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

3个回答

5

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

您可以尝试使用以下方法解决问题:

1)过度分配您的数组(通过 (desired aligment / sizeof element) - 1),并使用std::align。链接到 libstdc++ implementation

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];

你需要分配内存,保留足够的空间来存储原始指针(由malloc/其他函数返回)或指针被替换的字节数,以便后续的删除将释放正确的指针,将指针对齐到所需大小并返回它。
这里有一个答案另一个答案,展示了如何对齐内存。
请注意,这两个答案都使用了实现定义行为,将指针转换为整数进行位运算,然后再将其转换回来。唯一真正完全标准的方法是将内存强制转换为char*,并添加其值与下一个对齐地址之间的差异。
如果您可以使用一些非标准的内存分配函数,您也可以将它们包装成自定义的new运算符。

3

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

超对齐类型是否被支持是由实现定义的。

有一个建议更好地支持它。在那之前,如果你想做你试图做的事情,你必须使用aligned_alloc代替new


如果将数组放入结构体中:
struct S {
    alignas(16) float _[3];
};

那么,new S 将为 _ 提供正确的对齐方式,但不一定适用于 S 本身。这可能已经足够了。如果不行,您可以在 S 上重载 operator new()operator delete() 来保证正确的行为。


aligned_alloc似乎是C11的构造。您如何测试以查看是否可用? - Peeter Joot

0

C++中本地支持对齐仍然不尽如人意。从外观上看,您正在将其与4个浮点向量对齐,因此您错过了C++14无法做到比16字节对齐更好的危险,但即使如此,这也不是所有C++14编译器可靠支持的功能。如果您需要使用new float[]的可移植代码,则一开始就输掉了比赛。

我认为,在当前标准下,您可以得到最接近的解决方案是创建一个矢量大小和矢量对齐的数据类型(例如使用std::aligned_storage),然后养成使用它的习惯,而不是使用单个浮点数组进行矢量数学运算。如果您需要可变长度的向量,则必须将其舍入到最近的4个浮点数。

(抱歉,这不是您想要的答案,但我认为这是您前进所需的答案。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接