您的位置:首页 > 编程语言 > C语言/C++

C++基础系列十:C++中内存分配

2013-05-01 14:20 197 查看
(1)C++中内存种类

C++中,一般将内存分成5个区:栈、堆、自由存储区、全局/静态存储区、常量存储区。 此外还包括存放二进制函数体的程序代码区。

栈:就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区,里面的变量通常是局部变量、函数参数等。

堆:程序在运行的时候用new分配的指定大小的内存块,编译器不负责释放,程序员自己负责在何时用delete 释放内存,因而其生存期是由程序员决定的。如果程序员不释放,在程序结束后系统会释放掉这块内存。不同于数据结构中的堆,内存中的堆分配方式类似于链表。

自由存储区:由 malloc 等分配的内存块,和堆是十分相似的,不过它是用 free 来结束自己的生命的。

全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放),在 C++ 里面没有这个区分了,他们共同占用同一块内存区。程序结束后由系统释放。

常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改,除非通过非正当手段也可以修改,而且方法很多。程序结束后,由系统释放。

(2)C++中内存分配方式

C++中内存分配方式一般有以下三种:

1)从全局/静态存储区分配:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局变量,static变量。

2)从栈上分配:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。

3)从自由存储区上/堆上分配:亦称动态内存分配,程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。只有在堆上分配的内存才需要(也必须)我们进行释放,否则就会造成内存泄漏。

(3)栈和堆的区别

下面的一条语句中,既包含了堆分配也包含了栈分配:new分配了一块堆内存,而指针p分配的是一块栈内存,所以结果是:在栈内存中存放了一个指向一块堆内存的指针p。

int* p=new int[5];


栈和堆主要的区别有以下几点:

  1、管理方式不同;

  2、空间大小不同;

  3、能否产生碎片不同;

  4、生长方向不同;

  5、分配方式不同;

  6、分配效率不同;

  管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

  空间大小:一般来讲在 32 位系统下,堆内存可以达到4G的空间,主要受限于计算机系统中有效的虚拟内存,这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有大小限制的连续空间,例如,在VC6下面,默认的栈空间大小是1M或2M,是在编译时就确定的一个常数,如果超过栈空间,就会出现overflow(栈溢出)错误。

  碎片问题:对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在它弹出之前,在它上面的后进的栈内容已经被弹出,因而不会出现内存碎片。

  生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向,由低地址到高地址;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

  分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配;动态分配由 malloc函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

  分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

  从这里我们可以看到,堆和栈相比,由于大量 new/delete 的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP 和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

  虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

  无论是堆还是栈,都要防止越界现象的发生,因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: