我读了这个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。 也许在我的情况下唯一的缺点是性能损失,我的意思是未对齐的访问成本超过对齐?
答案 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)
答案 4 :(得分:3)
我认为在任何一种情况下,该平台上的未对齐访问都会非常慢,应该尽可能避免。