C++学习011-经常使用内存分配及释放函数

 

C++用有多种方法来分配及释放内存,下面是一些常用的内存分配及释放函数html

 

如今我仍是一个技术小白,通常用到也指示 new+delete 和 malloc和free程序员

 

其余的也是在学习中看到,下面的文字来之“笨小鸟”的文章编程

链接地址:http://blog.csdn.net/wxq1987525/article/details/7462446windows

文章详细内容数组

 

一。关于内存安全

 一、内存分配方式服务器

  内存分配方式有三种:数据结构

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

。例如全局变量,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));
第1、malloc 函数返回的是 void * 类型,若是你写成:p = malloc (sizeof(int)); 则程序没法经过编译,

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

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


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

调用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用调试方式运行,很快就找到了出错位置,由于在某个静态函数中笔误致使

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

 

关于内存方面的内容还有另外一个大神(歌行梅村)写的文章

文章链接:http://blog.csdn.net/chenjie863/article/details/16824501

原文内容:

 

1.为何要用GlobalLock()函数

C/C++ code

   HGLOBAL hImageMemory=GlobalAlloc(GMEM_MOVEABLE, dwFileSize); //给图片分配全局内存  
   void *pImageMemory=GlobalLock(hImageMemory); //锁定内存  
   DWORD dwReadedSize; //保存实际读取的文件大小   
   ReadFile(hFile, pImageMemory, dwFileSize, &dwReadedSize, NULL); //读取图片到全局内存当中  
   GlobalUnlock(hImageMemory); //解锁内存   
   CloseHandle(hFile); //关闭文件句柄   
   IStream *pIStream;//建立一个IStream接口指针,用来保存图片流 

解答: 

  lobalAlloc申请的内存分两种,一种是GMEM_FIXED,另外一种是GMEM_MOVEABLE。
  二者的差异只要在于GMEM_MOVEABLE类型的内存操做系统是能够移动的,好比堆中有好几块小内存,
   当再申请一大块内存时,操做系统会移动GMEM_MOVEABLE类型的内存来合并出一大块。
  正由于GMEM_MOVEABLE是可移动的,因此要用句柄标识,不能用内存地址标识,
   在使用时经过GlobalLock由句柄获得内存地址。

   对于GMEM_FIXED类型的,该函数返回的句柄就是内存指针,能够直接当内存指针使用。

出处:http://topic.csdn.net/u/20100802/17/2e66b3ef-285d-43da-b5a2-60f8d0665fbd.html

2. VC中关于GlobalAlloc,GlobalLock,GlobalUnLock的用法及疑问

调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。 
调用GlobalLock函数锁定内存块,该函数接受一个内存句柄做为参数,而后返回一个指向被锁定的内存块的指针。 您能够用该指针来读写内存。 
调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。 
调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。

GlobalAlloc
说明 
分配一个全局内存块 
返回值 
Long,返回全局内存句柄。零表示失败。会设置GetLastError 
参数表 
参数 类型及说明 
wFlags Long,对分配的内存类型进行定义的常数标志,以下所示: 
             GMEM_FIXED 分配一个固定内存块 
             GMEM_MOVEABLE 分配一个可移动内存块 
             GMEM_DISCARDABLE 分配一个可丢弃内存块 
             GMEM_NOCOMPACT 堆在这个函数调用期间不进行累积 
             GMEM_NODISCARD 函数调用期间不丢弃任何内存块 
             GMEM_ZEROINIT 新分配的内存块所有初始化成零 
dwBytes Long,要分配的字符数 
注解 
如指定了 GMEM_FIXED,那么返回值就是要使用的实际内存地址即指针(GlobalLock 会返回一样的值)——因此在使用固定内存块的时候不须要执行一个 GlobalLock/GlobalUnlock 操做
因为 Win32 采用了高级的内存管理方案,因此使用可移动的内存块并无什么好处
用这个函数分配的内存块容许在8位边界之内
【附】关于GlobalAlloc的问题
--------------------------------------------------------------------------------
问:在使用 GlobalAlloc 分配一个全局内存块时,使用GMEM_FIXED分配一个固定内存块与使用GMEM_MOVEABLE分 配一个可移动内存块到底有什么不一样?(请 具 体 点)
其效率上是否也存在差别?
为何在有些源码中,再使用GMEM_MOVEABLE标志分配内存时,将使用GlobalFree对其返回的内存句柄进行释放操做的语句注释掉,或者干脆就不写?难道是不须要这么作吗?
--------------------------------------------------------------------------------
答:GMEM_MOVEABLE是容许操做系统(或者应用程序)实施对内存堆的管理,在必要时,操做系统能够移动内存块获取更大的块,或者合并一些空闲的内存块,也称“垃圾回收”,它能够提升内存的利用率。通常状况下,内存堆空间是由用户来管理的,windows操做系统不干预。若是存在下列状况,即堆中有10个1K的空闲块,这时若是直接申请一个5K的内存空间,会获得不成功的信息。但若是其它已经被占用的内存块是movable,这时系统就能够移动这些内存块,合并出一个5k的内存块,并成功分配给用户使用。它的空间效率是以运行时的时间效率为代价的。

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);

 

 

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指针。

 

 

 

相关文章
相关标签/搜索