C++动态内存创建与内存管理学习笔记【转】

题记:学习C++的这一段时间中,在网络上获益颇多,在此感谢各位提问者与热心的解答者,尤其是那些认真的解答者。本人由于能力有限,不喜欢写任何东西发贴,即使是问题也只是搜索解决而已。最近看inside   the   c++   object   model,发现很有困难,因此觉得有必要将动态内存创建,继承与多态等方面深入理解。这就是本贴的来由,文章有点罗嗦冗长,如有感兴趣者浏览并对您有稍微一点帮助,那即是本人无限的荣幸,同时希望各位能够给出一些有益的建议(本人邮件:danielswjtu@eyou.com),在此表示感谢。
本文内容都摘自各出版物,稍微加以排列组合而已,同时也只是初步归纳。相关参考文献见(more)effective   c++,   (more)exceptional   c++,   exceptional   c++   style,c   and   pointer,   c专家编程,高质量C++   编程指南,Efficient   C++   Performance   Programming   Techniques,thinking   in   c++等。
注:发文时由于字数太多,将第四部分内存管理,第五部分一些注意事项删掉了。
------------------------------------------------------------------------------------

C++动态内存创建与内存管理学习笔记

1   内存空间逻辑组织
A   静态数据区:内存在程序启动的时候才被分配,而且可能直到程序开始执行的时候才被初始化,如函数中的静态变量就是在程序第一次执行到定义该变量的代码时才被初始化。所分配的内存在程序的整个运行期间都存在,如全局变量,static变量等。
注意:初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过void*来访问和操纵,程序结束后由系统自行释放。
B   代码区:存放函数体的二进制代码;
C   栈区:存放自动变量。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元由编译器自动释放,超出其作用域外的操作没有定义。栈内存分配运算内置于处理器的指令集中,效率很高,但分配的内存容量有限。栈存放函数的参数值,局部变量的值等。
D   堆区(自由存储区):在运行的时候调用程序(如C中的malloc或C++中的new)分配内存,可以在任何时候决定分配内存及分配的大小,用户自己负责在何时释放内存(如用free或delete)。堆中的所有东西都是匿名的,这样不能按名字访问,而只能通过指针访问。
堆需要一种策略来保存其内存是否已分配的信息。一种策略是建立一个可用块(自由存储区)的链表,每块由malloc分配的内存块都在自己的前面标明自己的大小,一般而言都经过边界对齐(alignment)处理,堆的大小受限于计算机系统中有效的虚拟内存。
堆的末端由一个称为break   的指针来标识,当堆管理器需要更多内存时,它可以通过系统调用brk和sbrk来移动break指针,一般情况下不必显式地调用brk,如果分配的内存容量很大,brk会被自动调用。用于管理内存的调用有:
Malloc和free--从堆中获得内存以及把内存返回给堆;
brk与sbrk――调整数据段的大小至一个绝对值(通过某个增量)。
注意:程序可能无法同时调用malloc()与brk(),因为如果使用了malloc,malloc希望当你调用brk与sbrk时,它具有唯一的控制权。由于sbrk向进程提供了唯一的方法将数据段内存返回给系统内核,所以如果使用了malloc,就有效地防止了程序的数据段缩小的可能性。
此处的堆与数据结构中的堆是两回事,它的分配方式类似于链表。
由于堆中的空间由用户负责分配及释放,因此需要注意内存泄漏的问题。
另外实际上堆区与自由存储区并不是一回事,详细信息见exceptional   c++条款35。
E   文字常量区(常量数据区):存放常量字符串等在编译期间就能确定的值,在程序结束后由系统自动释放。类对象不能存在于这个区域中。在本区域中所有的数据都是只读的,任何企图修改本区域数据的行为都会造成无法预料的后果。
演示内存分布的示例如下:(示例需要更改完善,同时解决其中的bug)
int   a   =   0;       //全局初始化区  
char   *p1;     //全局未初始化区  
main()  
{  
int   b;     //栈  
char   s[]   =   "abc ";     //栈
char   *p2;     //栈
char   *p3   =   "123456 ";     //123456在文字常量区,p3在栈上。(如何得到文字常量地址?)
static   int   c   =0;     //全局(静态)数据区  
p1   =   (char   *)malloc(10);  
p2   =   (char   *)malloc(20);       //分配得来的10和20字节的区域就在堆区。(如何得到?)  
strcpy(p1,   "123456 ");       //123456放在常量区,编译器可能会将它与p3所指向的 "123456 "优化成一个地方。
}

2   C中内存分配
A   malloc:原型为void*   malloc(size_t   size);在内存中的动态存储区中分配一个长度为size的空间,返回指向大小为size的内存区域首地址的void指针;用户必须决定对象的长度,即申请空间的大小。同时,malloc只是分配了一块返回值为void*的内存而不是生成一个对象。由于malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void*   转换成所需要的指针类型,malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数,因此使用sizeof是很好的方式,例如:int*   p   =   (int*)   malloc(sizeof(int)   *   length);使用malloc所分配的是一块连续的内存,同时由于编译器的实现问题(比如边界对齐等),其所分配的内存可能比所请求的多一点。
如果内存池为空,或者可用内存不能满足所请求的内存,则编译器向操作系统请求,要求得到更多的内存,并在所得到的内存上执行分配任务。若操作系统无法向malloc提供更多的内存,那么malloc返回一个NULL指针,因此有必要对malloc的返回指针进行检查。
B   realloc:原型为void   realloc(void*   ptr,   size_t   sz);用于修改一个原先已经分配内存块的大小,可以扩大也可以缩小。若扩大时,则保留原先的内存,将新添加的内存放于原先内存块的后面,新内存并未以任何方式进行初始化;若缩小时,该内存块尾部的部分内存释放,其余部分保留。
注意:若realloc的第一个参数为NULL,那么其行为与malloc一样。另外如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新块上,因此在使用realloc之后就不能再使用指向原内存的指针,而应该改用realloc所返回的新指针。
C   calloc:原型为void*   calloc(size_t   elem_num,   size_t   elem_sz);与malloc的主要区别在于calloc在返回指向内存的指针之前把它初始化为0,另一个区别是他们请求内存数量的方式不通,calloc的参数包括所需元素的数量和每个元素的字节数,由此它能够计算出总共需要分配的内存。
D   free:原型为void   free(   void*   p   );   由于指针p的类型以及它所指的内存的容量事先都是知道的,因此语句free(p)能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致未定义的结果。

转自:http://topic.csdn.net/t/20060826/17/4977703.html

原文地址:https://www.cnblogs.com/wintergrass/p/2229662.html