C++堆內存空間詳解(釋放內存、內存泄露)
2020-05-23 14:27:05
供稿:網友
家里要來客人了,我們要給客人們泡茶。如果規定只能在確定來幾位客人之前就把茶泡好,這就會顯得很尷尬:茶泡多了會造成浪費,泡少了怕怠慢了客人。所以,最好的方法就是等知道了來幾位客人再泡茶,來幾位客人就泡幾杯茶。
然而,我們在使用數組的時候也會面臨這種尷尬:數組的存儲空間必須在程序運行前申請,即數組的大小在編譯前必須是已知的常量表達式。空間申請得太大會造成浪費,空間申請得太小會造成數據溢出而使得程序異常。所以,為了解決這個問題,我們需要能夠在程序運行時根據實際情況申請內存空間。
在C++中,允許我們在程序運行時根據自己的需要申請一定的內存空間,我們把它稱為堆內存(Heap)空間。
如何獲得堆內存空間
我們用操作符new來申請堆內存空間,其語法格式為:
new 數據類型[表達式];
其中,表達式可以是一個整型正常量,也可以是一個有確定值的整型正變量,其作用類似聲明數組時的元素個數,所以兩旁的中括號不可省略。如果我們只申請一個變量的空間,則該表達式可以被省略,即寫作:
new 數據類型;
使用new操作符后,會返回一個對應數據類型的指針,該指針指向了空間的首元素。所以,我們在使用new操作符之前需要聲明一個對應類型的指針,來接受它的返回值。如下面程序段:
int *iptr;//聲明一個指針
int size;//聲明整型變量,用于輸入申請空間的大小
cin >>size;//輸入一個正整數
iptr=new int[size];//申請堆內存空間,接受new的返回值
我們又知道,數組名和指向數組首元素的指針是等價的。所以,對于iptr我們可以認為是一個整型數組。于是,我們實現了在程序運行時,根據實際情況來申請內存空間。
釋放內存
當一個程序運行完畢之后,它所使用的數據就不再需要。由于內存是有限的,所以它原來占據的內存空間也應該釋放給別的程序使用。對于普通變量和數組,在程序結束運行以后,系統會自動將它們的空間回收。然而對于我們自己分配的堆內存空間,大多數系統都不會將它們回收。如果我們不人為地對它們進行回收,只“借”不“還”,那么系統資源就會枯竭,電腦的運行速度就會越來越慢,直至整個系統崩潰。我們把這種只申請空間不釋放空間的情況稱為內存泄露(Memory Leak)。
確認申請的堆內存空間不再使用后,我們用delete操作符來釋放堆內存空間,其語法格式為:
delete [] 指向堆內存首元素的指針;
如果申請的是一個堆內存變量,則delete后的[]可以省略;如果申請的是一個堆內存數組,則該[]不能省略,否則還是會出現內存泄露。另外,我們也不難發現,delete后的指針就是通過new獲得的指針,如果該指針的數據被修改或丟失,也可能造成內存泄露。
下面我們來看一段程序,實踐堆內存的申請和回收:(程序8.7)
#include "iostream.h"
int main()
{
int size;
float sum=0;
int *heapArray;
cout <<"請輸入元素個數:";
cin >>size;
heapArray=new int[size];
cout <<"請輸入各元素:" <<endl;
for (int i=0;i<size;i++)
{
cin >>heapArray[i];
sum=sum+heapArray[i];
}
cout <<"這些數的平均值為" <<sum/size <<endl;
delete [] heapArray;
return 0;
}
運行結果:
請輸入元素個數:5
請輸入各元素:
1 3 4 6 8
這些數的平均值為4.4
可見,申請的堆內存數組在使用上和一般的數組并無差異。我們需要記住的是,申請了資源用完了就一定要釋放,這是程序員的好習慣,也是一種責任。
那么,我們能不能來申請一個二維的堆內存數組呢?事實上,new 數據類型[表達式][表達式]的寫法是不允許的。所以,如果有需要,最簡單的方法就是用一個一維數組來代替一個二維數組。這就是上一章最后一小段文字的意義所在。