'malloc'和'new'如何运作?它们有何不同(实施明智)?

时间:2008-12-13 22:49:56

标签: c++ c

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

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

5 个答案:

答案 0 :(得分:9)

我将引导您回答这个问题:What is the difference between new/delete and malloc/free?。马丁提供了很好的概述。快速了解他们如何工作(不要深入了解如何将其作为成员函数重载):

新表达和分配

  1. 代码包含一个提供type-id的new-expression。
  2. 编译器将查看类型是否使用分配函数重载operator new。
  3. 如果找到运算符新分配函数的重载,则使用赋予new和sizeof(TypeId)的参数作为其第一个参数调用该函数:
  4. 样品:

    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,并希望它可以生成。如果仍然没有足够的位置,则必须抛出std::bad_alloc或从中派生。一个具有throw()(无抛出保证)的分配器,在这种情况下它将返回一个空指针。
    2. C ++运行时环境将创建一个由分配函数返回的内存中由type-id指定的类型的对象。
    3. 有一些特殊名称的特殊分配函数:

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

      如果分配函数返回存储,并且运行时创建的对象的构造函数抛出,则自动调用operator delete。如果使用新形式的其他参数,例如

      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-expression和arrays

      如果你这样做

      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)

答案 1 :(得分:2)

newmalloc的不同之处如下:

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

总而言之,new是高度可定制的,除了内存分配之外还可以进行初始化工作。这是两个很大的不同。

答案 2 :(得分:1)

虽然malloc / freenew / delete具有不同的行为,但它们在低级别执行相同的操作:管理动态分配的内存。我假设这是你真正要问的。在我的系统上,new实际上在内部调用了malloc来执行其分配,所以我只会谈谈malloc

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

内存在堆上声明,因此请确保在完成后释放它。