到目前爲止,我們的程序中我們只用了聲明變量、數組和其他對象(objects)所必需的內存空間,這些內存空間的大小都在程序執行之前就已經確定了。但如果我們需要內存大小爲一個變量,其數值只有在程序運行時 (runtime)才能確定,例如有些情況下我們需要根據用戶輸入來決定必需的內存空間,那麼我們該怎麼辦呢?
答案是動態內存分配(dynamic memory),爲此C++ 集成了操作符new 和delete。
操作符 new 和 delete 是C++執行指令。本節後面將會介紹這些操作符在C中的等價命令。
操作符new 和new[ ]
操作符new的存在是爲了要求動態內存。new 後面跟一個數據類型,並跟一對可選的方括號[ ]裏面爲要求的元素數。它返回一個指向內存塊開始位置的指針。其形式爲:
pointer = new type
或者
pointer = new type [elements]
第一個表達式用來給一個單元素的數據類型分配內存。第二個表達式用來給一個數組分配內存。
例如:
bobby = new int [5];
在這個例子裏,操作系統分配了可存儲5個整型int元素的內存空間,返回指向這塊空間開始位置的指針並將它賦給bobby。因此,現在bobby 指向一塊可存儲5個整型元素的合法的內存空間,如下圖所示。
你可能會問我們剛纔所作的給指針分配內存空間與定義一個普通的數組有什麼不同。最重要的不同是,數組的長度必須是一個常量,這就將它的大小在程序執行之前的設計階段就被決定了。而採用動態內存分配,數組的長度可以常量或變量,其值可以在程序執行過程中再確定。
動態內存分配通常由操作系統控制,在多任務的環境中,它可以被多個應用(applications)共享,因此內存有可能被用光。如果這種情況發生,操作系統將不能在遇到操作符new 時分配所需的內存,一個無效指針(null pointer)將被返回。因此,我們建議在使用new之後總是檢查返回的指針是否爲空(null),如下例所示:
bobby = new int [5];
if (bobby == NULL) {
// error assigning memory. Take measures.
};
刪除操作符delete
既然動態分配的內存只是在程序運行的某一具體階段纔有用,那麼一旦它不再被需要時就應該被釋放,以便給後面的內存申請使用。操作符delete 因此而產生,它的形式是:
delete pointer;
或
delete [ ] pointer;
第一種表達形式用來刪除給單個元素分配的內存,第二種表達形式用來刪除多元素(數組)的內存分配。在多數編譯器中兩種表達式等價,使用沒有區別, 雖然它們實際上是兩種不同的操作,需要考慮操作符重載overloading (我們在後面的section 4.2節中將會看到)。
// rememb-o-matic #include ‹iostream.h› #include ‹stdlib.h› int main ( ) { char input [100]; int i,n; long * l; cout << "How many numbers do you want to type in? "; cin.getline (input,100); i=atoi (input); l= new long[i]; if (l == NULL) exit (1); for (n=0; n<i; n++) { cout << "Enter number: "; cin.getline (input,100); l[n]=atol (input); } cout << "You have entered: "; for (n=0; n<i; n++) { cout << l[n] << ", "; delete[] l; return 0; } |
How many numbers do you want to type in? 5 Enter number : 75 Enter number : 436 Enter number : 1067 Enter number : 8 Enter number : 32 You have entered: 75, 436, 1067, 8, 32, |
這個簡單的例子可以記下用戶想輸入的任意多個數字,它的實現歸功於我們動態地向系統申請用戶要輸入的數字所需的空間。
NULL是C++庫中定義的一個常量,專門設計用來指代空指針的。如果這個常量沒有被預先定義,你可以自己定以它爲0:
#define NULL 0
在檢查指針的時候,0和NULL並沒有區別。但用NULL 來表示空指針更爲常用,並且更易懂。原因是指針很少被用來比較大小或被直接賦予一個除0以外的數字常量,使用NULL,這一賦值行爲就被符號化了。
ANSI-C 中的動態內存管理Dynamic memory in ANSI-C
操作符new 和delete 僅在C++中有效,而在C語言中沒有。在C語言中,爲了動態分配內存,我們必須求助於函數庫stdlib.h。因爲該函數庫在C++中仍然有效,並且在一些現存的程序仍然使用,所以我們下面將學習一些關於這個函數庫中的函數用法。
函數malloc
這是給指針動態分配內存的通用函數。它的原型是:
void * malloc (size_t nbytes);
其中nbytes 是我們想要給指針分配的內存字節數。這個函數返回一個void*類型的指針,因此我們需要用類型轉換(type cast)來把它轉換成目標指針所需要的數據類型,例如:
ronny = (char *) malloc (10);
這個例子將一個指向10個字節可用空間的指針賦給ronny。當我們想給一組除char 以外的類型(不是1字節長度的)的數值分配內存的時候,我們需要用元素數乘以每個元素的長度來確定所需內存的大小。幸運的是我們有操作符sizeof,它可以返回一個具體數據類型的長度。
bobby = (int *) malloc (5 * sizeof(int));
這一小段代碼將一個指向可存儲5個int型整數的內存塊的指針賦給bobby,它的實際長度可能是 2,4或更多字節數,取決於程序是在什麼操作系統下被編譯的。
函數calloc
calloc 與malloc 在操作上非常相似,他們主要的區別是在原型上:
void * calloc (size_t nelements, size_t size);
因爲它接收2個參數而不是1個。這兩個參數相乘被用來計算所需內存塊的總長度。通常第一個參數(nelements)是元素的個數,第二個參數 (size) 被用來表示每個元素的長度。例如,我們可以像下面這樣用calloc定義bobby:
bobby = (int *) calloc (5, sizeof(int));
malloc 和calloc的另一點不同在於calloc 會將所有的元素初始化爲0。
函數realloc
它被用來改變已經被分配給一個指針的內存的長度。
void * realloc (void * pointer, size_t size);
參數pointer 用來傳遞一個已經被分配內存的指針或一個空指針,而參數size 用來指明新的內存長度。這個函數給指針分配size 字節的內存。這個函數可能需要改變內存塊的地址以便能夠分配足夠的內存來滿足新的長度要求。在這種情況下,指針當前所指的內存中的數據內容將會被拷貝到新的地址中,以保證現存數據不會丟失。函數返回新的指針地址。如果新的內存尺寸不能夠被滿足,函數將會返回一個空指針,但原來參數中的指針pointer 及其內容保持不變。
函數 free
這個函數用來釋放被前面malloc, calloc 或realloc所分配的內存塊。
void free (void * pointer);
注意:這個函數只能被用來釋放由函數malloc, calloc 和realloc所分配的空間。
你可以參考C++ reference for cstdlib獲得更多關於這些函數的信息。