解決上一篇的兩個問題之前,我們先來認識動態記憶體吧!
特色:
既然動態記憶體不是由系統自動釋放的,那我們就可以將函式中的變數(未來還會使用者)改用動態配置來宣告。這就是為什麼上一篇提到的問題(二)可以用動態配置來改善。
int
變數需要 4 bytes。template <classname T>
T *ptr = new T; // 配置使用關鍵字 new
delete ptr; // 釋放該指標所領導的記憶體
這個部分就是用來解答上一篇遇到的問題「不確定陣列大小」。
原理與動態配置一般變數相同,只有一點區別。
n
的陣列」,這樣系統才會配置 new T[n]
。ptr
,欲隨機存取陣列內容時,語法是 ptr[idx]
(注意索引值大小:0 <= idx < n)。delete [] ptr
。template <classname T>
int n;
std::cin >> n;
T *ptr = new T[n]; // 配置使用關鍵字 new
/* do something with ptr[idx] */
delete [] ptr; // 釋放該指標所領導的記憶體
既然一維陣列是用一般指標(T *
)儲存,那麼二維陣列就是用雙重指標(T **
)儲存。
想像二維陣列是許多一維陣列的集合,因此我們的做法大致可以歸納為以下幾個步驟:
new T*[row]
。ptr[i] = new T[col]
。ptr[i][j]
template <classname T>
int row, col;
std::cin >> row >> col;
T **ptr = new T*[row]; // 配置第一層(row)
for (int i=0; i<row; i++)
ptr[i] = new T[col]; // 配置第二層(col)
/* do something with ptr[rowIdx][colIdx] */
for (int i=0; i<row; i++)
delete [] ptr[i]; // 釋放第二層
delete [] ptr; // 釋放第一層
上述的做法確實可以配置二維陣列,但是當你印出所有記憶體位址,你將發現他們不完全連續!
那就來改善它吧!
原理其實很簡單,我們配置一條長度為 row * col
的一維陣列,而非多條長度為 col
的一維陣列。這樣我們就能確保這是一條連續記憶體。
做法大致是這樣:
T **ptr = new T*[row]
。row * col
的一維陣列:ptr[0] = new T[row * col]
。template <classname T>
int row, col;
std::cin >> row >> col;
T **ptr = new T*[row];
ptr[0] = new T[row * col];
for (int i = 1; i < row; i++)
ptr[i] = ptr[i - 1] + col;
/* do something with ptr[rowIdx][colIdx] */
delete [] ptr[0];
delete [] ptr;
未來的資料結構都會運用到動態配置。因為我們會把資料結構的建置包裝成函式,而函式裡宣告的變數都是區域變數,因此我們會在函式中使用動態宣告的技巧,使之脫離系統的自動控制。