C/C++動態分配記憶體
本部落格對以下幾篇部落格進行總結歸納:
1. 需要動態分配記憶體的原因
陣列是最常用的一種資料結構,其缺點是使用時必須確定陣列大小,因此會帶來一些不便:
1)需要儲存的資料大小不確定時,預先開闢的空間太小裝不下,太大則浪費空間;
2)使用的資料大部分預設儲存在棧(stack)裡,由系統管理,自動分配,自動刪除。但是stack很小,如果讀取的資料很大的話容易溢位。
3)系統要等到變數週期結束時才會釋放記憶體,當記憶體比較緊缺時,沒法立即釋放。
4)動態分配的優點:根據函式呼叫的需要,動態地分配和釋放儲存空間,大大提高了記憶體的使用效率。
2. C語言中的動態記憶體分配
1)C語言使用標準庫函式malloc()和free()來實現動態分配。
2)void *malloc(int size):申請size個位元組空間,返回值是void *(未確定型別的指標,即在申請記憶體時使用者還不知道要用來儲存什麼型別的資料,C/C++規定void *型別可以強制轉換為任意型別),所以需要做強制型別轉換為int*指標。
3)malloc的引數是記憶體大小,以位元組為單位,表示要申請多少個位元組。而在不同的系統裡面int型別佔用的位元組不一樣,而且malloc所以首先需要使用sizeof計算。
4)malloc只能分配記憶體,不能對所得的記憶體進行初始化,所以其初值為隨機的。
5)注意事項:申請記憶體空間後必須檢查是否分配成功,用完釋放,使原來指向該記憶體的指標指向
6)C程式只能用malloc/free管理動態記憶體;
3. C++中的動態記憶體分配
1)C++裡的類class裡面的建構函式是在類的例項化時自動呼叫,而不能手動呼叫,如果像C語言一樣使用malloc來為class動態分配記憶體,那麼將無法呼叫建構函式。
2)C++使用運算子new和delete來完成記憶體動態分配,使用new時自動呼叫建構函式,使用完畢用delete釋放記憶體時會自動呼叫解構函式。
3)使用new動態建立物件時,只需指定其資料型別,不必為該物件命名,例:
a) int *pi = new int;// pi 指向一個沒有初始化的int
b) int *pi=new int( );
c) string *ps=new string( ); //初始化為空字串(對於提供了預設建構函式的類型別,沒有必要對其物件進行值初始化)
d) int *pi=new int(100); //指標pi所指向的物件初始化為100
e) string *ps=new string(10,'9'); //*ps 為“9999999999”
4)new返回指定型別的指標,並且可以自動計算所需大小,例如:
a) int *p; p = new int; //返回型別為int* 型別(整數型指標),分配大小為 sizeof(int);
b) int* parr; parr = new int [100]; //返回型別為 int* 型別(整數型指標),分配大小為 sizeof(int) * 100;
c) int* p; p = (int *) malloc (sizeof(int)*128); //分配128個(可根據實際需要替換該數值)整型儲存單元,並將這128個連續的整型儲存單元的首地址儲存到指標變數p中。
d) double *pd=(double *) malloc (sizeof(double)*12); //分配12個double型儲存單元,並將首地址儲存到指標變數pd中。
5)使用delete釋放動態建立的物件,例:
a) delete pi ; // 釋放單個物件,此時pi指標變成了懸垂指標(懸垂指標指向曾經存放物件的記憶體,但該物件已經不存在了)
b) delete [ ]pi; //釋放陣列
4. 動態分配陣列記憶體
4.1 一維陣列
1)malloc-free
#include<iostream>
using namespace std;
void main(){
int *arr;
int len;//陣列大小
cin >> len;
//step1:分配記憶體
arr = (int*)malloc(len*sizeof(int));
//step2: 輸入資料
for (int i = 0; i < len; i++){
cin >> arr[i]; //注意不是cin >> *arr[i];
}
//step3:使用陣列
for (int i = 0; i < len; i++){
cout << "Your array is: "<< arr[i] << " ";
}
//step4: 釋放記憶體
free(arr);
}
2) new-delete
#include<iostream>
using namespace std;
void main(){
int len;//陣列大小
cin >> len;
//step1:分配記憶體
int *arr = new int[len];
//step2: 輸入資料
for (int i = 0; i < len; i++){
cin >> arr[i]; //注意不是cin >> *arr[i];
}
//step3:使用陣列
cout << "Your array is: " << endl;
for (int i = 0; i < len; i++){
cout << arr[i] << " ";
}
//step4: 釋放記憶體
delete []arr;
}
4.2 二維陣列
1)malloc-free
#include<iostream>
using namespace std;
void main(){
int **arr;//指標的指標
int row, col;//陣列大小
cin >> row >> col;
//step1-1:分配記憶體(行空間)
arr = (int**)malloc(row * sizeof(int*));
//step1-2:分配記憶體(列空間)
for (int i = 0; i < row; i++){
*(arr + i) = (int*)malloc(col * sizeof(int));
}
//step2: 輸入資料
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++)
cin >> arr[i][j]; //注意不是cin >> *arr[i][j];
}
//step3:使用陣列
cout << "Your array is: " << endl;
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
//step4: 釋放記憶體
for (int i = 0; i < row; i++){
free(*(arr + i));
}
}
2)new-delete
#include<iostream>
using namespace std;
void main(){
int row, col;//陣列大小
cin >> row >> col;
//step1-1:分配記憶體(行空間)
int **arr = new int*[row];//指向指標的指標
//step1-2:分配記憶體(列空間)
for (int i = 0; i < row; i++){
arr[i] = new int[col];
}
//step2: 輸入資料
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++)
cin >> arr[i][j]; //注意不是cin >> *arr[i][j];
}
//step3:使用陣列
cout << "Your array is: " << endl;
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
//step4: 釋放記憶體
delete []arr;
}
3)vector
#include<iostream>
#include<vector>
using namespace std;
void main(){
int row, col;//陣列大小
cin >> row >> col;
//step1:分配記憶體
vector<vector<int> > arr(row, vector<int>(col));
//step2: 輸入資料
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++)
cin >> arr[i][j]; //注意不是cin >> *arr[i][j];
}
//step3:使用陣列
cout << "Your array is: " << endl;
for (int i = 0; i < row; i++){
for (int j = 0; j < col; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
//step4: 無需釋放
}
5. 記憶體洩漏
1)記憶體洩漏(Memory Leak)是指程式中己動態分配的堆記憶體由於某種原因程式未釋放或無法釋放,造成系統記憶體的浪費,導致程式執行速度減慢甚至系統崩潰等嚴重後果。
2)記憶體洩漏具有隱蔽性和累積性,不易被發現。
3)在C或C++程式執行時的變數主要有三種分配方式:堆分配、棧分配、全域性和靜態分配,記憶體洩漏主要發生在堆分配中,即“配置了記憶體後,所有指向該記憶體的指標都遺失了”。
4)當開發程式中使用動態儲存變數較多和頻繁使用函式呼叫時,容易發生記憶體管理錯誤,常見的有:
a) 分配一個記憶體塊並使用其中未經初始化的內容;
b) 釋放一個記憶體塊,但繼續引用其中的內容;
c) 子函式中分配的記憶體空間在主函數出現異常中斷時、或主函式對子函式返回的資訊使用結束時,沒有對分配的記憶體進行釋放;
d) 程式實現過程中分配的臨時記憶體在程式結束時,沒有釋放臨時記憶體。
5)在記憶體中供使用者使用的記憶體空間分為三部分:
a) 程式儲存區:
b) 靜態儲存區:該區資料在程式的開始就分配好記憶體區,在整個程式執行過程中它們所佔的儲存單元是固定的,在程式結束時就釋放,因此靜態儲存區資料一般為全域性變數。
c) 動態儲存區:該區資料在程式執行過程中根據需要動態分配和動態釋放的儲存單元,動態儲存區資料有三類函式形參變數、區域性變數和函式呼叫時的現場保護與返回地址。