在 C++ 中利用堆疊與堆記憶體分配
本文將解釋如何在 C++ 中利用堆疊與堆記憶體分配的幾種方法。
C++ 中堆疊與堆記憶體的區別
當我們想討論記憶體的概念時,最好從執行最常見使用者程式的系統的角度來考慮。大多數使用者程式執行在作業系統環境中,它為我們管理硬體資源並處理使用者程式無法處理的各種過於複雜或低效的任務。其中一項任務是直接管理硬體記憶體。因此,幾乎所有的操作系統都提供了特殊的結構和功能來與硬體記憶體進行互動。作業系統提供的記憶體結構中的兩個常見概念是堆疊和堆。
堆疊是為系統中每個正在執行的程式保留的記憶體區域,它以 LIFO 方式執行。即,當程式開始執行 main
函式時,後者獲取其堆疊幀(堆疊記憶體的子集),其中自動儲存區域性變數和函式呼叫返回地址。一旦 main
呼叫另一個函式,就會在前一個堆疊幀之後以連續的方式建立一個新的堆疊幀。最新的堆疊幀將儲存相應函式的本地物件,當它返回時,這些記憶體未被佔用。
請注意,預設情況下,堆疊大小在大多數系統上是固定的,但如果使用者有特殊需求,可以進行一定程度的自定義。堆疊記憶體的大小限制使其適用於小型且主要是臨時物件。例如,Linux 作業系統中使用者程式的預設堆疊大小為 8MB。它可能比程式可能需要處理的單個 JPEG 照片小,因此使用者必須謹慎使用此空間。以下程式碼片段中宣告的變數都儲存在堆疊記憶體中。作為一般規則,如果每個區域性變數沒有像 static
或 volatile
這樣的特殊說明符,就會在堆疊上分配。
#include <iostream>
using std::cout;
using std::endl;
int main() {
int var1;
int var2 = 123;
int arr1[4] = {1, 2, 3, 4};
int var3 = var2;
cout << var1 << endl;
cout << var2 << endl;
cout << var3 << endl;
return EXIT_SUCCESS;
}
輸出:
0
123
123
另一方面,有一個稱為 - 堆(也稱為空閒儲存
)的記憶體區域,可以在其中儲存大物件並在執行時由程式設計師手動進行分配。這兩個特性使堆記憶體本質上是動態的,因為它的大小不需要在編譯時或程式執行期間的任何時刻確定。該程式可以呼叫特殊函式並從作業系統請求分配。請注意,從程式的角度來看,堆記憶體似乎是無限的,因為它不僅限於呼叫另一個分配函式來請求更多記憶體。雖然,作業系統管理所有正在執行的程序的記憶體;當沒有更多可用的實體記憶體時,它可能會拒絕新的分配。
作業系統中的記憶體系統非常複雜,需要了解各種特定於作業系統/硬體的概念,因此我們在本主題中僅涵蓋堆記憶體和堆疊記憶體的最低限度。C++ 語言中的堆記憶體手動管理可以使用 new
/delete
操作符或 malloc
/free
函式來完成。請注意,這些函式以類似的方式工作,使用者通常指定要分配的位元組數,並返回分配相同數量記憶體的地址。因此,程式設計師可以根據需要對給定的儲存區域進行操作。
下一個程式碼示例演示了在堆記憶體上分配不同物件的幾種情況。手動記憶體管理的一項重要功能是在不再需要分配的記憶體區域時將其返回給作業系統。後一個操作是使用對應於它們的分配對應物的 delete
/free
呼叫完成的。如果程式不釋放不需要的記憶體,則存在作業系統記憶體不足的風險,從而導致程式被終止。但是請注意,前面的問題主要發生在長時間執行的程式中,它們的特點是記憶體洩漏錯誤。
#include <iostream>
using std::cout;
using std::endl;
int main() {
auto var4 = new int;
cout << var4 << endl;
int *arr2 = new int[4];
auto arr3 = new int[4];
cout << arr2 << endl;
cout << arr3 << endl;
delete var4;
delete[] arr2;
delete[] arr3;
return EXIT_SUCCESS;
}