C++内存分配及管理.pdf





《C++内存分配及管理.pdf》由会员分享,可在线阅读,更多相关《C++内存分配及管理.pdf(14页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、在 C+中,内存分成 5 个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。堆,就是那些由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。自由存储区,就是那些由 malloc 等分配的内存块,他和堆是十分相似的,不过它是用 free 来结束自己的生命的.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变
2、量又分为初始化的和未初始化的,在 C+里面没有这个区分了,他们共同占用同一块内存区。常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在const 的思考一文中,我给出了 6 种方法)明确区分堆与栈 在 bbs 上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀.首先,我们举一个例子:void f()int p=new int5;这条短短的一句话就包含了堆与栈,看到 new,我们首先就应该想到,我们分配了一块堆内存,那么指针 p 呢?他分配的是一块栈内存,所以这句话的
3、意思就是:在栈内存中存放了一个指向一块堆内存的指针 p。在程序会先确定在堆中分配内存的大小,然后调用 operator new 分配内存,然后返回这块内存的首地址,放入栈中,他在 VC6 下的汇编代码如下:00401028 push 14h 0040102A call operator new(00401060)0040102F add esp,4 00401032 mov dword ptr ebp8,eax 00401035 mov eax,dword ptr ebp-8 00401038 mov dword ptr ebp4,eax 这里,我们为了简单并没有释放内存,那么该怎么去释放呢?
4、是 delete p 么?澳,错了,应该是 delete p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的 Cookie 信息去进行释放内存的工作。好了,我们回到我们的主题:堆和栈究竟有什么区别?主要的区别由以下几点:1、管理方式不同;2、空间大小不同;3、能否产生碎片不同;4、生长方向不同;5、分配方式不同;6、分配效率不同;管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生 memory leak.空间大小:一般来讲在 32 位系统下,堆内存可以达到 4G 的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,
5、一般都是有一定的空间大小的,例如,在 VC6 下面,默认的栈空间大小是 1M(好像是,记不清楚了)。当然,我们可以修改:打开工程,依次操作菜单如下:Project-SettingLink,在 Category 中选中 Output,然后在 Reserve 中设定堆栈的最大值和 commit.注意:reserve 最小值为 4Byte;commit 是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。碎片问题:对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先
6、进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长.分配方式:堆都是动态分配的,没有静态分配的堆。栈有 2 种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配.动态分配由 alloca 函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。分配效率:栈是机器系统提供的数据结构
7、,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是 C/C+函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多.从这里我们可以看到,堆和栈相比,由于大量 new/delete 的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用
8、户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP 和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候 debug 可是相当困难的:)1、什么是 con
9、st?常类型是指使用类型修饰符 const 说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:)2、为什么引入 const?const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。3、cons 有什么主要的作用?(1)可以定义 const 常量,具有不可变性。例如:const int Max=100;int ArrayMax;(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。例如:void f(const int i)。.。编译器就会知道 i 是一个常量,不允许修改;(3)可以避免意义模糊的数字出现,同样可以很方
10、便地进行参数的调整和修改。同宏定义一样,可以做到不变则已,一变都变!如(1)中,如果想修改 Max 的内容,只需要:const int Max=you want;即可!(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。还是上面的例子,如果在函数体内修改了 i,编译器就会报错;例如:void f(const int i)i=10;/error!(5)为函数重载提供了一个参考。class A .void f(int i)。.file:/一个函数 void f(int i)const.。.。file:/上一个函数的重载 .。.。.;(6)可以节省空间,避免不必要的内存分配。例如:defi
11、ne PI 3。14159 file:/常量宏 const doulbe Pi=3.14159;file:/此时并未将 Pi 放入 ROM 中 。.double i=Pi;file:/此时为 Pi 分配内存,以后不再分配!double I=PI;file:/编译期间进行宏替换,分配内存 double j=Pi;file:/没有内存分配 double J=PI;file:/再进行宏替换,又一次分配内存!const 定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象define一样给出的是立即数,所以,const 定义的常量在程序运行过程中只有一份拷贝,而define定义的常量在内存中有
12、若干个拷贝。(7)提高了效率。编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。3、如何使用 const?(1)修饰一般常量 一般常量是指简单类型的常量。这种常量在定义时,修饰符 const 可以用在类型说明符前,也可以用在类型说明符后.例如:int const x=2;或 const int x=2;(2)修饰常数组 定义或说明一个常数组可采用如下格式:int const a5=1,2,3,4,5;const int a5=1,2,3,4,5;(3)修饰常对象 常对象是指对象常量,定义格式
13、如下:class A;const A a;A const a;定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符 const 可以放在类名后面,也可以放在类名前面。(4)修饰常指针 const int A;file:/const 修饰指向的对象,A 可变,A 指向的对象不可变 int const A;file:/const 修饰指向的对象,A 可变,A 指向的对象不可变 int*const A;file:/const 修饰指针 A,A 不可变,A 指向的对象可变 const int const A;file:/指针 A 和 A 指向的对象都不可变 (5)修饰常引用 使用 const
14、 修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:const double v;(6)修饰函数的常参数 const 修饰符也可以修饰函数的传递参数,格式如下:void Fun(const int Var);告诉编译器 Var 在函数体中的无法改变,从而防止了使用者的一些无意的或错误的修改。(7)修饰函数的返回值:const 修饰符也可以修饰函数的返回值,是返回值不可被改变,格式如下:const int Fun1();const MyClass Fun2();(8)修饰类的成员函数:const 修饰符也可以修饰类的成员函数,格式如下:class Clas
15、sName public:int Fun()const;。.;这样,在调用函数 Fun 时就不能修改类里面的数据 (9)在另一连接文件中引用 const 常量 extern const int i;file:/正确的引用 extern const int j=10;file:/错误!常量不可以被再次赋值 另外,还要注意,常量必须初始化!例如:const int i=5;4、几点值得讨论的地方:(1)const 究竟意味着什么?说了这么多,你认为 const 意味着什么?一种修饰符?接口抽象?一种新类型?也许都是,在Stroustup最初引入这个关键字时,只是为对象放入ROM做出了一种可能,对于
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 内存 分配 管理

限制150内