新的/malloc如何记住分配的数据量?

3
为什么这不会导致内存泄漏?我在堆上为 long long 分配了空间,然后将返回的指针更改为 char*。之后,我在此指针上调用 delete(或在第二个示例中使用 free)。这两个示例都似乎不会导致内存泄漏。
#include <stdlib.h>

struct leaky {
   long long* testptr;
   leaky() {testptr = new long long; }
   ~leaky() { void* newptr = (void*) testptr;
              char* newptr2 = (char*) newptr;
              delete newptr2; }
};

struct leaky2 {
   long long* testptr;
   leaky2() { testptr = (long long*) malloc(sizeof(long long)); }
   ~leaky2() { void* newptr = (void*) testptr;
               char* newptr2 = (char*) newptr;
               free (newptr2); }
};

int main() {
   while (true) {
      {
         leaky leak = leaky();
      }
   }
}

我在Unix系统上测试了这个: gcc版本为4.6.3 20120306 (Red Hat 4.6.3-2) (GCC)。


请注意,malloc返回(void*),因此如果依赖于类型,则在malloc返回后就会丢失。像malloc这样的分配器保留标题(有时还有尾随数据),以让它们找出分配的大小等信息。 - Adrian Lis
4个回答

6
第一段代码是未定义行为,因为您不能从与您使用new操作符不相关的类型中delete。第二种情况是正确的,所以我们可以讨论它。
malloc和free的接口基于void*,因此类型在这里并不重要,但问题仍然存在:free如何知道使用malloc分配了多少内存?答案是这是实现定义的,可以用不同的方式来完成。分配器可以获取一个更大的内存块,并在其中存储信息。在某些情况下,malloc为不同的固定大小(特别是对于小对象)从不同的池中进行分配,因此信息不需要跟踪可能很小的对象。在这些情况下,free的实现只需要找出内存来自哪个池,并将该块返回给池即可。

4

malloc 函数将一些管理信息与其返回给您的地址值相关联(有时在该地址之前的内存区域)。这些管理信息包含分配的大小。将地址强制转换并不会改变该地址的值,仅改变您的代码对其的解释。


0
当使用malloc或new时,所使用的空间包括“开销”(与已用和空闲内存块大小相关的信息,指向所需空间的实际指针)和“有效载荷”(正在使用的内存)。
一种用于此簿记信息的行话是“初始化记录”,可能类似于以下结构体。
struct InitRecord
{
  int memBlockSize;
  void* mem;
};

这就是malloc如何记住你使用了多少内存。更深入理解的好方法(也是一项有趣的练习!)是创建自己的堆分配器。

这里有很好的参考资料 http://www.cs.ucsb.edu/~rich/class/cs170/labs/lab1.malloc/index.html

还有这里

http://systematicgaming.wordpress.com/2008/08/05/memory-management-introduction/


0
任何内存指针都可以转换为char*,并且转换回来后具有相同的值。因此,将其强制转换为char*只是改变了编译器解释该值的方式(例如,sizeof(*ptr)返回的大小等),而不会改变指针的值。

  • operator deletefree()接受void*。因此,在它们被要求释放内存时,任何有关“类型”的概念已经消失了。它们必须能够对内存进行处理,而不知道这些字节以前是什么。

    作为参考,它们通常通过分配一些额外的内存,并将该内存的大小记录在返回给您的地址之前的某些字节中来实现。但这是实现细节,请不要依赖它。


  • 一个值得注意的例外是伙伴系统分配器,其中分配的大小由指针值隐含确定。 - Raymond Chen
    另一个值得注意的例外是@dribeas提到的池分配器。 - James Kanze

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接