You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键细节说明

  1. 元数据对齐保证:通过required_alignment确保std::align计算出的位置,能让元数据放在用户指针前方时也满足自身对齐要求,避免未定义行为。
  2. 原始地址记录:元数据中直接存储new返回的原始指针,释放时无需计算偏移,直接使用该地址释放,彻底规避偏移计算的错误。
  3. 标准兼容性:完全基于C++11标准的std::align和内存操作,无平台依赖,可移植性强。

内容的提问来源于stack exchange,提问作者Jacob Faib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:15:43