这个内存分配器如何获得块的地址?

How does this memory allocator get the address of the block?

所以,我正在阅读此网站上的编写内存分配器http://dmitrysoshnikov.com/compilers/writing-a-memory-allocator/

我对这部分感到困惑,辅助函数在其中检索块的地址:

/**
 * Returns the object header.
 */
Block *getHeader(word_t *data) {
  return (Block *)((char *)data + sizeof(std::declval<Block>().data) -
                   sizeof(Block));
}

这是一个区块:

struct Block {
 
  // -------------------------------------
  // 1. Object header
 
  /**
   * Block size.
   */
  size_t size;
 
  /**
   * Whether this block is currently used.
   */
  bool used;
 
  /**
   * Next block in the list.
   */
  Block *next;
 
  // -------------------------------------
  // 2. User data
 
  /**
   * Payload pointer.
   */
  word_t data[1];
 
};

有人可以解释一下这背后的逻辑吗? 干杯。

内存分配器需要维护有关每个已分配块的大小以及可用的未分配内存所在位置的元数据。分配器通常会在分配的区域本身旁边维护该元数据。这就是示例分配器正在做的事情。 struct Block 表示分配的内存块 包括 其关联的元数据。前三个成员 (sizeusednext) 是元数据。 data 对应于呈现给分配器用户的 space(另见下文)。

为了响应分配请求,分配器将选择一个可用块,或者合并或拆分块以获得合适的块,将其标记为已使用,并且 return 指向其 data 的指针成员给调用者。稍后,它将想要再次访问与该数据指针一起使用的元数据,这就是 getHeader() 函数实现的目的:

  • data 应该是指向 struct Block.
  • data 成员的指针
  • 它被强制转换为类型char *,以便指针添加以字节为单位而不是word_ts。
  • sizeof(std::declval<Block>().data) - sizeof(Block),它是 C++,而不是 C,计算 struct Block 的开头从它的 data 成员开始的(负)偏移量,假设不安全地, struct Block 的布局中没有尾部填充。
  • 指针加法因此产生一个指向块开头地址的char *,并且
  • 将其转换为 Block * 会产生所需的指针。

关于 data 成员的一句话:该成员被声明为长度为 1 的数组,期望分配器用户可以安全地越过其边界。这被称为“struct hack”。尽管它恰好在许多实现中都有效,但它不符合任何 C 或 C++ 标准。因此它的行为是未定义的,你不应该把它作为一个模型来复制。

另请注意附带的注释具有误导性:data 不是指针,而是数据本身(第一个 word_t)。