C++内存管理学习笔记(1) (一)

2014-11-24 00:56:17 · 作者: · 浏览: 4

/****************************************************************/

/* 学习是合作和分享式的!

/* Author:Atlas
/* 转载请注明本文出处:

/****************************************************************/

C++内存管理
1.内存分配方式
在讲解内存分配之前,首先,要了解程序在内存中都有什么区域,然后在详细分析各种分配方式。

1.1 C语言C++内存分配区
下面的三张图,图1图2是一种比较详细的C语言的内存区域分法。图3是典型的C++内存分布图,简单易懂;以下内存分配图,区别就是图1和2则分为初始化和未初始化静态变量区,图3中是全局变量区。

C语言(图1和图2):(由地地址到高地址)

a)正文段:用来存放程序执行代码。通常,正文段是可共享的。另外,正文段常常是只读的,一次防止程序由于意外修改其自身。

b)初始化数据段:用来存放程序中已初始化的全局变量。数据段属于静态内存分配。

c)非初始化数据段:通常称为BSS段, 用来存放程序中未初始化的全局变量。BSS是英文Block Started by Symbol(由符号开始的块)的简称。BSS段属于静态内存分配。 在程序开始执行之前,内核将此段中的数据初始化为0或者空指针。

d)堆:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上 (堆被扩张)/释放的内存从堆中被剔除(堆被缩减)。

e)栈:栈又称堆栈, 存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈 的先进先出特点,所以栈特别方便用来保存/恢复调用现场。

\ \

图1 典型C语言内存分布区域 (UNIX高级环境编程) 图2 典型C语言内存分布区域

C++(图3):

根据《C++内存管理技术内幕》一书,在C++中,内存分成5个区,他们分别是堆,栈,自由存续区,全局/静态存续区,常量存续区。

a) 栈:内存由编译器在需要时自动分配和释放。通常用来存储局部变量和函数参数。栈运算分配内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

b) 堆:内存使用new进行分配使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。

c) 自由存储区:使用malloc进行分配,使用free进行回收。和堆类似。

d) 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。

e) 常量存储区:存储常量,不允许被修改。

这里,在一些资料中是这样定义C++内存分配的,可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。

a)静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

b)栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

c)堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

\

图3 典型c++内存区域

总结:C++与C语言的内存分配存在一些不同,但是整体上就一致的,不会影响程序分析。就C++而言,不管是5部分还是3大部分,只是分法不一致,将5部分中的c)d)e)合在一起则是3部分的a)。

1.2区分堆、栈、静态存储区
我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。

(1)静态存储区与栈区


1: char* p = “Hello World1”; 2: char a[] = “Hello World2”; 3: p[2] = ‘A’; 4: a[2] = ‘A’; 5: char* p1 = “Hello World1;”
这个程序是有错误的,错误发生在p[2] = ‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。但是,数据“Hello World1”和数据“Hello World2”是存储于不同的区域的。

因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。因为指针变量p仅仅能够存储某个存储空间的地址,数据“Hello World1”为字符串常量,所以存储在静态存储区。虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。换句话说,在数据区只保留一份相同的数据。

(2)堆与栈区别

我们先通过例子1来直观的说明下栈与堆内存的区别,然后在细致分析例子2中的情况。

例子1:
1: void fn(){ 2: int* p = new int[5]; 3: }
看到new,首先应该想到,我们分配了一块堆内存,那么指针p呢 它分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中。

注意:这里为了简单并没有释放内存,那么该怎么去释放呢 是deletep么 NO,错了,应该是delete [ ] p,这是告诉编译器:删除的是一个数组。


例子2:

1: int a = 0; //全局初始化区 2: char *p1; //全局未初始化区 3: main() 4: { 5: int b; //栈 6: char s[] = "abc"; //栈 7: char *p2; //栈 8: char *p3 = "123456"; // 123456\0在常量区,p3在栈上。 9: static int c =0; //全局(静态)初始化区 10: p1 = (char *)malloc(10); 11: p2 = (char *)malloc(20); 12: //分配得来得10和20字节的区域就在堆区