iT邦幫忙

2022 iThome 鐵人賽

DAY 4
1
Software Development

用C++ 設計程式中的系統櫃系列 第 4

[Day 04] 用C++ 設計程式中的系統櫃:動態配置記憶體

  • 分享至 

  • xImage
  •  

解決上一篇的兩個問題之前,我們先來認識動態記憶體吧!


動態配置的記憶體

特色:

  1. 動態記憶體由程式設計師來配置。換句話說,就是在執行程式時,才會配置記憶體。
  2. 動態記憶體儲存/被分配在 Heap 上。
  3. 動態記憶體的配置需要「指標」和「型別」來支援。
  4. 由程式設計師來決定記憶體釋放的時機點(好習慣)。

既然動態記憶體不是由系統自動釋放的,那我們就可以將函式中的變數(未來還會使用者)改用動態配置來宣告。這就是為什麼上一篇提到的問題(二)可以用動態配置來改善。


前進語法

動態配置一般變數

  1. 我們需要一個指標和一個型別。
    • 因為前面說到指標是紀錄記憶體最前端的地址,我們需要一個型別來告知系統要配置的記憶體大小!如:int 變數需要 4 bytes。
  2. 想要賦予這塊記憶體數值時,我們對這塊記憶體取值,再賦值。(因為當前的變數是指標變數)
  3. 釋放記憶體時,我們是釋放這個指標領導的記憶體。釋放後,我們仍可以使用這個指標變數,畢竟這個變數紀錄的只是一個地址,本身就不是紀錄一塊記憶體。
template <classname T>
T *ptr = new T; // 配置使用關鍵字 new
delete ptr; // 釋放該指標所領導的記憶體

動態配置一維陣列

這個部分就是用來解答上一篇遇到的問題「不確定陣列大小」。
原理與動態配置一般變數相同,只有一點區別。

  1. 配置所需的型別要更改為「內容物數量為 n 的陣列」,這樣系統才會配置 new T[n]
  2. 假設領導的指標變數為 ptr,欲隨機存取陣列內容時,語法是 ptr[idx] (注意索引值大小:0 <= idx < n)。
  3. 釋放記憶體時,語法更改為 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 **)儲存。
想像二維陣列是許多一維陣列的集合,因此我們的做法大致可以歸納為以下幾個步驟:

  1. 配置一個儲存各個一維陣列的領導指標的陣列:new T*[row]
  2. 有了領導的指標,我們就可以動態配置陣列了(配合迴圈):ptr[i] = new T[col]
  3. 使用時的語法與非動態配置的二維陣列語法相等:ptr[i][j]
  4. 釋放時,我們會先釋放「後配置的陣列」。如果先釋放「先配置的記憶體」,我們會遺失所有內層陣列的領導指標。
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 的一維陣列。這樣我們就能確保這是一條連續記憶體。
做法大致是這樣:

  1. 配置一個儲存各個一維陣列的領導指標的陣列:T **ptr = new T*[row]
  2. 配置一條長度為 row * col 的一維陣列:ptr[0] = new T[row * col]
  3. 算出各個一維陣列的領導指標為何,儲存於步驟一的陣列。
  4. 釋放時,從後配置的記憶體開始釋放
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;

未來的資料結構都會運用到動態配置。因為我們會把資料結構的建置包裝成函式,而函式裡宣告的變數都是區域變數,因此我們會在函式中使用動態宣告的技巧,使之脫離系統的自動控制。


上一篇
[Day 03] 用C++ 設計程式中的系統櫃:動態配置記憶體(前傳)
下一篇
[Day 05] 用C++ 設計程式中的系統櫃:神奇的箭號
系列文
用C++ 設計程式中的系統櫃30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言