1. 程式人生 > >記憶體分配(malloc()和free())

記憶體分配(malloc()和free())

C語言的一個特性是接近底層,對於硬體的控制能力比其他高階動態語言要強。同時,C語言賦予程式設計師更大的自由度,更信任程式設計師。在記憶體的分配與釋放上,我們知道非靜態變數(塊作用域,無連結,自動生存期)在程式進入到變數定義所在的地方(塊或函式內)時分配記憶體,在離開塊作用域時釋放。對於靜態變數,在程式載入到記憶體時,變數的記憶體分配到靜態儲存區,且直到程式執行結束(終止)時釋放。這些都是程式執行過程中由程式自動控制的。
同時,C提供給程式設計師更大的許可權,可以手動控制變數的記憶體分配和釋放,相關的函式為malloc(),calloc()和free()。

手動分配記憶體malloc()和calloc()

malloc():函式原型:void malloc(size_t size),引數為要分配的記憶體位元組數,返回值為指向void的指標,pointer-to-void,一般返回該段記憶體空間的首個位元組。我們可以使用型別轉化將分配的記憶體起始地址複製給一個指標變數,這樣我們就可以操作這段記憶體空間內的資料,並在必要時候釋放該段記憶體空間。
double
ptd;
ptd = (double ) malloc(30 sizeof(double));
我們首先宣告一個指向double型別的指標變數ptd,然後使用malloc申請包含30個double型別的一段記憶體空間,並將其記憶體空間的起始地址賦值給ptd。我們甚至可以用變數n替換30這個常量,這樣相當於我們可以動態建立一個可變陣列,類似VLA。若malloc分配記憶體失敗,則返回空指標NULL。


calloc()和malloc類似,函式原型為:void *calloc(size_t nitems, size_t size)。它有兩個引數,第一個為要申請的記憶體單元數,第二個為每個記憶體單元的位元組數。

使用free() 釋放記憶體

一般使用malloc分配可一段記憶體空間後,需要在必要的時候手動釋放掉,以防止記憶體洩露。free()和malloc()配合使用。
示例:

/* dyn_arr.c -- dynamically allocated array */
#include<stdio.h>
#include<stdlib.h> /* for malloc(), free()  */

int main(void)
{
    double * ptd;
    int max = 0;
    int number = 0;
    int i = 0;

    puts("What is the maximum number of type double entries?");
    if (scanf("%d", &max) != 1)
    {
        puts("Number not correctly entered -- bye.");
        exit(EXIT_FAILURE);
    }
    ptd = (double *) malloc(max * sizeof(double));
    if (ptd == NULL)
    {
        puts("Memory allocation failed. Goodbye.");
        exit(EXIT_FAILURE);
    }
    /* ptd now points to an array of max elements */
    puts("Enter the values (q to quit):");
    while (i < max && scanf("%lf", &ptd[i]) == 1)
        ++i;
    printf("Here are your %d entries:\n", number = i);
    for (i = 0; i < number; i++)
    {
        printf("%7.2f ", ptd[i]);
        if (i % 7 == 6)
            putchar('\n');
    }
    if (i % 7 != 0)
        putchar('\n');
    puts("Done.");
    free(ptd);

    return 0;
}

輸出:
What is the maximum number of type double entries?
5
Enter the values (q to quit):
20 30 35 25 40 80
Here are your 5 entries:
20.00 30.00 35.00 25.00 40.00
Done.

雖然我們輸入了6個integer型別的資料,但實際輸出只有5個,因為我們申請的這段記憶體空間為包含5個integer型別的陣列。

free()的重要性

若使用malloc或calloc手動分配記憶體,且沒有使用free釋放掉該段記憶體,某些作業系統會自動釋放掉這段記憶體,但不是所有的作業系統都支援這種特性。所以,不要依靠作業系統,而是要手動釋放掉這段記憶體。
原因:來看下面這段程式碼:

int main()
{
    double glad[2000];
    int i;
    for (i = 0; i < 1000; i++)
        gobble(glad, 2000);
}

void gobble(double ar[], int n)
{
    double * temp = (double *) malloc(n * sizeof(double));
    /* free(temp);   // forgot to use free()   */
}

main函式每次呼叫gobble時,都會使用malloc手動申請2000個doube型別的記憶體空間,假設每個double型別佔用8個位元組(bytes)。那麼執行到malloc處時,總共會申請16000位元組的空間,而我們沒有呼叫free函式手動釋放掉這段記憶體空間,當該函式執行完畢時,指向這16000個位元組空間的指標變數temp(塊作用域,無連結,自動生存期)將會消失,但這16000個位元組的記憶體空間仍然存在,且程式無法通過任何變數或方式訪問該記憶體空間。
第二次呼叫gobble時,仍然會出現這種情況,直到for迴圈結束時,我們總共手動申請了1000 * 2000 * 8 = 16000000位元組的記憶體空間,很有可能沒到這麼多記憶體時,記憶體已經消耗完畢。這種情況,我們稱為記憶體洩漏。所以,我們要時刻記得使用free釋放掉malloc手動分配的記憶體空間。