HOME ABOUT CONTACT

C/C++教學: 第十八課 - 動態記憶體配置(Dynamic Memory Allocation)

Rain September 21, 2024
Outline

1. 簡介

2. 運用malloc和memset函式配置

3. 運用calloc函式配置

4. 運用realloc函式重新配置大小

5. 指標走訪

簡介top

在前一篇我們介紹的陣列(Array)中,舉的範例是以靜態配置的方式來宣告陣列,在本篇我們即將介紹什麼是動態記憶體配置(Dynamic Memory Allocation)。

動態記憶體配置指的是,我們可以在程式中任意變動已向系統要求的記憶體,例如調整大小、釋放記憶體等等。

另外還有一個重點是,動態配置的記憶體,其有效範圍和全域變數一樣,因為它們都被存放在 Heap 當中,有興趣想先了解這個原理的讀者可以 Google 搜尋關鍵字 Heap and Stack。

運用malloc和memset函式配置top

首先我們先來介紹用 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 函式進行初始值配置,參數分別是記憶體指標、預配置值(預設值)、預配置的空間大小。

運用calloc函式配置top

接下來,除了 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函式重新配置大小top

如本篇簡介提到,動態配置的特性是可以變動已向系統要求的記憶體空間,現在我們可以用 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 會保留原先空間的變數值,並且擴展設計者所指定的空間大小。

指標走訪top

動態配置及空間大小調整介紹完之後,我們接下來要來看看如何用這個指標來走訪記憶體內的變數。我們可以用兩種方式來走訪,先來看看下面的範例:


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 值
                        

下一篇: 第十九課 - 結構(Struct)

Last updated:

Related Artical List

  1. C/C++教學: 第十六課 - 傳值呼叫、傳址呼叫、傳參呼叫
  2. C/C++教學: 第十七課 - 陣列(Array)
  3. C/C++教學: 第十九課 - 結構(Struct)

Article List