Есть ли какая-либо гарантия выравнивания адреса, возвращаемого новой операцией C ++? - PullRequest
27 голосов
/ 03 февраля 2009

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

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 C ++ Express 2008. Кажется, что все адреса, возвращаемые новой операцией, выровнены. Но я не уверен. Поэтому мой вопрос: есть ли гарантия? Если у них есть гарантия, я не должен совмещаться, если нет, я должен.

Ответы [ 5 ]

23 голосов
/ 03 февраля 2009

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

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

РЕДАКТИРОВАТЬ : Благодаря timday за выделение ошибки в gcc / glibc, где гарантия не действует.

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

14 голосов
/ 17 мая 2012

Это поздний ответ, но только для пояснения ситуации в 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
7 голосов
/ 03 февраля 2009

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

4 голосов
/ 03 февраля 2009

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

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

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

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

Tony

4 голосов
/ 03 февраля 2009

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

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

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