Подтвердить что ты не робот

Должен ли я действительно беспокоиться о выравнивании при использовании нового оператора размещения?

Я читал это Когда мне нужно беспокоиться о выравнивании?, но я все еще не знаю, нужно ли мне беспокоиться о не выровненном указателе, возвращаемом при размещении нового оператора - например в этом примере:

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. Может быть, единственными недостатками в моем случае были бы штраф за производительность, я имею в виду невысокую стоимость доступа больше, чем выровнен?

4b9b3361

Ответ 1

Когда вы вызываете новое размещение в буфере:

A *a = new (buf) A;

вы вызываете встроенный void* operator new (std::size_t size, void* ptr) noexcept, как определено в:

18.6.1.3 Формы размещения [new.delete.placement]

Эти функции зарезервированы, программа на С++ не может определять функции, которые заменяют версии в Стандартная библиотека С++ (17.6.4). Положения (3.7.4) не применяются к этим зарезервированным формам размещения оператор new и оператор 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 (после #include <xmmintrin.h>), то программа будет segfault, как и ожидалось.

Ответ 2

Просто потому, что все работает, это не значит, что это действительно так.

С++ - это спецификация, которая определяет, что требуется для работы. Компилятор также может заставить не работать. Что означает "undefined поведение": все может случиться, поэтому ваш код больше не переносится.

С++ не требует, чтобы это работало. Поэтому, если вы передадите свой код компилятору, где это не работает, вы больше не можете обвинять С++; это ваша вина за неправильное использование языка.

Ответ 3

Да, все зависит от архитектуры и, возможно, от флагов оптимизации компилятора. Все будет нормально работать до тех пор, пока вы не выполните A b = a; или какой-либо другой случайный доступ, который скомпилируется с помощью movdqa и ваша программа выйдет из строя.

Ответ 4

некоторые процессоры, безусловно, взорвут это (например, sparc), другим это не понравится, другие будут медленными. С++ предполагает, что вы знаете, что делаете (или нет), как и reinterpret_cast и т.д.

Ответ 5

быстрый google сообщает мне, что gcc on powerpc имеет возможность сообщать компилятору, если система без права доступа не привязана к системе.

Я бы предположил, что в любом случае неприглаженные обращения на этой платформе будут очень медленными, и их следует избегать как можно больше.