使用贴片新操作符时,我真的不得不担心对齐吗?

时间:2012-08-02 16:19:10

标签: c++ g++ memory-alignment powerpc placement-new

我读了这个When should I worry about alignment?但是我仍然不知道是否我不得不担心放置新运算符返回的对齐指针 - 就像在这个例子中一样:

class A {
public:
   long double a;
   long long b;
   A() : a(1.3), b(1234) {}
};

char buffer[64];

int main() {
   // (buffer + 1) used intentionally to have wrong alignment
   A* a = new (buffer + 1) A(); 
   a->~A();
}

__alignof(A) == 4(buffer + 1)未与4对齐。但一切正常 - 这里有完整的例子:http://ideone.com/jBrk8

如果这取决于架构,那么我正在使用:linux / powerpc / g ++ 4.x.x。

[更新]发布此问题后,我读了这篇文章:http://virtrev.blogspot.de/2010/09/memory-alignment-theory-and-c-examples.html。 也许在我的情况下唯一的缺点是性能损失,我的意思是未对齐的访问成本超过对齐?

5 个答案:

答案 0 :(得分:19)

当你在缓冲区上调用new时:

A *a = new (buf) A;

您正在调用内置void* operator new (std::size_t size, void* ptr) noexcept,如下所示:

  

     

18.6.1.3安置表格[new.delete.placement]

     

这些函数是保留的,C ++程序可能不会定义替换版本中的版本的函数   标准C ++库(17.6.4)。 (3.7.4)的规定不适用于这些保留的安置形式   operator new和operator delete。

    void* operator new(std::size_t size, void* ptr) noexcept;
  退货:ptr
  备注:故意不执行任何其他操作。

(3.7.4)的条款包括返回的指针应该适当对齐,因此void* operator new (std::size_t size, void* ptr) noexcept返回非对齐指针(如果传入的话)是很好的。这不是但是,让摆脱困境:

  

5.3.4新[expr.new]

     

[14]注意:当分配函数返回null以外的值时,它必须是指向存储块的指针   其中保留了对象的空间。假设存储块被适当地对齐   和所要求的大小。

因此,如果您将未对齐的存储传递给placement-new表达式,则违反了存储已对齐的假设,结果为UB。


的确,在上面的程序中,如果您将long long b替换为__m128 b(在#include <xmmintrin.h>之后),则程序将按预期进行分段。

答案 1 :(得分:10)

仅仅因为一切似乎都有效并不意味着实际

C ++是一个规范,用于定义必需的工作原理。编译器也可以使不需要的东西工作。这就是“未定义的行为”的含义:任何事情都可能发生,因此您的代码不再可移植。

C ++不要求这个工作。因此,如果你将代码带到一个不起作用的编译器,你就不能再责怪C ++了;误用语言是你的错。

答案 2 :(得分:4)

是的,这一切都取决于架构,也可能是编译器优化标志。 一切正常,直到你A b = a;或其他一些随机访问被编译到某些movdqa操作并且程序崩溃。

答案 3 :(得分:4)

某些处理器肯定会爆炸(例如sparc),其他处理器根本不关心,其他处理器会很慢。 C ++假设您知道自己在做什么(或不知道),与reinterpret_cast等相同。

答案 4 :(得分:3)

快速google告诉我,powerpc上的gcc可以选择告诉编译器系统是否对系统进行了未对齐访问。

我认为在任何一种情况下,该平台上的未对齐访问都会非常慢,应该尽可能避免。