【问题标题】:How do 'malloc' and 'new' work? How are they different (implementation wise)? [duplicate]“malloc”和“new”如何工作?它们有何不同(实施方面)? [复制]
【发布时间】:2010-09-26 19:47:12
【问题描述】:

我知道它们在语法上有何不同,C++ 使用 new,而 C 使用 malloc。但是在高级解释中它们是如何工作的呢?

What is the difference between new/delete and malloc/free?

【问题讨论】:

  • 我不认为这是重复的。我认为 Joel 是在询问 new/malloc 如何分配内存以及它们的实现方式是否存在差异。这与他们对用户的行为方式不同。
  • @Jay:查看 Joel 对@litb 回复的评论。

标签: c++ c


【解决方案1】:

我将引导您找到这个答案:What is the difference between new/delete and malloc/free?。马丁提供了一个很好的概述。关于它们如何工作的快速概览(无需深入探讨如何将它们作为成员函数重载):

新表达式和分配

  1. 该代码包含一个提供类型 ID 的新表达式。
  2. 编译器将检查该类型是否使用分配函数重载了运算符 new。
  3. 如果它发现运算符 new 分配函数的重载,则使用赋予 new 的参数和 sizeof(TypeId) 作为其第一个参数来调用该函数:

示例:

new (a, b, c) TypeId;

// the function called by the compiler has to have the following signature:
operator new(std::size_t size, TypeOfA a, TypeOfB b, TypeOf C c);
  1. 如果 operator new 分配存储失败,它可以调用new_handler,并希望它能够到位。如果仍然没有足够的地方,new 必须抛出std::bad_alloc 或从它派生。具有throw()(无抛出保证)的分配器,在这种情况下应返回一个空指针。
  2. C++ 运行时环境将在分配函数返回的内存中创建由 type-id 给出的类型的对象。

有一些特殊的分配函数被赋予了特殊的名称:

  • no-throw 新。这需要 nothrow_t 作为第二个参数。如下形式的新表达式将调用仅采用 std::size_t 和 nothrow_t 的分配函数:

例子:

new (std::nothrow) TypeId;
  • placement new。这将 void* 指针作为第一个参数,而不是返回新分配的内存地址,而是返回该参数。它用于在给定地址创建对象。标准容器使用它来预分配空间,但仅在需要时才创建对象。

代码:

// the following function is defined implicitly in the standard library
void * operator(std::size_t size, void * ptr) throw() {
    return ptr;
}

如果分配函数返回存储,并且运行时创建的对象的构造函数抛出,则自动调用操作符delete。如果使用了带有附加参数的 new 形式,例如

new (a, b, c) TypeId;

然后调用带有这些参数的操作符 delete。该操作符删除版本仅在删除完成时才被调用,因为对象的构造函数确实抛出了。如果你自己调用 delete,那么编译器将使用普通的 operator delete 函数,只接受一个 void* 指针:

int * a = new int;
=> void * operator new(std::size_t size) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();

TypeWhosCtorThrows * a = new ("argument") TypeWhosCtorThrows;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
=> void operator delete(void * ptr, char const* arg1) throw();

TypeWhosCtorDoesntThrow * a = new ("argument") TypeWhosCtorDoesntThrow;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();

新表达式和数组

如果你这样做了

new (possible_arguments) TypeId[N];

编译器使用operator new[] 函数而不是普通的operator new。运算符可以传递一个不完全是sizeof(TypeId)*N 的第一个参数:编译器可以添加一些空间来存储创建的对象的数量(必须能够调用析构函数)。标准是这样说的:

  • new T[5] 导致调用操作员 new[](sizeof(T)*5+x),并且
  • new(2,f) T[5] 导致调用操作员 new[](sizeof(T)*5+y,2,f)

【讨论】:

  • 也许我只是不知道如何正确搜索...
  • 我有一个问题。如果我有(忽略 vtable 等可能存在的错误——只是一个例子): MyClass *p = malloc(sizeof(MyClass)); MyClass *q = new (p) MyClass(); p == q 总是会吗? q 是否都会
  • 谢谢伙计。是的 q == p,因为这个位置 new 将再次从其分配函数返回 p。实际上,vtables 没有任何问题,您可以创建任何类似的类型。 boost::variant 就是这样做的(据我所知),例如,为多种类型提供一个缓冲区。
  • 你先调用ctor q->~MyClass(); 来删除它手动,然后你可以调用 free(p);释放缓冲区。希望这会有所帮助,祝你好运:)
  • @litb,非常感谢。这确实有助于我从 C/asm 背景中理解 C++ 内部结构。 =]
【解决方案2】:

虽然malloc/freenew/delete 有不同的行为,但它们都在底层做同样的事情:管理动态分配的内存。我假设这是你真正要问的。在我的系统上,new实际上是在内部调用malloc来执行它的分配,所以我只讲malloc

mallocfree 的实际实现可能会有很大差异,因为实现内存分配的方法有很多。一些方法获得更好的性能,一些浪费更少的内存,另一些更适合调试。垃圾收集语言也可能有完全不同的分配方式,但你的问题是关于 C/C++。

通常,块是从堆中分配的,堆是程序地址空间中的一大块内存区域。该库为您管理堆,通常使用像sbrkmmap 这样的系统调用。从堆中分配块的一种方法是维护一个空闲和已分配块列表,其中存储块大小和位置。最初,列表可能包含整个堆的一个大块。当请求一个新块时,分配器将从列表中选择一个空闲块。如果块太大,可以将其分成两个块(一个是请求的大小,另一个是剩下的任何大小)。当一个分配的块被释放时,它可以与相邻的空闲块合并,因为拥有一个大的空闲块比几个小的空闲块更有用。实际的块列表可以存储为单独的数据结构或嵌入到堆中。

有很多变化。您可能希望保留单独的空闲和已分配块列表。如果您有单独的堆区域用于常见大小的块或单独的列表用于这些大小,您可能会获得更好的性能。例如,当您分配一个 16 字节块时,分配器可能有一个 16 字节块的特殊列表,因此分配可以是 O(1)。仅处理 2 的幂的块大小也可能是有利的(其他任何东西都被四舍五入)。例如,Buddy allocator 就是这样工作的。

【讨论】:

    【解决方案3】:

    newmalloc 的不同之处如下:

    • 它通过调用operator new 在分配的内存中构造一个值。可以通过重载此运算符来调整此行为,可以适用于所有类型,也可以仅适用于您的类。
    • 如果无法分配内存,它会调用处理函数。如果您事先注册了这样的处理函数,这使您有机会即时释放所需的内存。
    • 如果这没有帮助(例如,因为您没有注册任何函数),则会引发异常。

    总而言之,new 是高度可定制的,并且除了内存分配之外还进行初始化工作。这是两大区别。

    【讨论】:

      【解决方案4】:

      在 C 中: malloc 分配一块您在参数中提供的大小的内存,并返回一个指向该内存的指针。

      内存是在堆上声明的,所以请确保在完成后释放它。

      【讨论】:

        【解决方案5】:

        "new" 比 malloc 做得更多。 malloc 只是分配内存 - 它甚至不会为您将其归零。 new 初始化对象,调用构造函数等。我怀疑在大多数实现中 new 只不过是基本类型的 malloc 的薄包装。

        【讨论】:

          猜你喜欢
          • 2011-04-26
          • 1970-01-01
          • 1970-01-01
          • 2014-05-25
          • 2012-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多