Есть ли гарантия выравнивания возврата адреса с помощью новой операции С++?

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

for(size_t i = 0; i < 100; ++i) {
    char *p = new char[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    short *p = new short[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    float *p = new float[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
system("pause");

Компилятор, который я использую, - это Visual С++ Express 2008. Кажется, что все адреса, возвращаемые новой операцией, выровнены. Но я не уверен. Поэтому мой вопрос: есть ли какие-либо гарантии? Если у них есть гарантия, мне не нужно выравниваться, если нет, я должен.

Ответы

Ответ 1

Уравнение имеет следующую гарантию от стандарта (3.7.3.1/2):

Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель любого полного типа объекта, а затем используется для доступа к объекту или массиву в выделено хранилище (до хранилище явно освобождается вызовом соответствующей функции освобождения).

EDIT: timday для выделения bug в gcc/glibc, где гарантия не выполняется.

РЕДАКТИРОВАТЬ 2: комментарий Бена подчеркивает интересный случай края. Требования к процедурам распределения предназначены для тех, которые предусмотрены только стандартом. Если у приложения есть собственная версия, тогда нет никакой гарантии на результат.

Ответ 2

Это поздний ответ, но просто для выяснения ситуации в Linux - на 64-битных системах память всегда выравнивается по 16 байт:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

Адрес блока, возвращаемого malloc или realloc в системе GNU, всегда является  несколько из восьми (или шестнадцати на 64-битных системах).

Оператор new вызывает malloc внутренне (см. ./gcc/libstdc++-v3/libsupc++/new_op.cc) поэтому это относится и к new.

Реализация malloc, которая является частью glibc, в основном определяет MALLOC_ALIGNMENT - 2*sizeof(size_t) и size_t - 32 бит = 4 байт и 64 бит = 8 байт на системе x86-32 и x86-64, соответственно.

$ cat ./glibc-2.14/malloc/malloc.c:
...
#ifndef INTERNAL_SIZE_T
#define INTERNAL_SIZE_T size_t
#endif
...
#define SIZE_SZ                (sizeof(INTERNAL_SIZE_T))
...
#ifndef MALLOC_ALIGNMENT
#define MALLOC_ALIGNMENT       (2 * SIZE_SZ)
#endif

Ответ 3

Кстати, MS documentation упоминает что-то о malloc/новых возвращаемых адресах, которые выравниваются по 16 байт, но из экспериментов это не так. Мне понадобилось выравнивание по 16 байт для проекта (для ускорения копий памяти с расширенным набором инструкций), в конце концов я прибегал к написанию собственного распределителя...

Ответ 4

Оператор new/new [] будет возвращать указатели с достаточным выравниванием, чтобы он хорошо справлялся с базовыми типами данных (double, float и т.д.). По крайней мере, любой разумный компилятор С++ + runtime должен это сделать.

Если у вас есть особые требования к выравниванию, такие как SSE, то, вероятно, хорошая идея использовать специальные функции aligned_malloc или сворачивать ваши собственные.

Ответ 5

Я работал над системой, в которой они использовали выравнивание, чтобы освободить нечетный бит для собственного использования!

Они использовали нечетный бит для реализации системы виртуальной памяти.

Когда указатель имел нечетный бит, он использовал это, чтобы обозначить, что он указал (минус нечетное бит) к информации, чтобы получить данные из базы данных, а не сами данные.

Я подумал, что это особенно неприятный фрагмент кода, который был очень умным для его собственного блага!!

Тони