26

我读了这篇文章 我什么时候应该担心对齐?但我仍然不知道我是否必须担心放置 new 运算符返回的未对齐指针 - 就像在这个例子中一样:

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.xx

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

4

5 回答 5

23

当您在缓冲区上调用placement 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] 注意:当分配函数返回一个非空值时,它必须是一个指向已为对象保留空间的存储块的指针。假定存储块已适当对齐并具有请求的大小。

因此,如果您将未对齐的存储传递给放置新表达式,则您违反了存储已对齐的假设,结果是 UB。


事实上,在你上面的程序中,如果你long long b__m128 b(after #include <xmmintrin.h>) 替换,那么程序会出现段错误,正如预期的那样。

于 2012-08-02T16:57:47.350 回答
11

仅仅因为一切看起来都有效,并不意味着它确实有效

C++ 是一种规范,它定义了工作所需的内容。编译器还可以使不需要的东西工作。这就是“未定义行为”的含义:任何事情都可能发生,因此您的代码不再可移植。

C++ 不需要这样做。所以,如果你把你的代码带到一个编译器上,但编译器还是不行,你就不能再责怪C++了。滥用语言是你的错。

于 2012-08-02T16:22:43.770 回答
4

是的,这完全取决于架构,可能还取决于编译器优化标志。A b = a;在您这样做或其他一些随机访问被编译为某些movdqa操作并且您的程序崩溃之前,它都会正常工作。

于 2012-08-02T16:22:58.647 回答
4

某些处理器肯定会因此而崩溃(例如 sparc),其他处理器则根本不在乎,其他处理器会很慢。C++ 假设您知道自己在做什么(或不知道),与 reinterpret_cast 等相同。

于 2012-08-02T16:26:20.957 回答
3

一个快速的 google 告诉我 powerpc 上的 gcc 有一个选项来告诉编译器是否由系统处理未对齐的访问。

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

于 2012-08-02T16:26:22.347 回答