У меня есть вопрос по проблеме 13.9 из книги «Взлом интервью по кодированию».
Вопрос состоит в том, чтобы написать выровненную функцию alloc и free, которая поддерживает выделение памяти, и в ответе код приведен ниже:
void *aligned_malloc(size_t required_bytes, size_t alignment) {
void *p1;
void **p2;
int offset=alignment-1+sizeof(void*);
if((p1=(void*)malloc(required_bytes+offset))==NULL)
return NULL;
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2[-1]=p1; //line 6
return p2;
}
Я так запутался со строкой 5 и строкой 6. Почему вы должны сделать «и», так как вы уже добавили смещение к p1? и что значит [-1]? Спасибо за помощь заранее.
Ваш пример кода не является полным. Это ничего не выделяет. Совершенно очевидно, что вам не хватает инструкции malloc, которая устанавливает указатель p1. У меня нет книги, но я думаю, что полный код должен идти по следующим направлениям:
void *aligned_malloc(size_t required_bytes, size_t alignment) {
void *p1;
void **p2;
int offset=alignment-1+sizeof(void*);
p1 = malloc(required_bytes + offset); // the line you are missing
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2[-1]=p1; //line 6
return p2;
}
Итак … что делает код?
Я собираюсь догадаться, что align_free это что-то вроде:
void aligned_free( void* p ) {
void* p1 = ((void**)p)[-1]; // get the pointer to the buffer we allocated
free( p1 );
}
p1 — фактическое распределение. p2 — это возвращаемый указатель, который ссылается на память после точки выделения и оставляет достаточно места как для выравнивания, так и для хранения фактически выделенного указателя в первую очередь. когда выровнен align_free (), будет получено p1, чтобы сделать «реальный» free ().
Что касается математики, это становится немного более громоздким, но это работает.
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
Помните, что p1 — фактическая ссылка распределения. Для пиков давайте предположим следующее с 32-битными указателями:
alignment = 64 bytes, 0x40
offset = 0x40-1+4 = 0x43
p1 = 0x20000110, a value returned from the stock malloc()
Что важно, так это оригинал malloc()
это выделяет дополнительные 0x43 байта пространства выше и вне исходного запроса. Это должно обеспечить как математику выравнивания а также пространство для 32-битного указателя может быть учтено:
p2=(void**)(((size_t)(p1)+offset)&~(alignment-1)); //line 5
p2 = (0x20000110 + 0x43) &~ (0x0000003F)
p2 = 0x20000153 &~ 0x0000003F
p2 = 0x20000153 & 0xFFFFFFC0
p2 = 0x20000140
p2 выравнивается по границе 0x40 (то есть все биты в 0x3F равны 0), и остается достаточно места для хранения 4-байтового указателя для исходного распределения, на который ссылается p1.
Это было навсегда так как я сделал выравнивание по математике, так что, если я уловил биты, пожалуйста кто-то исправит это.
И, кстати, это не единственный способ сделать это.
void* align_malloc(size_t size, size_t alignment)
{
// sanity check for size/alignment.
// Make sure alignment is power of 2 (alignment&(alignment-1) ==0)
// allocate enough buffer to accommodate alignment and metadata info
// We want to store an offset to address where CRT reserved memory to avoid leak
size_t total = size+alignment+sizeof(size_t);
void* crtAlloc = malloc(total);
crtAlloc += sizeof(size_t); // make sure we have enough buffer ahead to store metadata info
size_t crtArithmetic = reinterprete_cast<int>(crtAlloc); // treat as int for pointer arithmetic
void* pRet = crtAlloc + (alignment - (crtArithmetic%alignment));
size_t *pMetadata = reinterprete_cast<size_t*>(pRet);
pMetadata[-1] = pRet - crtArithmetic- sizeof(size_t);
return pRet;
}
В качестве примера size = 20, alignement = 16 и crt malloc вернули адрес 10. и принимая sizeof (size_t) как 4 байта
- total bytes to allocate = 20+16+4 = 40
- memory committed by crt = address 10 to 50
- first make space in front by adding sizeof(size_t) 4 bytes so you point at 14
- add offset to align which is 14 + (16-14%16) = 16
- move back sizeof(size_t) 4 bytes (i.e. 12) and treat that as size_t pointer and store offset 2 (=12-10) to point to crt malloc
Начните
Таким же образом, align_free приведёт пустой указатель к указателю size_t, переместится назад на одну позицию, прочитает значение, сохраненное там, и отрегулирует смещение, чтобы перейти к началу crt alloc
void* align_free(void* ptr)
{
size_t* pMetadata = reinterprete_cast<size_t*> (ptr);
free(ptr-pMetadata[-1]);
}
Оптимизация: вам не нужно дополнительное выделение sizeof (size_t), если ваше выравнивание было больше, чем sizeof (size_t)