выражение new [] не учитывает выравнивание в Microsoft VC ++ - PullRequest
6 голосов
/ 28 января 2011

Если выражение new [] используется для создания массива объектов, имеющих деструкторы, объекты в массиве могут быть неправильно выровнены

#include <stdint.h>
#include <stdio.h>

#pragma pack(8)
struct A{
  int64_t i;
  char dummy;
  ~A(){}
};

int main(){
  A* pa= new A[2];
  printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa);
}

(я строю 32-битную цель с VC ++ 2010 express)

Вывод (на моем компьютере):

 sizeof(A)= 16 pointer= 00344f4c

(sizeof (A) = 16 показывает, что компилятор понимает требования выравнивания для A и структура дополняется 7 байтами [отредактировано: __alignof (A) также возвращает 8] )

Я понимаю, почему это происходит: new [] необходимо хранить длину массива, и он использует для этой цели первые 4 байта выделенной памяти, затем он выделяет сам массив без надлежащего заполнения.

С практической точки зрения такое поведение определенно плохое, но соответствует ли он стандарту или нет?

Ответы [ 8 ]

8 голосов
/ 28 января 2011

Вы должны использовать __declspec для этой цели.Ваш код также генерировал смещенные объекты на моем компьютере (используя VS2010), но когда я изменил на __declspec(align(8)), указатели были правильно выровнены.

Я считаю, что pragma pack изменяет только размер структуры и не дает никаких гарантий относительно ее местоположения.

3 голосов
/ 28 января 2011

Стандарт гарантирует, что память выровнена правильно.

Я сомневаюсь, что Visual Studio что-то неправильно поняла.

В настоящее время я не понимаю, почему вы думаете, что она не выровнена?

Вы пытаетесь предложить, чтобы он был выровнен по 16-байтовым границам?
Это не требуется стандартом.Может потребоваться только выравнивание по 4-байтовым границам.

3.11 Выравнивание [basic.align]

2 Основное выравнивание представлено выравниванием, меньшим или равным наибольшему выравниваниюподдерживается реализацией во всех контекстах, что равно alignof (std :: max_align_t) (18.2).

Таким образом, следующий код должен распечатать максимально необходимое выравнивание реализации:

#include <iostream>
#include <cstddef>
#include <cstdalign>

int main()
{
        std::cout << alignof(std::max_align_t) << "\n";
}
2 голосов
/ 28 января 2011

Интересно.Мой первый вопрос: что происходит без пакета #pragma?Каково собственное выравнивание для этой структуры.Оператор new не имеет возможности узнать, какие параметры вы могли бы установить с помощью прагмы, поэтому его присутствие или отсутствие здесь не имеет никакого эффекта.И формально, нет никаких нарушений стандарта, так как Intel не требует какого-либо выравнивания для чего-либо (но поддержание правильного выравнивания, безусловно, помогает производительности).В целом, это не кажется желательной «функцией», но это проблема QoI, а не стандартного соответствия.

1 голос
/ 28 января 2011

Хорошо, чтобы ответить на ваш вопрос буквально, я не думаю, что стандарт вообще что-либо говорит о пакете #pragma.Поэтому я бы сказал, что ваш код имеет неопределенное поведение в соответствии со стандартом.

0 голосов
/ 28 января 2011

operator new(), какое ключевое слово new вызывает под капотом, не знает выравнивания типа.Единственное требование выравнивания состоит в том, что operator new() должен выделять память, соответствующим образом выровненную для встроенного типа с наибольшим требованием выравнивания, которое часто равно 8 на 32-битной платформе (требование выравнивания double).

* 1005Сказав это, пример, который вы опубликовали, распределяет память по 8-байтовой границе, как и должно быть.
0 голосов
/ 28 января 2011

Если struct A содержит только простые старые данные , вы можете использовать aligned_malloc (см. документацию Microsoft ). Если это необходимо построить, вы можете комбинировать это с оператором placement new. Примерно так (у меня нет Microsoft VC ++, поэтому он может не скомпилироваться):

void* buf = _aligned_malloc (2 * sizeof (struct A), 8) ;
A* pa = new (buf) A[2];

Удаление - это боль: вы должны явно вызывать pa [i]. ~ A () для каждого элемента массива, а затем _aligned_free (buf).

0 голосов
/ 28 января 2011

IIRC. По умолчанию используется выравнивание байтов, используйте / Zpn , чтобы установить другую упаковку, после чего вы получите заполнение.(или #pragma pack (n))

РЕДАКТИРОВАТЬ: подумайте, я думаю, что упаковка по умолчанию даже варьируется между версиями VS

0 голосов
/ 28 января 2011
 printf("sizeof(A)= %d, pointer= %08x", sizeof(A), pa);

В качестве предостережения используйте %p, если хотите напечатать адрес памяти :

printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa);

%x ожидает unsigned int, размер которого может отличаться от размера указателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...