zoukankan      html  css  js  c++  java
  • globalalloc、malloc和new的区别

    简单来说:

    malloc是c分配内存的库函数,new是c++分配内存的操作符,而globalalloc是win32提供的分配内存的API

    malloc不能自动调用构造和析构函数,在c++中没什么实用价值,但是c++经常调用c程序,这时才用到。

    new是c++最常用的内存分配方式。

    globalalloc是win32提供的,有很多特别的功能,如跨进程、锁定等特殊功能。需要特殊功能时才用。

    以下转自:http://blog.sina.com.cn/s/blog_6cb8c15e0100t5g9.html

    GlobalAlloc是为了与Win16兼容才保留的,在Win32下不要使用。 全局内存对象使用GlobalAlloc函数分配,在Windows 3.X的时代,分配的内存可以有两种,全局的和局部的,例如GlobalAlloc和LocalAlloc。但在Win32的时代这些函数已经被废弃了,现在的内存只有一种就是虚存。在Win32中所有的进程所使用的内存区域是相互隔离的,每个进程都拥有自己的地址空间。而且系统使用了页面交换功能,就是利用磁盘空间来模拟RAM,在RAM中数据不使用时将会被交换到磁盘,在需要时将会被重新装入RAM。

    两者都是在堆上分配内存区。 
    malloc()是C运行库中的动态内存分配函数,WINDOWS程序基本不使用了,因为它比WINDOWS内存分配函数少了一些特性,如,整理内存。 
    GlobalAlloc()是16位WINDOWS程序使用的API,返回一个内存句柄,在实际需要使用时,用GlobalLock()来实际得到内存区。但,32位WINDOWS系统中,应使用新的内存分配函数HeapAlloc()以得到更好的支持,GlobalAlloc()还可以用,主要是为了兼容。

    HeapAlloc apply memory from kernel32.dll   
    GlobalAlloc obsolete malloc   apply memory form C runtime   memory ,and C r untime applys         from    kernel32.dll   
    new a wrapper of malloc but it is NOT a must for new to implement 
    based on malloc.  

    CoMemAlloc apply memory from kernel32.dll

    all are heap memory.

    recommend HeapAlloc for big block memory allocation 
    recommend stack memory space.
    recommend HeapAlloc for big block memory allocation 
    recommend stack memory space.


    malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。 
    对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。 
    因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。 
    我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见示例7-8。

    class Obj{

    public : 

    Obj(){ cout << “Initialization” << endl; }

    ~Obj(){ cout << “Destroy” << endl; }

    void Initialize(){ cout << “Initialization” << endl; }

    void Destroy(){ cout << “Destroy” << endl; }

    }; 
    void UseMallocFree(){ 

    Obj *a = (obj *)malloc(sizeof(obj)); // 申请动态内存 

    a->Initialize(); // 初始化 //…

     a->Destroy(); // 清除工作 

    free(a); // 释放内存


    void UseNewDelete(){

     Obj *a = new Obj; // 申请动态内存并且初始化 //… 

    delete a; // 清除并且释放内存


    示例7-8 用malloc/free和new/delete如何实现对象的动态内存管理

    类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。函数UseNewDelete则简单得多。 
    所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。 
    既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。 
    如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。 
    全局内存对象使用GlobalAlloc函数分配,在Windows 3.X的时代,分配的内存可以有两种,全局的和局部的,例如GlobalAlloc和LocalAlloc。但在Win32的时代这些函数已经被废弃了,现在的内存只有一种就是虚存。在Win32中所有的进程所使用的内存区域是相互隔离的,每个进程都拥有自己的地址空间。而且系统使用了页面交换功能,就是利用磁盘空间来模拟RAM,在RAM中数据不使用时将会被交换到磁盘,在需要时将会被重新装入RAM。

    文章二:转自:http://blog.csdn.net/weiwangchao_/article/details/6971835

    一。关于内存

     1、内存分配方式

      内存分配方式有三种:

      (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在

    。例如全局变量,static变量。

      (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存

    储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

      (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自

    己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

       2.内存使用错误
          发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。

    而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有

    发生任何问题,你一走,错误又发作了。 常见的内存错误及其对策如下:
           * 内存分配未成功,却使用了它。

      编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查

    指针是否为NULL。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。

      * 内存分配虽然成功,但是尚未初始化就引用它。

      犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值

    错误(例如数组)。 内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不

    可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

      * 内存分配成功并且已经初始化,但操作越过了内存的边界。

      例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞

    错,导致数组操作越界。

      * 忘记了释放内存,造成内存泄露。

      含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次

    程序突然死掉,系统出现提示:内存耗尽。

      动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误

    (new/delete同理)。

      * 释放了内存却继续使用它。
     
      有三种情况:

      (1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新

    设计数据结构,从根本上解决对象管理的混乱局面。

      (2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函

    数体结束时被自动销毁。

      (3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

      【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存

      【规则2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

      【规则3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

      【规则4】动态内存的申请与释放必须配对,防止内存泄漏。

      【规则5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

     
    二. 详解new,malloc,GlobalAlloc
        
     1.  new

      new和delete运算符用于动态分配和撤销内存的运算符

    new用法:

              1>     开辟单变量地址空间

                   1)new int;  //开辟一个存放数组的存储空间,返回一个指向该存储空间的地址.int *a = new

    int 即为将一个int类型的地址赋值给整型指针a. 

                   2)int *a = new int(5) 作用同上,但是同时将整数赋值为5

              2>    开辟数组空间

                   一维: int *a = new int[100];开辟一个大小为100的整型数组空间

             一般用法: new 类型 [初值]

    delete用法:

              1> int *a = new int;

                   delete a;   //释放单个int的空间

              2>int *a = new int[5];

                   delete [] a; //释放int数组空间

              要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问.

              用new和delete可以动态开辟,撤销地址空间.在编程序时,若用完一个变量(一般是暂时存储的数组),

    下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它.

    2.  malloc
      原型:extern void *malloc(unsigned int num_bytes); 
      用法:#i nclude <malloc.h>或#i nclude <stdlib.h> 
      功能:分配长度为num_bytes字节的内存块 
      说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。 
      当内存不再使用时,应使用free()函数将内存块释放。 
      malloc的语法是:指针名=(数据类型*)malloc(长度),(数据类型*)表示指针. 
    说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型

    的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。

    malloc()函数的工作机制 
      malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc

    函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大

    小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并

    将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到

    最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以

    满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们

    进行整理,将相邻的小空闲块合并成较大的内存块。
     
    和new的不同
    从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需

    要大小。比如:
    int *p;
    p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);
    或:
    int* parr;
    parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;
    而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。
    int* p;
    p = (int *) malloc (sizeof(int));
    第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,

    报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。
    第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:
    int* p = (int *) malloc (1);
    代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无

    家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。


    3.  GlobalAlloc
     
       VC中关于GlobalAlloc,GlobalLock,GlobalUnLock,GlobalFree

    调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。 
    调用GlobalLock函数锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指向被锁定的内存块的指

    针。 您可以用该指针来读写内存。 
    调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。 
    调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。
      
    GlobalAlloc 
    说明 
    分配一个全局内存块 
    返回值 
    Long,返回全局内存句柄。零表示失败。会设置GetLastError 
    参数表 
    参数 类型及说明 
    wFlags Long,对分配的内存类型进行定义的常数标志,如下所示: 
                 GMEM_FIXED 分配一个固定内存块 
                 GMEM_MOVEABLE 分配一个可移动内存块 
                 GMEM_DISCARDABLE 分配一个可丢弃内存块 
                 GMEM_NOCOMPACT 堆在这个函数调用期间不进行累积 
                 GMEM_NODISCARD 函数调用期间不丢弃任何内存块 
                 GMEM_ZEROINIT 新分配的内存块全部初始化成零 
    dwBytes Long,要分配的字符数

      GlobalLock  
    函数功能描述:锁定一个全局的内存对象,返回指向该对象的第一个字节的指针
    函数原型:
    LPVOID GlobalLock( HGLOBAL hMem )
    参数:
    hMem:全局内存对象的句柄。这个句柄是通过GlobalAlloc或GlobalReAlloc来得到的
    返回值:
    调用成功,返回指向该对象的第一个字节的指针
    调用失败,返回NULL,可以用GetLastError来获得出错信息
    注意:
    调用过GlobalLock锁定一块内存区后,一定要调用GlobalUnlock来解锁
      
      GlobalUnlock
    函数功能描述:解除被锁定的全局内存对象
    函数原型:BOOL GlobalUnlock( HGLOBAL hMem );
    参数:hMem:全局内存对象的句柄
    返回值:
    非零值,指定的内存对象仍处于被锁定状态
    0,函数执行出错,可以用GetLastError来获得出错信息,如果返回NO_ERROR,则表示内存对象已经解锁了
    注意:    这个函数实际上是将内存对象的锁定计数器减一,如果计数器不为0,则表示执行过多个GlobalLock

    函数来对这个内存对象加锁,需要对应数目的GlobalUnlock函数来解锁。如果通过GetLastError函数返回错误

    码为ERROR_NOT_LOCKED,则表示未加锁或已经解锁。

      示例:
    // Malloc memory
    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSize);
    // Lock memory
    pMem = (BYTE *) GlobalLock(hMem);
    ..................
    // Unlock memory
    GlobalUnlock(hMem);
    GlobalFree(hMem);

    三 总结

    灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题。当程序越来越复杂时,内存的管理也

    会变得越加复杂,稍有不慎就会出现内存问 题。内存泄漏是最常见的内存问题之一。内存泄漏如果不是很严重

    ,在短时间内对程序不会有太大的影响,这也使得内存泄漏问题有很强的隐蔽性,不容易被发现。 然而不管内

    存泄漏多么轻微,当程序长时间运行时,其破坏力是惊人的,从性能下降到内存耗尽,甚至会影响到其他程序

    的正常运行。另外内存问题的一个共同特点 是,内存问题本身并不会有很明显的现象,当有异常现象出现时已

    时过境迁,其现场已非出现问题时的现场了,这给调试内存问题带来了很大的难度。

     下载Windows Debug 工具, http://www.microsoft.com/whdc/devtools/debugging/default.mspx
    安装后,使用其中的gflags.exe工具打开PageHeap,
    gflags -p /enable MainD.exe /full
    重新使用VS用调试方式运行,很快就找到了出错位置,因为在某个静态函数中笔误导致

    在编写稳定的服务器程序时,这个工具尤为有用。


    参考文献及网页地址:
    1. http://www.bccn.net/Article/kfyy/cjj/jszl/200607/4172.html
    2. http://www.7880.com/Info/Article-8282a500.html 
    3. http://www.cnblogs.com/jjzhou1988/archive/2008/11/30/1344314.html
    4. http://blog.chinaunix.net/u3/101356/showart_2031203.html
    5. http://www.cnblogs.com/howareyou586/archive/2008/11/06/1328353.html

    1. 首先我们来看HeapAlloc:
    MSDN上的解释为:HeapALloc是从堆上分配一块内存,且分配的内存是不可移动的(即如果没有连续的空间能满足分配的大小,程序不能将其他零散的 空间利用起来,从而导致分配失败),该分配方法是从一指定地址开始分配,而不像GloabalAlloc是从全局堆上分配,这个有可能是全局,也有可能是 局部。函数原型为:
    LPVOID
    HeapAlloc(
        HANDLE hHeap,
        DWORD dwFlags,
       SIZE_T dwBytes
        );
    hHeap是进程堆内存开始位置。
    dwFlags是分配堆内存的标志。包括HEAP_ZERO_MEMORY,即使分配的空间清零。
    dwBytes是分配堆内存的大小。
    其对应的释放空间函数为HeapFree。

    2. 再看GlobalAlloc:该函数用于从全局堆中分配出内存供程序使用,函数原型为:
    HGLOBAL GlobalAlloc(
    UINT uFlags,
    SIZE_T dwBytes
    );
    uFlags参数含义
    GHND   GMEM_MOVEABLE和GMEM_ZEROINIT的组合
    GMEM_FIXED   分配固定内存,返回值是一个指针
    GMEM_MOVEABLE   分配活动内存,在Win32中,内存块不能在物理内存中移动,但能在默认的堆中移动。返回值是内存对象的句柄,用函数GlobalLock可将句柄转化为指针
    GMEM_ZEROINIT   将内存内容初始化为零
    GPTR   GMEM_FIXED和GMEM_ZEROINIT的组合
    一般情况下我们在编程的时候,给应用程序分配的内存都是可以移动的或者是可以丢弃的,这样能使有限的内存资源充分利用,所以,在某一个时候我们分配的那块 内存的地址是不确定的,因为他是可以移动的,所以得先锁定那块内存块,这儿应用程序需要调用API函数GlobalLock函数来锁定句柄。如下: lpMem=GlobalLock(hMem); 这样应用程序才能存取这块内存。所以我们在使用GlobalAllock时,通常搭配使用GlobalLock,当然在不使用内存时,一定记得使用 GlobalUnlock,否则被锁定的内存块一直不能被其他变量使用。
    GlobalAlloc对应的释放空间的函数为GlobalFree。

    3. LocalAlloc:该函数用于从局部堆中分配内存供程序使用,函数原型为:
    HLOCAL LocalAlloc(
    UINT uFlags,
    SIZE_T uBytes
    );
    参数同GlobalAlloc。
    在16位Windows中是有区别的,因为在16位windows用一个全局堆和局部堆来管理内存,每一个应用程序或dll装入内存时,代码段被装入全局 堆,而系统又为每个实例从全局堆中分配了一个64kb的数据段作为该实例的局部堆,用来存放应用程序的堆栈和所有全局或静态变量。而 LocalAlloc/GlobalAlloc就是分别用于在局部堆或全局堆中分配内存。
    由于每个进程的局部堆很小,所以在局部堆中分配内存会受到空间的限制。但这个堆是每个进程私有的,相对而言分配数据较安全,数据访问出错不至于影响到整个系统。
    而在全局堆中分配的内存是为各个进程共享的,每个进程只要拥有这个内存块的句柄都可以访问这块内存,但是每个全局内存空间需要额外的内存开销,造成分配浪费。而且一旦发生严重错误,可能会影响到整个系统的稳定。
    不过在Win32中,每个进程都只拥有一个省缺的私有堆,它只能被当前进程访问。应用程序也不可能直接访问系统内存。所以在Win32中全局堆和局部堆都 指向进程的省缺堆。用LocalAlloc/GlobalAlloc分配内存没有任何区别。甚至LocalAlloc分配的内存可以被 GlobalFree释放掉。所以在Win32下编程,无需注意Local和Global的区别,一般的内存分配都等效于 HeapAlloc(GetProcessHeap(),...)。
    LocalAlloc对应的释放函数为LockFree。


    4. VirtualAlloc:该函数的功能是在调用进程的虚地址空间,预定或者提交一部分页,如果用于内存分配的话,并且分配类型未指定MEM_RESET,则系统将自动设置为0;其函数原型:
    LPVOID VirtualAlloc(
    LPVOID lpAddress, // region to reserve or commit
    SIZE_T dwSize, // size of region
    DWORD flAllocationType, // type of allocation
    DWORD flProtect // type of access protection
    );
    VirtualAlloc可以通过并行多次调用提交一个区域的部分或全部来保留一个大的内存区域。多重调用提交同一块区域不会引起失败。这使得一个应用程 序保留内存后可以随意提交将被写的页。当这种方式不在有效的时候,它会释放应用程序通过检测被保留页的状态看它是否在提交调用之前已经被提交。
    VirtualAlloc对应的释放函数为VirtualFree。


    5.Malloc:malloc与free是C++/C语言的标准库函数,可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用 malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是 库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。


    6.New:new/delete是C++的运算符。可用于申请动态内存和释放内存。C++语言需要一个能完成动态内存分配和初始化工作的运算符new, 以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。C++程序经常要调用C函数,而C程序只能用malloc /free管理动态内存。new 是个操作符,和什么"+","-","="...有一样的地位.
            malloc是个分配内存的函数,供你调用的.
            new是保留字,不需要头文件支持.
            malloc需要头文件库函数支持.new 建立的是一个对象,
            malloc分配的是一块内存.
            new建立的对象你可以把它当成一个普通的对象,用成员函数访问,不要直接访问它的地址空间
            malloc分配的是一块内存区域,就用指针访问好了,而且还可以在里面移动指针.
    内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针

  • 相关阅读:
    布隆过滤器
    django-redis 和 vue 实现的购物车
    富文本编辑器
    django 和 七牛云 交互
    django 提供的加密算法
    DRF mixins 的常用方法
    Python笔记5(作业)
    Python笔记5(random&sys&os模块)
    Python笔记5(collections模块)
    Python笔记5(时间模块)
  • 原文地址:https://www.cnblogs.com/wangjixianyun/p/3404476.html
Copyright © 2011-2022 走看看