一、堆内存的分配
32位操作系统支持4GB内存的连续访问,但通常把内存分为两个2GB的空间,每个进程在运行时最大可以使用2GB的私有内存(0x00000000—0x7FFFFFFF)。即理论上支持如下的大数组:
char szBuffer[2*1024*1024*1024];
当然,由于在实际运行时,程序还有代码段、临时变量段、动态内存申请等,实际上是不可能用到上述那么大的数组的。
至于高端的2GB内存地址(0x80000000—0xFFFFFFFF),操作系统一般内部保留使用,即供操作系统内核代码使用。在Windows和Linux平台上,一些动态链接库(Windows的dll,Linux的so)以及ocx控件等,由于是跨进程服务的,因此一般也在高2GB内存空间运行。
二、内存分配类型
1.堆区(heap) : 也称为动态内存分配。程序在运行时用malloc或new申请的内存,由开发人员自己决定何时使用free或delete释放内存和生存期。开发人员没有收回的话,会造成内存泄漏。
2.栈区(stack) : 在执行函数时,函数内局部变量,对象的内部成员变量在栈上创建,生命周期持续到函数结束或对象析构,生命期结束时释放内存。
3.静态存储区 : 存放全局变量和静态变量,程序结束时由系统释放,分为全局初始化区和全局未初始化区。
4.文字常量区 : 存放常量字符串,程序结束时由系统释放。
5.程序代码区 : 存放函数体的二进制代码。
代码如下:
int nGlobalTest = 0; // 静态存储区-全局初始化区
char *pGlobalTest; // 静态存储区-全局未初始化区
int main()
{
int nTest; // 栈
char szTest[] = "aaa"; // 栈
char *pTest; // 栈
char *pTest1 = "test"; // "test\0"在常量区,pTest在栈
static int nStaticTest = 0; // 静态存储区-全局初始化区
pGlobalTest = (char*)malloc(10); // 10个字节区域在堆区
strcpy(pGlobalTest, "test"); // "test\0"在常量区,
system("pause");
return 0;
}
三、堆和栈的区别
1.管理方式 : 堆由开发人员释放内存,不释放容易产生内存泄漏;栈由编译器自动管理,无需我们手工控制,系统释放内存。
2.碎片问题 : 对于堆来讲,频繁的内存分配会造成内存空间的不连续,造成大量的内存碎片,使程序效率降低;对于栈来说,不会产生碎片问题,因为栈是先进后出的队列,不会出现一个内存块从栈中间弹出。
3.生长方向 : 对于堆,生长方向是向上的,朝着内存地址增加的方向生长;对于栈,生长方向是向下的,朝着内存地址减小的方向生长。
4.分配方式 : 堆只有动态分配,没有静态分配;栈分静态分配和动态分配。
5.分配效率 : 堆则是 C/C++ 函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间,就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回;栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。显然,堆的效率比栈要低得多。
四、使用内存遇到的问题
1.动态内存分配失败
分配内存是,堆资源不足,分配失败,返回一个空指针,没有对返回指针进行空判断。
代码如下:
int* p = new int(1);
if (p == NULL)
return;
2.指针删除与堆空间释放
删除一个指针p,实际上是删除了p所指的目标,释放了所占的堆空间,不是删除p本身。释放 堆空间后,p成了空悬指针,不能再通过P使用该空间,在重新给P赋值之前,也不能再直接使用p。
3.内存泄漏和重复释放
new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
4.动态分配的变量或对象的生命期
无名对象的生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错。
五、使用内存可能引起的BUG
1.改写错误
越过数组边界写入数据,在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构。
如:char* p = (cahr*)malloc(10);
p[-1] = 0;
p[10] = 0;
2.错误使用指针
在指针赋值之前就用它来引用内存。
向库函数传送一个错误指针。
对指针进行释放之后再访问它的内容(可以先free掉指针,再将它设置为空)。
3.指针释放引起的错误
同一块内存释放两次
释放一块未曾malloc分配的内存
释放仍在使用中的内存
释放一个无效的指针