内存分配方式
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈 -- 由编译器自动分配释放
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
是由编译器在需要时自动分配,不需要时自动清除的变量存储区。通常存放局部变量、函数参数等。
堆 -- 一般由程序员分配释放
就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序(程序员)去控制,一般一个new就要对应一个delete,一个new[]与一个delete[]对应。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区
就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区
全局变量和静态变量被分配到同一块内存中(在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。)
常量存储区
这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
(注意:堆和自由存储区其实不过是同一块区域,new底层实现代码中调用了malloc,new可以看成是malloc智能化的高级版本)
栈和堆的讨论
堆: 操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删 除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码 中的delete语句才能正确的释放本内存空间。我们常说的内存泄露,最常见的就是堆泄露(还有资源泄露),它是指程序在运行中出现泄露,如果程序被关闭掉的话,操作系统会帮助释放泄露的内存。
栈: 在函数调用时第一个进栈的主函数中的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数 的各个参数,在大多数的C编译器中,参数是由右往左入栈,然后是函数中的局部变量。
管理方式:
- 堆中资源由程序员控制(容易产生memory leak)。
- 栈资源由编译器自动管理,无需手工控制。
系统响应:
- 对于堆,应知道系统有一个记录空闲内存地址的链表,当系统收到程序申请时,遍历该链表,寻找第一个空间大于申请空间的堆结点,删除空闲结点链表中的该结点,并将该结点空间分配给程序(大多数系统会在这块内存空间首地址记录本次分配的大小,这样delete才能正确释放本内存空间,另外系统会将多余的部分重新放入空闲链表中)。
- 对于栈,只要栈的剩余空间大于所申请空间,系统为程序提供内存,否则报异常提示栈溢出。
空间大小:
- 堆是不连续的内存区域(因为系统是用链表来存储空闲内存地址,自然不是连续的),堆大小受限于计算机系统中有效的虚拟内存(32bit系统理论上是4G),所以堆的空间比较灵活,比较大。
- 栈是一块连续的内存区域,大小是操作系统预定好的,windows下栈大小是2M(也有是1M,在编译时确定,VC中可设置)。
碎片问题:
- 对于堆,频繁的new/delete会造成大量碎片,使程序效率降低。
- 对于栈,它是一个先进后出的队列,进出一一对应,不会产生碎片。
生长方向:
- 堆向上,向高地址方向增长。
- 栈向下,向低地址方向增长。
分配方式:
- 堆都是动态分配(没有静态分配的堆)。
- 栈有静态分配和动态分配,静态分配由编译器完成(如局部变量分配),动态分配由alloca函数分配,但栈的动态分配的资源由编译器进行释放,无需程序员实现。
分配效率:
- 堆由C/C++函数库提供,机制很复杂。所以堆的效率比栈低很多。
- 栈是极其系统提供的数据结构,计算机在底层对栈提供支持,分配专门寄存器存放栈地址,栈操作有专门指令。
“野指针”成因主要有两种:
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
(2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
malloc/free与new/delete的区别:
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。 它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。
动态分配和释放内存
C++ 提供了一种“动态内存分配”机制,使得程序可以在运行期间,根据实际需要,要求操作系统临时分配一片内存空间用于存放数据。此种内存分配是在程序运行中进行的,而不是在编译时就确定的,因此称为“动态内存分配”。
在 C++ 中,通过 new 运算符来实现动态内存分配。
new 运算符的第一种用法:
T \*p = new T;
其中,T 是任意类型名,p 是类型为 T\* 的[指针]
**new 运算符还有第二种用法,用来动态分配一个任意大小的数组:**
T *p =new T[N];
//其中,T 是任意类型名,p 是类型为 T* 的指针,N 代表“元素个数”,可以是任何值为正整数的表达式,表达式中可以包含变量、函数调用等。这样的语句动态分配出 N × sizeof(T) 个字节的内存空间,这片空间的起始地址被赋值给 p。
程序从操作系统动态分配所得的内存空间在使用完后应该释放,交还操作系统,以便操作系统将这片内存空间分配给其他程序使用。C++ 提供 delete 运算符,用以释放动态分配的内存空间。delete 运算符的基本用法如下:
delete p;
如果是用 new 的第二种用法分配的内存空间,即动态分配了一个数组,那么释放该数组时,应以如下形式使用 delete 运算符:
delete\[\] p;
牢记,用 new 运算符动态分配的内存空间,一定要用 delete 运算符释放。否则,即便程序运行结束,这部分内存空间仍然不会被操作系统收回,从而成为被白白浪费掉的内存垃圾。这种现象也称为“内存泄露”。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。