Как выровнять указатель

Как выровнять указатель с 16-байтовой границей?

Я нашел этот код, не уверен, что его правильный

char* p= malloc(1024);

if ((((unsigned long) p) % 16) != 0) 
{
     unsigned char *chpoint = (unsigned char *)p;        
     chpoint += 16 - (((unsigned long) p) % 16);
     p = (char *)chpoint;
}

Будет ли это работать?

спасибо

Ответы

Ответ 1

С++ 0x предлагает std::align, который делает именно это.

// get some memory
T* const p = ...;
std::size_t const size = ...;

void* start = p;
std::size_t space = size;
void* aligned = std::align(16, 1024, p, space);
if(aligned == nullptr) {
    // failed to align
} else {
    // here, p is aligned to 16 and points to at least 1024 bytes of memory
    // also p == aligned
    // size - space is the amount of bytes used for alignment
}

который кажется очень низким. Я думаю,

// also available in Boost flavour
using storage = std::aligned_storage_t<1024, 16>;
auto p = new storage;

также работает. Вы можете легко справиться с правилами псевдонимов, хотя, если не будете осторожны. Если у вас был точный сценарий (подходят N объектов типа T с 16-байтовой границей?) Я думаю, что я мог бы рекомендовать что-то приятнее.

Ответ 2

Попробуйте следующее:

Он возвращает выровненную память и освобождает память, практически без дополнительных затрат на управление памятью.

#include <malloc.h>
#include <assert.h>

size_t roundUp(size_t a, size_t b) { return (1 + (a - 1) / b) * b; }

// we assume here that size_t and void* can be converted to each other
void *malloc_aligned(size_t size, size_t align = sizeof(void*))
{
    assert(align % sizeof(size_t) == 0);
    assert(sizeof(void*) == sizeof(size_t)); // not sure if needed, but whatever

    void *p = malloc(size + 2 * align);  // allocate with enough room to store the size
    if (p != NULL)
    {
        size_t base = (size_t)p;
        p = (char*)roundUp(base, align) + align;  // align & make room for storing the size
        ((size_t*)p)[-1] = (size_t)p - base;      // store the size before the block
    }
    return p;
}

void free_aligned(void *p) { free(p != NULL ? (char*)p - ((size_t*)p)[-1] : p); }

Внимание:

Я почти уверен, что я ступаю по частям стандарта C здесь, но кто заботится.: P

Ответ 3

В библиотеке glibc malloc, realloc всегда возвращает 8 байтов. Если вы хотите выделить память с некоторым выравниванием, которое имеет более высокую мощность 2, вы можете использовать memalign и posix_memalign. Прочитайте http://www.gnu.org/s/hello/manual/libc/Aligned-Memory-Blocks.html

Ответ 4

posix_memalign - это один из способов: http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_memalign.html, пока ваш размер равен двум.

Проблема с предоставленным вами решением заключается в том, что вы рискуете списать конец выделенной памяти. Альтернативное решение состоит в том, чтобы выделить нужный размер + 16 и использовать подобный трюк тому, который вы делаете, чтобы получить указатель, который выровнен, но все же попадает в выделенный регион. Тем не менее, я бы использовал posix_memalign в качестве первого решения.

Ответ 5

несколько вещей:

  • не меняйте указатель, возвращаемый malloc/new: вам понадобится его позже, чтобы освободить память;
  • убедитесь, что ваш буфер достаточно большой после настройки выравнивания.
  • используйте size_t вместо unsigned long, так как size_t гарантированно имеет тот же размер, что и указатель, в отличие от всего остального:

здесь код:

size_t size = 1024; // this is how many bytes you need in the aligned buffer
size_t align = 16;  // this is the alignment boundary
char *p = (char*)malloc(size + align); // see second point above
char *aligned_p = (char*)((size_t)p + (align - (size_t)p % align));
// use the aligned_p here
// ...
// when you're done, call:
free(p); // see first point above