malloc和free函數使用注意事項,C語言malloc和free使用詳解

2020-07-16 10:04:24
在 C 語言中,程式中 malloc 等記憶體分配函數的使用次數一定要和 free 相等,並一一配對使用。絕對要避免“malloc 兩次 free 一次”或者“malloc 一次 free 兩次”等情況。這就像我們的婚姻制度,必須是“一夫一妻制”,不能夠“多夫一妻”或者“一夫多妻”,這些都是不合法的,如下面的範例程式碼所示:
#define MAX_BUF_SIZE 100
int main(void)
{
    /*記憶體釋放標誌*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL)
    {
        /*...*/
    }
    if (flag == 0)
    {
        free(p);
    }
    free(p);
    return 0;
}
在上面範例程式碼中,當條件“if(flag==0)”成立時,“free(p)”將被執行兩次,從而導致記憶體的雙重釋放錯誤。因此,應該消除這種雙重釋放潛在的風險,保證動態記憶體只被釋放一次,如下面的範例程式碼所示:
#define MAX_BUF_SIZE 100
int main(void)
{
    /*記憶體釋放標誌*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL)
    {
        /*...*/
    }
    if (flag == 0)
    {
        /*...*/
    }
    free(p);
    p=NULL;
    return 0;
}
當然,也可以採用下面的方式:
#define MAX_BUF_SIZE 100
int main(void)
{
    /*記憶體釋放標誌*/
    int flag = 0;
    char * p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL)
    {
        /*...*/
    }
    if (flag == 0)
    {
        free(p);
        p = NULL;
    }
    if (p != NULL)
    {
        free(p);
        p = NULL;
    }
    return 0;
}
除此之外,對於記憶體釋放還必須保證只釋放動態分配的記憶體,即不能用 free 來釋放非 malloc、realloc、calloc 與 aligned_alloc 等記憶體分配函數分配的記憶體空間。與此同時,也不要將指標變數進行自增或者自減操作,使其指向動態分配的記憶體空間中間的某個位置,然後直接釋放,這樣也有可能引起未知的錯誤。

在 free 之後必須為指標賦一個新值

在使用指標進行動態記憶體分配操作時,在指標 p 被 free 釋放之後,指標變數本身並沒有被刪除。如果這時候沒有將指標 p 置為 NULL,會讓人誤以為 p 是個合法的指標而在以後的程式中錯誤使用它。

如下面的範例程式碼所示:
#define MAX_BUF_SIZE 100
int main(void)
{
    char * p = NULL;
    p=(char *)malloc(MAX_BUF_SIZE);
    if (p == NULL)
    {
        /*...*/
    }
    /*記憶體初始化*/
    memset(p, '', MAX_BUF_SIZE);
    strcpy(p, "hello");
    /*釋放記憶體*/
    if (p != NULL)
    {
        free(p);
    }
    if (p != NULL)
    {
        /*發生錯誤*/
        strcpy(p, "world");
    }
    return 0;
}
在上面的範例程式碼中,第一個判斷語句:
/*釋放記憶體*/
if (p != NULL)
{
    free(p);
}
雖然釋放了指標變數 p,但這個時候指標變數 p 本身並沒有被刪除,其儲存的地址並沒有改變。但是,此時 p 雖不是 NULL 指標,但它卻不指向合法的記憶體塊,成為“野指標”或稱為“懸垂指標”。接下來,在執行第二個判斷語句時:
if (p != NULL)
{
    /*發生錯誤*/
    strcpy(p, "world");
}
條件“if (p != NULL)”成立,“strcpy(p, "world");}”語句將被繼續執行,導致程式出錯。或許有人會問,“free(p)”到底釋放了什麼?

“free(p)”釋放的是指標變數 p 所指向的記憶體,而不是指標變數 p 本身。指標變數 p 並沒有被釋放,仍然指向原來的儲存空間。

其實,指標只是一個變數,只有程式結束時才被銷毀。釋放記憶體空間後,原來指向這塊空間的指標還是存在的,只不過現在指標指向的這塊記憶體是不合法的。因此,在釋放記憶體後,必須把指標指向 NULL,以防止指標在後面不小心又被解除參照了。

如下面的範例程式碼所示:
#define MAX_BUF_SIZE 100
int main(void)
{
    char * p = NULL;
    /*記憶體申請*/
    p = (char *)malloc(MAX_BUF_SIZE);
    if (p == NULL)
    {
        /*...*/
    }
    /*記憶體初始化*/
    memset(p, '', MAX_BUF_SIZE);
    strcpy(p, "hello");
    /*釋放記憶體*/
    if (p != NULL)
    {
        free(p);
        /*在free之後給指標儲存一個新值*/
        p = NULL;
    }
    if (p != NULL)
    {
        /*發生錯誤*/
        strcpy(p, "world");
    }
    return 0;
}
現在,通過語句“p=NULL”給指標變數 p 賦予一個 NULL 值之後,第二個條件語句“if(p!=NULL)”將不成立,語句“strcpy(p,"world")”也將不會被執行。所以一定要記住一條:free(p) 完之後,一定要將指標變數 p 置為 NULL。