该分配器如何发挥调试作用?

问题描述

当我阅读sgi / STL的源代码时,我注意到了

template <class _Alloc>
class debug_alloc {

private:

  enum {_S_extra = 8};  // Size of space used to store size.  Note
                        // that this must be large enough to preserve
                        // alignment.

public:

  static void* allocate(size_t __n)
  {
    char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra);
    *(size_t*)__result = __n;
    return __result + (int) _S_extra;
  }

  static void deallocate(void* __p,size_t __n)
  {
    char* __real_p = (char*)__p - (int) _S_extra;
    assert(*(size_t*)__real_p == __n);
    _Alloc::deallocate(__real_p,__n + (int) _S_extra);
  }

  static void* reallocate(void* __p,size_t __old_sz,size_t __new_sz)
  {
    char* __real_p = (char*)__p - (int) _S_extra;
    assert(*(size_t*)__real_p == __old_sz);
    char* __result = (char*)
      _Alloc::reallocate(__real_p,__old_sz + (int) _S_extra,__new_sz + (int) _S_extra);
    *(size_t*)__result = __new_sz;
    return __result + (int) _S_extra;
  }

};

这是调试模板。

我可以看到它确保分配的空间始终大于8个字节,并且我还可以看到它使用*(size_t*)__result = __n;替换了从__result的地址开始的内容,但我对此感到非常困惑

为什么要分配空间然后用__n替换内容? 为什么然后返回__result + (int) _S_extra;并定义char* __real_p = (char*)__p - (int) _S_extra;? 请详细说明函数void* allocate(size_t __n)void deallocate(void* __p,size_t __n)在内存中的工作方式。

链接到源代码https://github.com/karottc/sgi-stl/blob/master/concept_checks.h

解决方法

此分配器在每个分配之前使用一个8字节的标头来记录其大小。通常将这样的元数据存储在实际分配旁边。即使标头包含一个4字节的整数,但8个字节仍可确保返回的指针与8个字节对齐,这是较大类型的常见要求。

从代码中可以看到,分配请求n + 8个字节(称为结果分配p),将大小存储在开头,然后返回p + 8。

取消分配和重新分配的指针为p + 8,因此必须先减去以返回到实际分配。