在前一篇我們介紹的陣列(Array)中,舉的範例是以靜態配置的方式來宣告陣列,在本篇我們即將介紹什麼是動態記憶體配置(Dynamic Memory Allocation)。
動態記憶體配置指的是,我們可以在程式中任意變動已向系統要求的記憶體,例如調整大小、釋放記憶體等等。
另外還有一個重點是,動態配置的記憶體,其有效範圍和全域變數一樣,因為它們都被存放在 Heap 當中,有興趣想先了解這個原理的讀者可以 Google 搜尋關鍵字 Heap and Stack。
首先我們先來介紹用 malloc 和 memset 標準函式來配置陣列,malloc 函式是向系統請求單一塊記憶體空間,其空間大小可由設計者指定,但空間中的
變數沒有預設值,需搭配 memset 函式來對這空間進行預設值配置。
我們先來看看它的語法:
void main (int argc, char* argv[]) {
int *ptr = nullptr; // 或是你也可以不用預設值(nullptr) -> int * ptr;
int size = 100;
ptr = (int*)malloc(size * sizeof(int));
if (!ptr) {
printf("Memory allocation failed!\n");
return;
}
memset(ptr, 0, size * sizeof(int));
}
首先,在一開始的變數定義中,假設我們需要配置一個整數陣列,那麼我們會需要用一個整數指標 ptr 作為開頭,而不是直接向上一篇範例那樣直接地宣告陣列,這樣就變成靜態配置了。我們需要用這個指標去指向動態配置的記憶體位址,然後定義記憶體大小,如上例為 100 個變數,接著就調用 malloc 函式來配置記憶體。
在 malloc 這行程式碼,我們需要在 malloc 函式前特別標上 (int*),這是在告訴程式當 malloc 配置完成之後,請把指標轉換成 int 整數型指標,因為 malloc 函式不會知道我們要配置的空間是給什麼樣子的資料型態,它只是很單純地配置空間而已,因此 malloc 返回的指標須由設計者自行標示轉換,而在 malloc 我們給予的參數就是記憶體配置的大小。這行程式碼結束之後,為了確定我們的記憶體請求是否有成功,可以用判斷式來判斷指標是否為空(NULL 或 nullptr),
若請求成功之後,再調用 memset 函式進行初始值配置,參數分別是記憶體指標、預配置值(預設值)、預配置的空間大小。
接下來,除了 malloc 函式可以動態配置記憶體之外,還有另一個 calloc 函式也能進行動態記憶體配置,它是由數個區塊組合而成的記憶體空間,並且內部會做預設值為 0 的動作。
我們先來看看它的語法:
void main (int argc, char* argv[]) {
int *ptr = nullptr;
int size = 100;
ptr = (int*)calloc(size, sizeof(int));
if (!ptr) {
printf("Memory allocation failed!\n");
return;
}
}
calloc 函式語法和 malloc 很類似,差別在於參數個數不是一個,而是拆開成"變數個數"和"單個變數所占記憶體"。如前述,由於內部已經會做預設值 0 的動作,因此我們就不需要用 memset 函式來進行預設值配置。
如本篇簡介提到,動態配置的特性是可以變動已向系統要求的記憶體空間,現在我們可以用 realloc 函式進行記憶體空間大小的調整,如下範例:
void main (int argc, char* argv[]) {
int *ptr = nullptr;
int size = 100;
ptr = (int*)calloc(size, sizeof(int));
if (!ptr) {
printf("Memory allocation failed!\n");
return;
}
size = 200;
realloc(ptr, size * sizeof(int));
if (!ptr) {
printf("Memory re-allocation failed!\n");
return;
}
}
realloc 的參數有兩個,第一者為指向記憶體空間的指標,第二為調整後記憶體大小。我們在範例中先配置了 100 個變數空間之後,隨即馬上再將此空間調整為 200 ,你可以會有疑問,若原本的空間已經有存放了一些變數在裏頭,那麼它們會被清掉嗎? 答案是不會的,realloc 會保留原先空間的變數值,並且擴展設計者所指定的空間大小。
動態配置及空間大小調整介紹完之後,我們接下來要來看看如何用這個指標來走訪記憶體內的變數。我們可以用兩種方式來走訪,先來看看下面的範例:
void main (int argc, char* argv[]) {
int *ptr = nullptr;
int size = 10;
ptr = (int*)calloc(size, sizeof(int));
if (!ptr) {
printf("Memory allocation failed!\n");
return;
}
// 第一種方式走訪並將所有變數給定為 1
for (int i = 0 ; i < size ; i++) {
ptr[i] = 1;
}
size = 20;
realloc(ptr, size * sizeof(int));
if (!ptr) {
printf("Memory re-allocation failed!\n");
return;
}
// 第二種方式走訪並輸出各個變數的值
for (int i = 0 ; i < size ; i++) {
printf("%d ", *(ptr+i));
}
}
首先,第一種是像陣列表示一樣,用方括號框住索引(index)來走訪,如上例,邊走訪邊配置值給各個變數。第二種方式是用取值表示法的方式,在上例中我們是將值輸出,要特別注意的是,括號的範圍有很大的意義差異,看看下面範例:
*(ptr + i) // 將指標從初始位址移動到第 i 個位置後,再取出該記憶體位址的值
*(ptr + i) == ptr[i] // 同上,與陣列表示式相同
*ptr + i // 取出 ptr 記憶體位址的值,再加上 i 值
*(ptr) + i // 同上,取出 ptr 記憶體位址的值,再加上 i 值
Last updated: