Как выделить выровненную память только с использованием стандартной библиотеки - PullRequest
396 голосов
/ 23 октября 2008

Я только что закончил тест в рамках собеседования, и один вопрос поставил меня в тупик, даже используя Google для справки. Я хотел бы посмотреть, что команда StackOverflow может сделать с этим:

Функция memset_16aligned требует 16-байтового выровненного указателя, переданного ей, иначе произойдет сбой.

a) Как бы вы разместили 1024 байта памяти и выровняли ее по 16-байтовой границе?
б) Освободите память после выполнения memset_16aligned.

{    
   void *mem;
   void *ptr;

   // answer a) here

   memset_16aligned(ptr, 0, 1024);

   // answer b) here    
}

Ответы [ 17 ]

3 голосов
/ 12 октября 2010

использование memalign, Aligned-Memory-Blocks может быть хорошим решением проблемы.

1 голос
/ 25 ноября 2013

MacOS X:

  1. Все указатели, выделенные с помощью malloc, выровнены по 16 байтов.
  2. C11 поддерживается, так что вы можете просто вызвать align_malloc (16, size).

  3. 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 (). В общем, я бы действительно усомнился в мудрости этого дизайна.

0 голосов
/ 14 марта 2014

Для решения я использовал концепцию заполнения, которая выравнивает память и не тратит впустую память одного байта.

Если есть ограничения, вы не можете тратить ни одного байта. Все указатели, выделенные с помощью 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);
0 голосов
/ 25 ноября 2013

Если есть ограничения, которые вы не можете потратить ни одного байта, то это решение работает: Примечание: есть случай, когда это может выполняться бесконечно: D

   void *mem;  
   void *ptr;
try:
   mem =  malloc(1024);  
   if (mem % 16 != 0) {  
       free(mem);  
       goto try;
   }  
   ptr = mem;  
   memset_16aligned(ptr, 0, 1024);
0 голосов
/ 25 марта 2013

Вы также можете добавить около 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);
}
0 голосов
/ 04 сентября 2012

Просто использовать memalign? http://linux.die.net/man/3/memalign

0 голосов
/ 04 сентября 2012
long add;   
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...