C++11中如何可移植恢复对齐分配器的元数据?
问题描述
我正在编写一个分配器(为简化起见,假设不会出现失败或抛出异常):
struct AllocationHeader { std::size_t size; std::size_t align; }; void *aligned_allocate(std::size_t requested_size, std::size_t alignment) { constexpr auto header_size = sizeof(AllocationHeader); const auto total_size = header_size + requested_size + alignment; auto alloced_ptr = ::new unsigned char[total_size]; ::new (alloced_ptr) AllocationHeader{requested_size, alignment}; auto ret_ptr = static_cast<void *>(alloced_ptr + header_size); auto remaining_size = total_size - header_size; return std::align(alignment, requested_size, ret_ptr, remaining_size); }
释放内存时我希望恢复植入的元数据:
void aligned_deallocate(void *ptr) { constexpr auto header_size = sizeof(AllocationHeader); // 这显然是错误的 auto header_start = reinterpret_cast<unsigned char *>(ptr) - header_size; auto header = reinterpret_cast<AllocationHeader *>(header_start); do_something_with(header->size, header->align); // 剩余的释放逻辑... }
但显然转换回AllocationHeader是错误的。理论上原始align可以是任意值(假设是2的幂),包括大于std::max_align_t的值。
那么在C++11中,我该如何可移植地恢复std::align()移动指针的偏移量?
编辑:
我想可以先进行指针偏移:
void *aligned_allocate(std::size_t requested_size, std::size_t alignment) { constexpr auto header_size = sizeof(AllocationHeader); const auto total_size = header_size + requested_size + alignment; auto alloced_ptr = ::new unsigned char[total_size]; auto ret_ptr = static_cast<void *>(alloced_ptr + header_size); auto remaining_size = total_size - header_size; std::align(alignment, requested_size, ret_ptr, remaining_size); ::new (reinterpret_cast<unsigned char *>(ret_ptr) - header_size) AllocationHeader{requested_size, alignment}; return ret_ptr; }
但这样AllocationHeader可能会被构造在不对齐的地址上...
解决方案
核心思路是把元数据放在对齐后的内存块前方,同时保证元数据自身的对齐要求被满足,并且通过直接记录原始分配地址,避免释放时的偏移计算误差。
正确实现代码
#include <cstddef> #include <new> struct AllocationHeader { std::size_t size; std::size_t align; unsigned char* original_ptr; // 记录原始分配的起始地址 }; void *aligned_allocate(std::size_t requested_size, std::size_t alignment) { // 取元数据自身对齐要求与用户请求对齐的较大值,保证元数据的对齐合法性 constexpr auto header_align = alignof(AllocationHeader); const auto required_alignment = (alignment > header_align) ? alignment : header_align; // 总分配空间:元数据大小 + 用户请求大小 + 最大对齐偏移量 const auto total_size = sizeof(AllocationHeader) + requested_size + required_alignment; auto alloced_ptr = ::new unsigned char[total_size]; // 计算满足对齐要求的起始位置,预留元数据的空间 void* ret_ptr = alloced_ptr; std::size_t remaining_size = total_size; std::align(required_alignment, requested_size + sizeof(AllocationHeader), ret_ptr, remaining_size); // 在用户指针前方构造元数据 auto header_ptr = reinterpret_cast<AllocationHeader*>( reinterpret_cast<unsigned char*>(ret_ptr) - sizeof(AllocationHeader) ); ::new (header_ptr) AllocationHeader{requested_size, alignment, alloced_ptr}; // 返回给用户的是元数据之后的对齐地址 return reinterpret_cast<unsigned char*>(header_ptr) + sizeof(AllocationHeader); } void aligned_deallocate(void *ptr) { // 直接从用户指针往前偏移元数据大小获取元数据 auto header_ptr = reinterpret_cast<AllocationHeader*>( reinterpret_cast<unsigned char*>(ptr) - sizeof(AllocationHeader) ); do_something_with(header_ptr->size, header_ptr->align); // 释放原始分配的内存(元数据为POD,析构可省略) delete[] header_ptr->original_ptr; }
关键细节说明
- 元数据对齐保证:通过
required_alignment确保std::align计算出的位置,能让元数据放在用户指针前方时也满足自身对齐要求,避免未定义行为。 - 原始地址记录:元数据中直接存储
new返回的原始指针,释放时无需计算偏移,直接使用该地址释放,彻底规避偏移计算的错误。 - 标准兼容性:完全基于C++11标准的
std::align和内存操作,无平台依赖,可移植性强。
内容的提问来源于stack exchange,提问作者Jacob Faib
相关产品推荐
相关产品推荐

