我对书中的问题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]是什么意思?感谢您的帮助。
答案 0 :(得分:11)
您的示例代码不完整。它什么都不分配。很明显你缺少一个设置p1指针的malloc语句。我没有这本书,但我认为完整的代码应该遵循这些方针:
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 );
}
答案 1 :(得分:10)
p1是实际分配。 p2是返回的指针,它引用超出分配点的内存,并为第一个位置留下足够的空间用于对齐和存储实际分配的指针。当调用aligned_free()时,将检索p1以执行“real”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引用。
它已经永远,因为我做了对齐数学运算,所以如果我找到了这些位,请有人纠正这个。
答案 2 :(得分:0)
顺便说一下,这不是唯一的方法。
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会将void指针转换为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)额外分配