二、内存的分配及内存错误类型
1.内存的分配方式
内存分配方式有三种:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
2.new、delete/malloc、free
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数,而是C++的运算符。
把new和delete与malloc和free混在一起用也是个坏想法。对一个用new获取来的指针调用free,或者对一个用malloc获取来的指针调用delete,理论上是可以的,但其后果是不可预测的。大家都知道“不可预测”的意思:它可能在开发阶段工作良好,在测试阶段工作良好,但也可能会最后在你最重要的客户的脸上爆炸。
3.delete和free对指针做了什么
大家都知道delete和free都是释放内存的,但是释放的内存是谁的内存?是指针变量在内存中的空间还是指针变量指向的内存?这个大家一定要弄清楚,答案是后者。
char *p = (char *) malloc(100);
strcpy(p, “hello”);
free(p); // p 所指的内存被释放,但是p所指的地址仍然不变
// 释放的概念只是一种对该段内存unlock,而并不是删掉p中所存储的地址值
…
if(p != NULL) // 没有起到防错作用,因为p是有值的
{
strcpy(p, “world”); // 出错
}
这里有很重要的两个原则:
(1)指针消亡了,并不表示它所指的内存会被自动释放。
(2)内存被释放了,并不表示指针会消亡或者成了NULL指针
因此,一旦我们用了new或malloc申请了空间,切记要用delete或free进行释放之;一旦用delete或free将动态申请的内存释放了,一定不要忘了将指针变量赋值为Null。
PS:如果申请的空间是一个动态数组,用delete释放的时候切记要在delete后面加上[],否则delete只会释放数组的第一个元素的空间,后面的都不会被释放掉,造成内存泄露。
4.动态内存不释放会怎么样
很多初学指针的人在用new/malloc申请空间后经常会忘记将空间delete/free,而且从效果上来看也没什么,甚至会认为系统会自动释放,看下例:
void main(void)
{
float *p = NULL;
while(TRUE)
{
p = new float[1000000];
cout << “eat more memory!” << endl;
}
}
这段程序运行一下,明显是一个无限循环,典型的只有new而没有delete的情况,找一个内存监控软件查看一下内存损失情况就明白了。如果强行Ctrl+C终止程序,然后关掉VC会怎么样?申请的空间会释放吗?答案是不会,一定不会!
有时我们为确保安全性喜欢将程序这样写:
void main(void)
{
float *p = NULL;
while(TRUE)
{
p = new float[1000000];
cout << “eat memory” << endl;
if(p==NULL)
exit(1);
}
}
看上去很好,即申请不成功(即内存被全部吃掉)就会调用exit(1)而将该“坏”程序杀掉,这样有用吗?不一定。
在32位系统中,操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。所以,我们在写程序动态申请内存的时候一定要注意这方面的问题。