Как выделить выровненную память только с использованием стандартной библиотеки?
Я только что закончил тест как часть собеседования, и один вопрос поставил меня в тупик - даже используя Google для справки. Я хотел бы посмотреть, что команда стекпотока может сделать с ним:
Функция "memset_16aligned" требует, чтобы ей был передан выровненный указатель размером 16 байт, иначе произойдет сбой.
а) Как бы вы разместили 1024 байта памяти и выровняли ее по 16-байтовой границе?
б) Освободите память после выполнения memset_16aligned.
{
void *mem;
void *ptr;
// answer a) here
memset_16aligned(ptr, 0, 1024);
// answer b) here
}
17 ответов
Оригинальный ответ
{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
Фиксированный ответ
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
Объяснение по запросу
Первый шаг - выделить достаточно свободного места на всякий случай. Поскольку память должна быть выровнена на 16 байтов (это означает, что адрес начального байта должен быть кратным 16), добавление 16 дополнительных байтов гарантирует, что у нас будет достаточно места. Где-то в первых 16 байтах есть 16-байтовый выровненный указатель. (Обратите внимание, что malloc()
должен возвращать указатель, который достаточно хорошо выровнен для любой цели. Тем не менее, значение "любой" в первую очередь для таких вещей, как базовые типы - long
, double
, long double
, long long
и указатели на объекты и указатели на функции. Когда вы делаете более специализированные вещи, например, играете с графическими системами, им может потребоваться более строгое выравнивание, чем остальной системе - отсюда и такие вопросы и ответы.)
Следующим шагом является преобразование пустого указателя в указатель на символ; GCC, несмотря на это, вы не должны выполнять арифметику указателей на пустых указателях (и GCC имеет опции предупреждения, чтобы сообщить вам, когда вы злоупотребляете им). Затем добавьте 16 к стартовому указателю. предполагать malloc()
вернул вам невозможно плохо выровненный указатель: 0x800001. Добавление 16 дает 0x800011. Теперь я хочу округлить до 16-байтовой границы - поэтому я хочу сбросить последние 4 бита до 0. 0x0F имеет последние 4 бита, равные единице; следовательно, ~0x0F
все биты установлены в один, кроме последних четырех. Anding, что с 0x800011 дает 0x800010. Вы можете перебрать другие смещения и увидеть, что работает та же арифметика.
Последний шаг, free()
, легко: вы всегда и только возвращаетесь к free()
значение, которое один из malloc()
, calloc()
или же realloc()
вернулся к вам - все остальное является катастрофой. Вы правильно предоставили mem
держать это значение - спасибо. Бесплатные релизы.
Наконец, если вы знаете о внутренностях вашей системы malloc
пакет, вы можете догадаться, что он вполне может вернуть 16-байтовые данные (или 8-байтовые). Если бы он был выровнен по 16 байтам, вам не пришлось бы копаться со значениями. Тем не менее, это хитро и непереносимо - другое malloc
пакеты имеют различное минимальное выравнивание, и поэтому одно допущение, когда он делает что-то другое, приведет к дампам ядра. В широких пределах это решение является переносимым.
Кто-то еще упомянул posix_memalign()
как еще один способ получить выровненную память; это не доступно везде, но часто может быть реализовано с использованием этого в качестве основы. Обратите внимание, что было удобно, чтобы выравнивание было степенью 2; другие расстановки сложнее.
Еще один комментарий - этот код не проверяет, что распределение прошло успешно.
поправка
Windows programmer отметил, что вы не можете выполнять операции с битовой маской для указателей, и, действительно, GCC (протестированные 3.4.6 и 4.3.1) действительно жалуется на это. Итак, исправленная версия основного кода - преобразованная в основную программу, следует. Я также позволил себе добавить только 15 вместо 16, как было указано. я использую uintptr_t
с тех пор C99 существует достаточно долго, чтобы быть доступным на большинстве платформ. Если бы не было использования PRIXPTR
в printf()
заявления, было бы достаточно #include <stdint.h>
Вместо того, чтобы использовать #include <inttypes.h>
, [Этот код включает исправление, указанное Siyuan Ren, который повторял точку зрения, впервые высказанную Bill K несколько лет назад, которую мне удалось пропустить до сих пор.]
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
int main(void)
{
void *mem = malloc(1024+15);
void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
return(0);
}
И вот немного более обобщенная версия, которая будет работать для размеров, которые имеют степень 2:
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
static void test_mask(size_t align)
{
uintptr_t mask = ~(uintptr_t)(align - 1);
void *mem = malloc(1024+align-1);
void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
assert((align & (align - 1)) == 0);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
}
int main(void)
{
test_mask(16);
test_mask(32);
test_mask(64);
test_mask(128);
return(0);
}
Преобразовать test_mask()
в функцию распределения общего назначения, единственное возвращаемое значение от распределителя должно было бы закодировать адрес освобождения, как указали несколько человек в своих ответах.
Проблемы с интервьюерами
Uri прокомментировал: "Может быть, у меня сегодня утром проблема с пониманием прочитанного, но если вопрос об интервью конкретно говорит:" Как бы вы распределили 1024 байта памяти ", а вы явно выделяете больше, чем это? Не будет ли это автоматическим отказом интервьюера?
Мой ответ не помещается в комментарий из 300 символов...
Это зависит, я полагаю. Я думаю, что большинство людей (включая меня) восприняли вопрос так: "Как бы вы распределили пространство, в котором можно хранить 1024 байта данных, и где базовый адрес кратен 16 байтам". Если интервьюер действительно имел в виду, как вы можете выделить 1024 байта (только) и выровнять их по 16 байтов, то варианты более ограничены.
- Ясно, что одна возможность состоит в том, чтобы выделить 1024 байта и затем дать этому адресу "обработку выравнивания"; проблема с этим подходом состоит в том, что фактическое доступное пространство не является должным образом определенным (используемое пространство составляет от 1008 до 1024 байтов, но не было механизма, позволяющего указать, какой размер), что делает его менее полезным.
- Другая возможность состоит в том, что вы должны написать полный распределитель памяти и убедиться, что 1024-байтовый блок, который вы возвращаете, соответствующим образом выровнен. Если это так, вы, вероятно, в конечном итоге выполните операцию, аналогичную той, которая была предложена, но вы скрываете ее в распределителе.
Однако, если бы интервьюер ожидал какого-либо из этих ответов, я бы ожидал, что они признают, что это решение отвечает на тесно связанный вопрос, а затем пересмотрят свой вопрос, чтобы направить разговор в правильном направлении. (Кроме того, если интервьюер стал действительно неуклюжим, я бы не хотел работать; если ответ на недостаточно точное требование сгорел без исправления, тогда интервьюер - это не тот, для кого безопасно работать.)
Мир движется дальше
Название вопроса недавно изменилось. Это было Решить выравнивание памяти в вопросе C интервью, которое озадачило меня. Пересмотренный заголовок (Как распределить память только с помощью стандартной библиотеки?) Требует слегка пересмотренного ответа - это дополнение дает его.
C11 (ISO / IEC 9899: 2011) добавлена функция aligned_alloc()
:
7.22.3.1
aligned_alloc
функцияконспект
#include <stdlib.h> void *aligned_alloc(size_t alignment, size_t size);
Описание
aligned_alloc
Функция выделяет место для объекта, выравнивание которого определяетсяalignment
чей размер указанsize
и чье значение не определено. Значениеalignment
должно быть действительным выравниванием, поддерживаемым реализацией и значениемsize
должен быть целым кратнымalignment
,Возвращает
aligned_alloc
Функция возвращает либо нулевой указатель, либо указатель на выделенное пространство.
И POSIX определяетposix_memalign()
:
#include <stdlib.h> int posix_memalign(void **memptr, size_t alignment, size_t size);
ОПИСАНИЕ
posix_memalign()
функция должна выделитьsize
байты выровнены по границе, указаннойalignment
и должен вернуть указатель на выделенную память вmemptr
, Значениеalignment
должно быть степень двух кратныхsizeof(void *)
,После успешного завершения значение, указанное
memptr
должен быть кратнымalignment
,Если размер запрошенного пространства равен 0, поведение определяется реализацией; значение, возвращаемое в
memptr
должен быть либо нулевым указателем, либо уникальным указателем.
free()
функция освобождает память, которая была ранее выделенаposix_memalign()
,ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
После успешного завершения,
posix_memalign()
должен вернуть ноль; в противном случае возвращается номер ошибки, чтобы указать на ошибку.
Любой или оба из них могут быть использованы для ответа на вопрос сейчас, но только функция POSIX была опцией, когда на вопрос был первоначально дан ответ.
За кулисами новая выровненная функция памяти выполняет почти ту же работу, что и описанную в вопросе, за исключением того, что она позволяет более легко форсировать выравнивание и отслеживать внутреннее начало выровненной памяти, чтобы код не приходится иметь дело со специально - он просто освобождает память, возвращаемую функцией выделения, которая использовалась.
Три несколько разных ответа в зависимости от того, как вы смотрите на вопрос:
1) Достаточно хорошо для точного заданного вопроса является решение Джонатана Леффлера, за исключением того, что для округления до 16 выровнено, вам нужно только 15 дополнительных байтов, а не 16.
A:
/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;
B:
free(mem);
2) Для более общей функции выделения памяти вызывающая сторона не хочет отслеживать два указателя (один для использования и один для освобождения). Таким образом, вы сохраняете указатель на "настоящий" буфер под выровненным буфером.
A:
void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;
B:
if (ptr) free(((void**)ptr)[-1]);
Обратите внимание, что в отличие от (1), где в mem было добавлено только 15 байтов, этот код может фактически уменьшить выравнивание, если ваша реализация гарантирует 32-байтовое выравнивание из malloc (маловероятно, но в теории реализация C может иметь 32 байта). выровненный тип). Это не имеет значения, если все, что вы делаете, это вызываете memset_16aligned, но если вы используете память для структуры, это может иметь значение.
Я не уверен, насколько хорошо это исправить (кроме предупреждения пользователя о том, что возвращаемый буфер не обязательно подходит для произвольных структур), поскольку нет никакого способа программно определить, какова гарантия выравнивания для конкретной реализации. Я предполагаю, что при запуске вы могли бы выделить два или более 1-байтовых буфера и предположить, что худшее выравнивание, которое вы видите, - это гарантированное выравнивание. Если вы ошибаетесь, вы тратите впустую память. Любой, у кого есть идея получше, скажите, пожалуйста...
[ Добавлено: "Стандартный" трюк заключается в создании объединения "максимально выровненных типов" для определения необходимого выравнивания. Максимально выровненные типы, вероятно, будут (в C99) ' long long
',' long double
',' void *
', или же ' void (*)(void)
"; если вы включите <stdint.h>
Вы могли бы предположительно использовать intmax_t
' на месте long long
(и на машинах Power 6 (AIX), intmax_t
даст вам 128-битный целочисленный тип). Требования выравнивания для этого объединения можно определить, внедрив его в структуру с одним символом, за которым следует объединение:
struct alignment
{
char c;
union
{
intmax_t imax;
long double ldbl;
void *vptr;
void (*fptr)(void);
} u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;
Затем вы будете использовать большее из запрошенного выравнивания (в примере, 16) и align
Значение рассчитано выше.
На (64-битной) Solaris 10 кажется, что основное выравнивание для результата из malloc()
кратно 32 байтам.
]
На практике выровненные распределители часто принимают параметр для выравнивания, а не для его аппаратного соединения. Таким образом, пользователь передаст размер структуры, которая ему небезразлична (или наименьшая степень 2 больше или равна этой величине), и все будет хорошо.
3) Используйте то, что обеспечивает ваша платформа: posix_memalign
для POSIX, _aligned_malloc
на винде.
4) Если вы используете C11, то самый чистый - портативный и лаконичный вариант - использовать стандартную библиотечную функцию aligned_alloc
это было введено в этой версии спецификации языка.
Вы также можете попробовать posix_memalign()
(на платформах POSIX, конечно).
Вот альтернативный подход к части "округления вверх". Не самое блестяще закодированное решение, но оно выполняет свою работу, и этот тип синтаксиса немного легче запомнить (плюс будет работать для значений выравнивания, которые не являются степенью 2). uintptr_t
приведение было необходимо, чтобы успокоить компилятор; арифметика указателей не очень любит деление или умножение.
void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
К сожалению, в C99 кажется довольно сложно гарантировать какое-либо выравнивание таким образом, чтобы его можно было переносить на любую реализацию C, соответствующую C99. Зачем? Поскольку указатель не гарантированно является "байтовым адресом", который можно представить с помощью плоской модели памяти. Также не гарантировано представление uintptr_t, которое в любом случае является необязательным типом.
Мы могли бы знать о некоторых реализациях, которые используют представление для void * (и по определению также char *), который является простым байтовым адресом, но в C99 он непрозрачен для нас, программистов. Реализация может представлять указатель с помощью набора {сегмент, смещение}, где смещение может иметь выравнивание "кто знает, что" в реальности. Да, указатель может даже быть некоторой формой значения поиска в хеш-таблице или даже значением поиска в связанном списке. Это может кодировать информацию о границах.
В недавнем черновике C1X для стандарта C мы видим ключевое слово _Alignas. Это может помочь немного.
Единственная гарантия, которую дает нам C99, состоит в том, что функции выделения памяти будут возвращать указатель, подходящий для назначения указателю, указывающему на любой тип объекта. Поскольку мы не можем указать выравнивание объектов, мы не можем реализовать наши собственные функции выделения с ответственностью за выравнивание четко определенным, переносимым способом.
Было бы хорошо ошибиться в этом утверждении.
На фронте заполнения счетчика 16 по 15 байтов фактическое число, которое нужно добавить, чтобы получить выравнивание N, равно max(0,NM), где M - естественное выравнивание распределителя памяти (и оба - степени 2).
Так как минимальное выравнивание памяти любого распределителя составляет 1 байт, 15=max(0,16-1) является консервативным ответом. Однако, если вы знаете, что ваш распределитель памяти будет выдавать вам 32-битные адреса, выровненные по int (что довольно часто), вы могли бы использовать 12 в качестве пэда.
Это не важно для этого примера, но это может быть важно для встроенной системы с 12 КБ ОЗУ, где каждый сохраненный int имеет значение.
Лучший способ реализовать это, если вы на самом деле попытаетесь сохранить каждый возможный байт, - это использовать макрос как исходное выравнивание памяти. Опять же, это, вероятно, полезно только для встроенных систем, где вам нужно сохранять каждый байт.
В приведенном ниже примере в большинстве систем значение 1 вполне подходит для MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
однако для нашей теоретической встроенной системы с 32-разрядным выравниванием выделение может сэкономить немного драгоценной памяти:
#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT 4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
Возможно они были бы удовлетворены знанием memalign? И, как отмечает Джонатан Леффлер, есть две новые предпочтительные функции, о которых нужно знать.
Ой, Флорин победил меня в этом. Однако, если вы прочитаете справочную страницу, на которую я ссылаюсь, вы, скорее всего, поймете пример, предоставленный более ранним постером.
Я удивлен, что никто не проголосовал за ответ Shao, что, насколько я понимаю, невозможно выполнить то, что просят в стандартном C99, поскольку формальное преобразование указателя на интегральный тип является неопределенным поведением. (Помимо стандарта, разрешающего конвертацию uintptr_t
<-> void*
но стандарт, похоже, не позволяет делать какие-либо манипуляции с uintptr_t
значение, а затем преобразовать его обратно.)
Мы делаем такие вещи постоянно для Accelerate.framework, сильно векторизованной библиотеки OS X / iOS, где мы должны постоянно уделять внимание выравниванию. Есть довольно много вариантов, один или два из которых я не видел выше.
Самый быстрый метод для такого маленького массива - просто положить его в стек. С GCC / Clang:
void my_func( void )
{
uint8_t array[1024] __attribute__ ((aligned(16)));
...
}
Бесплатно () не требуется. Обычно это две инструкции: вычтите 1024 из указателя стека, затем И указатель стека с -alignment. Предположительно, запрашивающему потребовались данные в куче, потому что его срок службы массива превысил стек, или рекурсия работает, или пространство стека стоит серьезной премии.
В OS X / iOS все вызовы malloc/calloc/ и т. Д. всегда выровнены по 16 байтов. Например, если вам нужно выровнять 32 байта для AVX, вы можете использовать posix_memalign:
void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
RunInCirclesWaivingArmsWildly();
...
free(buf);
Некоторые люди упоминали интерфейс C++, который работает аналогично.
Не следует забывать, что страницы выровнены с большой степенью двойки, поэтому выровненные по размеру буферы также выровнены по 16 байтов. Таким образом, mmap() и valloc() и другие подобные интерфейсы также являются опциями. Преимущество mmap () в том, что буфер может быть выделен предварительно инициализированным с чем-то ненулевым, если хотите. Так как у них размер страницы выровнен, вы не получите от них минимальное выделение, и он, вероятно, будет подвержен сбоям виртуальной машины при первом касании.
Сырный: Включите охрану malloc или аналогичный. Буферы размером n*16 байт, такие как этот, будут выровнены n*16 байт, потому что VM используется для перехвата переполнения, а ее границы находятся на границах страницы.
Некоторые функции Accelerate.framework используют предоставленный пользователем временный буфер для использования в качестве рабочего пространства. Здесь мы должны предположить, что переданный нам буфер сильно смещен, и пользователь активно пытается усложнить нашу жизнь. (Наши тестовые сценарии прикрепляют защитную страницу прямо перед и после временного буфера, чтобы подчеркнуть злобу.) Здесь мы возвращаем минимальный размер, который нам нужен, чтобы гарантировать 16-байтовый выровненный сегмент где-то в нем, а затем вручную выравниваем буфер после. Этот размер - требуемый_размер + выравнивание - 1. Итак, в этом случае это 1024 + 16 - 1 = 1039 байт. Затем выровняйте так:
#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
uint8_t *alignedBuf = (uint8_t*)
(((uintptr_t) tempBuf + ((uintptr_t)alignment-1))
& -((uintptr_t) alignment));
...
}
Добавление alignment - 1 переместит указатель за первый выровненный адрес, а затем ANDing с -alignment (например, 0xfff...ff0 для alignment=16) вернет его на выровненный адрес.
Как описано в других статьях, в других операционных системах без 16-байтовых гарантий выравнивания вы можете вызывать malloc с большим размером, позже выделить указатель free(), затем выполнить выравнивание, как описано выше, и использовать выровненный указатель, так же как описано для нашего временного буфера.
Что касается align_memset, это довольно глупо. Вам нужно только зациклить до 15 байтов, чтобы достичь выровненного адреса, а затем продолжить с выровненными хранилищами с некоторым возможным кодом очистки в конце. Вы можете даже выполнить очистку битов в векторном коде, либо в виде невыровненных хранилищ, которые перекрывают выровненную область (при условии, что длина равна по крайней мере длине вектора), либо используя что-то вроде movmaskdqu. Кто-то просто ленится. Тем не менее, это, вероятно, разумный вопрос для интервью, если интервьюер хочет знать, довольны ли вы stdint.h, побитовыми операторами и основами памяти, поэтому надуманный пример можно простить.
Использование memalign, Aligned-Memory-Blocks может быть хорошим решением проблемы.
Первое, что пришло мне в голову при чтении этого вопроса, - это определить выровненную структуру, создать ее экземпляр, а затем указать на нее.
Есть ли фундаментальная причина, по которой я скучаю, поскольку никто другой не предложил это?
В качестве идентификатора, так как я использовал массив char (предполагая, что системный char равен 8 битам (т.е. 1 байт)), я не вижу необходимости в атрибуте((упакованном)) обязательно (поправьте меня, если я ошибаюсь), но я все равно это вставил.
Это работает на двух системах, на которых я его пробовал, но возможно, что есть оптимизация компилятора, о которой я не подозреваю, что она дает мне ложные срабатывания в отношении эффективности кода. Я использовал gcc 4.9.2 на OSX и gcc 5.2.1 на Ubuntu.
#include <stdio.h>
#include <stdlib.h>
int main ()
{
void *mem;
void *ptr;
// answer a) here
struct __attribute__((packed)) s_CozyMem {
char acSpace[16];
};
mem = malloc(sizeof(struct s_CozyMem));
ptr = mem;
// memset_16aligned(ptr, 0, 1024);
// Check if it's aligned
if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
else printf("Rubbish.\n");
// answer b) here
free(mem);
return 1;
}
MacOS X:
- Все указатели, выделенные с помощью malloc, выровнены по 16 байтов.
C11 поддерживается, так что вы можете просто вызвать align_malloc (16, size).
MacOS X выбирает код, оптимизированный для отдельных процессоров во время загрузки для memset, memcpy и memmove, и этот код использует трюки, о которых вы никогда не слышали, чтобы сделать его быстрым. 99% вероятности, что memset работает быстрее, чем любой рукописный memset16, что делает весь вопрос бессмысленным.
Если вы хотите 100% портативное решение, до C11 его нет. Потому что нет портативного способа проверить выравнивание указателя. Если он не должен быть на 100% портативным, вы можете использовать
char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;
Это предполагает, что выравнивание указателя сохраняется в младших битах при преобразовании указателя в unsigned int. Преобразование в unsigned int теряет информацию и определяется реализацией, но это не имеет значения, потому что мы не конвертируем результат обратно в указатель.
Ужасная часть, конечно, в том, что оригинальный указатель должен быть где-то сохранен, чтобы вызвать с ним функцию free (). В общем, я бы действительно усомнился в мудрости этого дизайна.
Если существуют ограничения, вы не можете тратить один байт, тогда это решение работает: Примечание: есть случай, когда это может выполняться бесконечно:D
void *mem;
void *ptr;
try:
mem = malloc(1024);
if (mem % 16 != 0) {
free(mem);
goto try;
}
ptr = mem;
memset_16aligned(ptr, 0, 1024);
Для решения я использовал концепцию заполнения, которая выравнивает память и не тратит память одного байта.
Если есть ограничения, вы не можете тратить ни одного байта. Все указатели, выделенные с помощью malloc, выровнены по 16 байтов.
C11 поддерживается, так что вы можете просто вызвать align_malloc (16, size).
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
Просто использовать memalign? http://linux.die.net/man/3/memalign
Вы также можете добавить около 16 байтов, а затем выровнять исходный ptr на 16 бит, добавив (16-mod), как показано под указателем:
main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );
printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );
free(mem1);
}
size =1024;
alignment = 16;
aligned_size = size +(alignment -(size % alignment));
mem = malloc(aligned_size);
memset_16aligned(mem, 0, 1024);
free(mem);
Надеюсь, это самая простая реализация, дайте мне знать ваши комментарии.
long add;
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);