上一篇讲完内存管理香港概念之后,这一篇重点介绍内存堆栈、malloc()-free()a、new-delete的使用及实现过程。
首先,我们必须知道的是,每个程序在内存中分为几个存储区,静态存储区,堆栈,堆,自由存储区、常量存储区及程序二进制代码的存储区。还有就是CPU的寄存器。如下图所示:
静态存储区:存放上一篇中提到的静态变量,包括静态全局变量、静态内部变量、静态局部变量,这些变量在程序编译时被存放到静态存储区。
常量存储区:主要存放程序中涉及到的常量,比如字符串常量就是存放在这个区。
寄存器:寄存器可以放到这里面也可以不包括,因为他是属于CPU的,为了全面,我还是把他包括进来了。
程序二进制代码区:主要存放的是程序代码被编译器转化而成的二进制代码。
栈区:堆栈区存放的是自动变量在嵌套的代码块结构中,每定义一个变量就往栈里添加变量,其大小一般为1M。代码块结束时局部变量弹出。值得注意的是,这里的弹出并非变量真的从栈中弹出,而是栈顶指针向下移动,原先的变量不销毁,等到有新的自动变量进栈时,进行覆盖,指针向上移动。如下图所示:
需要注意的是,当我们调用递归函数时,如果递归层次太深,超过栈的内存上限时,便出现栈溢出。
堆区:堆区一般是用来存放我们动态分配的变量,如用malloc()及new分配到的内存。如malloc向堆区发起指定大小的内存申请,如果堆区有足够的内存空间便返回相应的地址空间,不成功时操作将等待有内存归还。如下图所示:
值得注意的是,堆区的内存分配是通过一张空闲链表来实现的,申请的时候,如果空闲链表的大小比所申请的空间大,则返回相应的内存区间,否则申请不成功。
当代码中通过free()、delete释放内存时,相应的内存返回给空闲链表,空闲链表再将其组织起来,等待下一次的申请。
堆区内存分配的特点是分配快回收慢,适合大内存的对象。
相比起栈区,堆区主要靠手动管理,而栈区自动管理;堆区最大可以到4G,而栈区只有1M;堆区在内存管理上存在大量内存碎片,栈区几乎没有碎片;堆区需要组织内存空间,效率比起栈区也低。
下面再介绍下关于malloc(),calloc(),realloc()的区别:
malloc()只有一个参数,就是所需要的内存空间,申请完不初始化;
calloc()有两个参数,一个是内存空间的个数,另一个是每个内存空间的大小,申请完每位会自动初始化为0;
realloc()是对内存大小重新分配,如果新申请的内存比原来的大则返回原来的地址,新增加的地址不初始化,如果是减少则会返回新的内存地址。
值得注意的是,在使用malloc()时,要记得使用free()适时释放废弃的变量空间,否则会内存泄露。除非在接近程序结束时可以不free(),程序结束后所有内存自动清空。
下面介绍有关new操作。new是C++的关键字,在C语言中没有。
在C++中对类对象使用new时,会执行三个步骤:
1.在堆中申请内存空间;
2.执行构造函数;
3.返回对象。
需要动态分配内存时使用new或new [],不需要的时候使用delete或delete[]释放内存,这样做防止内存泄露。
除了常规的new之外,还可以通过布局new来手动分配内存及内存位置。如:
<span style="font-size:18px;">char buffer[512]; double *p; p = new (位移量+N*sizeof(double)) double[N];</span>
代码中创建了一个缓冲区,使用布局new在缓冲区上的指定位置(位移量的位置)分配内存空间,与常规new操作不同,布局new操作不知道哪些内存被使用,所以这就需要程序猿自己管理内存位置。
还有一点需要注意的是使用布局new时,如果缓冲区不是动态分配的,那么不能用delete p[]之类的操作释放内存,所申请的内存会随着缓冲区(自动变量)的释放而释放。只有当缓冲区也是动态分配时才可以用delete释放内存空间。