簡體   English   中英

我如何正確處理C中的malloc失敗,尤其是當有多個malloc時?

[英]How can I correctly handle malloc failure in C, especially when there is more than one malloc?

假設這是我的代碼的一部分:

 int foo()
 {  
    char *p, *q ;
    if((p = malloc(BUFSIZ)) == NULL) {
        return ERROR_CODE;
    }
    if((q = malloc(BUFSIZ)) == NULL) {
        free(p)
        return ERROR_CODE;
    }
    /* Do some other work... */

    free(p);
    free(q);  
 }

由於第一個malloc可能成功而第二個malloc失敗,所以我在第二個“錯誤處理程序”中使用free(p) 但是,如果還有更多的malloc呢?如果我想修改代碼(調整它們的順序,添加或刪除一些malloc )怎么辦?

我知道在C ++中有諸如RAII和異常安全之類的東西。但是總的來說,在C中處理malloc失敗的正確方法是什么? (也許使用一些goto嗎?)

您的代碼很好,但是對於許多變量,我希望:

int
foo()
{
    char *p = NULL;
    char *q = NULL;
    int ret = 0;

    if (NULL == (p = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // possibly do something here

    if (NULL == (q = malloc(BUFSIZ)))
    {
        ret = ERROR_CODE;
        goto error;
    }

    // insert similar repetitions

    // hopefully do something here

  error:
    free (p);
    free (q);
    return ret;
}

請注意,將NULL釋放定義為無操作。

這避免了n n變量的n個縮進級別。 您可以類似地清理文件句柄等(盡管您必須在close()周圍放置一個條件)。

現在,如果您知道可以一次分配它們,那么dasblinkenlight有一個很好的答案,但是這是另一種方法:

int
foo()
{
    int ret = 0;
    char *p = malloc(BUFSIZ);
    char *q = malloc(BUFSIZ);
    char *r = malloc(BUFSIZ);
    if (!p || !q || !r)
    {
        ret = ERROR_CODE;
        goto exit;
    }

    // do something

  exit:
    free(p);
    free(q);
    free(r);
    return ret;
}

最終可能性:如果您確實想在malloc失敗時退出程序,請考慮使用malloptM_CHECK_ACTION選項。 這使得malloc()錯誤得到檢查,並調用abort() ,可能會顯示一條有用的消息。

從手冊頁:

名稱

mallopt設置內存分配參數

概要

  #include <malloc.h> int mallopt(int param, int value); 

描述

mallopt()函數調整用於控制內存分配函數行為的參數(請參閱malloc(3) )。 param參數指定要修改的參數,而value指定該參數的新值。

可以為param指定以下值:

M_CHECK_ACTION

設置此參數可控制在檢測到各種編程錯誤(例如,釋放同一指針兩次)時glibc的響應方式。 分配給此參數的值的3個最低有效位(2、1,和0)確定glibc行為,如下所示:

位0 :如果該位置1,則在stderr上打印一條單行消息,其中提供有關錯誤的詳細信息。 該消息以字符串"*** glibc detected ***"開頭,然后是程序名稱,在其中檢測到錯誤的內存分配函數的名稱,錯誤的簡要說明以及內存地址,其中檢測到錯誤。

位1 :如果設置了此位,則在打印由位0指定的任何錯誤消息之后,通過調用abort(3)終止程序。 在glibc的版本,因為2.4,如果位0也被置位,則,打印錯誤消息並中止之間,該程序還打印在上的狀態的堆棧跟蹤backtrace(3)並打印在風格進程的存儲器映射/proc/[pid]/maps (請參閱proc(5) )。

位2 :(自glibc 2.4起)僅當還設置了位0時,此位才有效。 如果設置了此位,則描述錯誤的單行消息將簡化為僅包含檢測到錯誤的函數的名稱以及錯誤的簡短描述。

由於將NULL傳遞給free()是完全可以的,因此您可以在“直線”中分配所需的所有內容,一次性檢查所有內容,然后在完成后釋放所有內容,無論您是否擁有實際完成了任何工作:

char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
    /* Do some other work... */
}
free(p);
free(q);
free(r);

只要沒有中間依賴項,即不具有多級依賴項的結構,此方法就起作用。 在執行此操作時,最好定義一個用於釋放這種結構的函數,而不假定所有內存塊均為非NULL

對於大量分配,我會花時間創建一個跟蹤分配的內存管理器。 這樣,無論功能是否成功,您都不必擔心泄漏。

通常的想法是為malloc創建一個包裝器,以記錄成功的分配,然后根據請求釋放它們。 要釋放內存,只需將特殊大小傳遞給包裝函數。 使用尺寸的0來釋放內存,如果你知道你沒有實際分配的將是適合0大小的塊。 否則,您可能希望使用~0ULL作為免費請求大小。

這是一個簡單的示例,每次釋放之間最多允許100個分配。

#define FREE_ALL_MEM 0

void *getmem( size_t size )
{
    static void *blocks[100];
    static int count = 0;

    // special size is a request to free all memory blocks
    if ( size == FREE_ALL_MEM )
    {
        for ( int i = 0; i < count; i++ )
            free( blocks[i] );
        count = 0;
        return NULL;
    }

    // using a linked list of blocks would allow an unlimited number of blocks
    // or we could use an array that can be expanded with 'realloc'
    // but for this example, we have a fixed size array
    if ( count == 100 )
        return NULL;

    // allocate some memory, and save the pointer in the array
    void *result = malloc( size );
    if ( result )
        blocks[count++] = result;

    return result;
}

int foo( void )
{
    char *p, *q;

    if ( (p = getmem(BUFSIZ)) == NULL ) {
        return ERROR_CODE;
    }
    if ( (q = getmem(BUFSIZ)) == NULL ) {
        getmem( FREE_ALL_MEM );
        return ERROR_CODE;
    }

    /* Do some other work... */

    getmem( FREE_ALL_MEM );
    return SUCCESS_CODE;
}

這是習慣問題,但我更喜歡:

int returnFlag = FAILURE;

if ((p = malloc...) != NULL)
{
    if ((q = malloc..) != NULL)
    {
        // do some work
        returnFlag = SUCCESS; // success only if it is actually success

        free(q);
    }
    free(p);
}

return returnFlag; // all other variants are failure

如果您希望分配大量的項目,它可能會變得凌亂。 嘗試避免“ goto”方法。 並不是因為過去的“ goto is bad”道德准則,而是因為這種方式確實可以掩蓋瘋狂和內存泄漏。

對於少量的malloc來說,這有點矯kill過正,但是您可以考慮使用以下方法:

void free_mem(void **ptrs, size_t len)
{
    for (size_t i = 0; i < len; ++i)
    {
        free(ptrs[i]);
        ptrs[i] = NULL;
    }
}

int foo(...)
{
    void *to_be_freed[N];
    int next_ptr = 0;
    for (size_t i = 0; i < N; ++i) to_be_freed[i] = NULL;

    p = malloc(..);
    if (!p)
    {
        free_mem(to_be_freed,N);
        return ERROR_CODE;
    }
    to_be_freed[next_ptr++] = p;

    // Wash, rinse, repeat, with other mallocs
    free_mem(to_be_freed,N)
    return SUCCESS;
}

實際上,您可以使用跟蹤此內容的東西包裝malloc。 將數組和數組大小放入結構中,然后將其傳遞給所需的分配大小。

我認為第一個答案是最通用的,因為它可以用於除malloc引起的錯誤以外的其他錯誤。 但是我會刪除gotos並像這樣使用一次單次while循環。

int foo()
{
  char *p = NULL;
  char *q = NULL;
  int ret = 0;
  do {
    if (NULL == (p = malloc(BUFSIZ)))
    {
      ret = ERROR_CODE;
      break;
    }

    // possibly do something here

    if (NULL == (q = malloc(BUFSIZ)))
    {
      ret = ERROR_CODE;
      break;
    }

    // insert similar repetitions

    // hopefully do something here
  } while(0);
  free (p);
  free (q);
  return ret;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM