前面我们讲过,分配了一个内存块但忘记了释放它这是一个严重的错误,这样的内存块要等到程序执行结束时才会被释放掉(不懂的话就看栈的相关介绍)。
如果程序运行很长时间(例如在服务器上,注意不是所有的操作系统都像windows一样每天都要重启)并且在不停地申请新内存块,忘记释放那些已经不再有用的老内存块将迟早会把内存消耗殆尽,直接导致后面的new操作无法执行甚至是崩溃。
这样的编程漏洞我们称之为内存泄漏(memory leak)。
new语句所返回的地址是访问这个内存块的唯一线索,同时也是delete语句用来把这个内存块归还给内存池的唯一线索。
看下这个例子:
int *x;
x = new int[1000];
delete[] x;
x = NULL;
这意味着如果这个地址值(保存在x里)丢失了,就会发生内存泄漏问题。
地址值会因为很多原因而丢失
第一种情况,因为一个地址变量被无意中改写,例如:
int *x;
x = new int[3000];
x = new int[4000];
delete[] x;
x = NULL;
这样上面那个地址就没了,就是造成内存泄漏。
第二种情况,用来保存内存块地址的指针变量作用域问题,例如:
void foo()
{
My Class *x;
x = new MyClass();
}
当foo函数结束时,指针变量x将超出它的作用域,这意味着它将不复存在,它的值当然就会丢失。
有两种方法可以解决解决该漏洞
1. 在return语句之前的某个地方插入一条delete x语句,如:
void foo()
{
MyClass *x;
x = new MyClass();
delete x;
x = NULL;
return 0;
}
如果没有return就在结束之前插入,一个意思。
2. 让函数把内存块的地址返回给它的调用者,如:
void foo()
{
MyClass *x;
x = new MyClass();
return x;
内存作用域
变量都有一个作用域:规定了它可以在程序的哪些部分使用。
这个作用域通常就是对它们做出声明和定义的函数的函数体,如main函数或某个子函数。
如果被定义在任何一个函数的外部,变量将拥有全局作用域,这意味它们可以在整个程序中的所有函数里使用。
不过应该避免使用全局变量,因为它们往往会让代码变得难以调试和容易出错。
动态内存不存在作用域的问题,一旦被分配,内存块可以在程序的任何地方使用。
因为动态内存没有作用域,所以必须由程序员来跟踪它们的使用情况,并在不再需要用到它们的时候把它们及时归还给系统。
这里需要特别注意的是,虽然动态分配的内存块没有作用域,但用来保存其地址的指针变量是受作用域影响的。就是上面讲的上面的内存泄漏问题。