2016-03-24 5 views
-1

Рассмотрим эту программу:Другой способ очистки в C?

int main(void) 
{ 
    int* i = malloc(sizeof(int)); 
    int* j = malloc(sizeof(int)); 
} 

Однако это наивный подход, поскольку malloc может произойти сбой и указатели не являются free «d.

Таким образом, вы можете сделать это:

int main(void) 
{ 
    int* i; 
    int* j; 

    if ((i = malloc(sizeof(int)) < 0) 
    { 
     return -1; 
    } 
    if ((j = malloc(sizeof(int)) < 0) 
    { 
     free(i); 
     return -1; 
    } 

    free(i); 
    free(j); 
} 

Однако я считаю, что это очень подвержены ошибкам. Подумайте, нужно назначить 20 указателей, в последнем случае ошибки malloc, вы должны указать free 19 переменных, а затем return -1.

Я также знаю atexit, который может помочь мне написать это:

int* i; 
int* j; 

void del_i(void) 
{ 
    free(i); 
} 

void del_j(void) 
{ 
    free(j); 
} 

int main(void) 
{ 
    if ((i = malloc(sizeof(int)) < 0) 
    { 
     return -1; 
    } 
    else 
    { 
     atexit(del_i); 
    } 

    if ((j = malloc(sizeof(int)) < 0) 
    { 
     return -1; 
    } 
    else 
    { 
     atexit(del_j); 
    } 
} 

Что лучше, но я не люблю того, чтобы объявить все указатели как глобальные. Есть ли какой-нибудь способ, чтобы объединить эти два подхода, в основном:

  1. Имея деструкторов для указателей, которые могут быть либо выполнены непосредственно или использованы с atexit.
  2. Наличие указателей, локальных для функций.
+0

Я не понимаю вашу точку. Если вы заканчиваете/останавливаете выполнение программы, зачем беспокоиться о 'free()'? –

+0

1. Вы всегда должны указывать указатели 'free'. 2. Подумайте об этом в контексте более масштабной программы. – hgiesel

+0

Спасибо, что просветили меня, но я буду придерживаться своей версии. :) –

ответ

0
int main(void) 
{ 
int* i = NULL; // Init with NULL otherwise free on none NULL possible 
int* j = NULLL; 

if (!(i = malloc(sizeof(int))) 
{ 
    goto exit; 
} 
if (!(j = malloc(sizeof(int))) 
{ 
    goto exit; 
} 
... 
exit: 
    free(i); 
    free(j); 
    ... 
    return err; 
} 

Это то, что вы можете решить с помощью GOTO заявления.

+2

Вы переходите к 'exit', когда распределения выполняются. Не очень полезно. – unwind

+0

Да, вы правы, мой хранилище исправлено, что он забыл! – Schafwolle

+1

Просто опасайтесь http://www.dwheeler.com/essays/apple-goto-fail.html -типов ошибок. – Tommy

3

Во-первых, это не обнаружить malloc недостаточность:

if ((i = malloc(sizeof(int)) < 0) 
{ 
    return -1; 
} 

malloc возвращает NULL на провал, а не отрицательное число.

Во-вторых, atexit хорош для очистки статических и глобальных объектов. Не рекомендуется делать локальные объекты глобальными только для использования внутри atexit.

Лучшим подходом является создание struct для всех связанных указателей, которые необходимо выделить в единице «все или ничего», определить функцию для их освобождения сразу и написать функцию, которая выделяет их по одному с проверкой на каждом выделении памяти:

typedef struct AllOrNothing { 
    double *dPtr; 
    int *iPtr; 
    float *fPtr; 
    size_t n; 
} AllOrNothing; 

void freeAllOrNothing(AllOrNothing *ptr) { 
    free(ptr->dPtr); 
    free(ptr->iPtr); 
    free(ptr->fPtr); 
    free(ptr); 
} 

int allocateAllOrNothing(size_t n, AllOrNothing **res) { 
    *res = malloc(sizeof(AllOrNothing)); 
    if (*res == NULL) { 
     return -1; 
    } 
    // Freeing NULL is allowed by the standard. 
    // Set all pointers to NULL upfront, so we can free them 
    // regardless of the stage at which the allocation fails 
    (*res)->dPtr = NULL; 
    (*res)->iPtr = NULL; 
    (*res)->fPtr = NULL; 
    (*res)->n = n; 
    (*res)->dPtr = malloc(n*sizeof(double)); 
    if ((*res)->dPtr == NULL) { 
     free(*res); 
     *res = NULL; 
     return -1; 
    } 
    (*res)->fPtr = malloc(n*sizeof(float)); 
    if ((*res)->fPtr == NULL) { 
     free(*res); 
     *res = NULL; 
     return -1; 
    } 
    (*res)->iPtr = malloc(n*sizeof(int)); 
    if ((*res)->iPtr == NULL) { 
     free(*res); 
     *res = NULL; 
     return -1; 
    } 
    return 0; 
} 
4

free на NULL определяется как сейф не оп. Таким образом, изменение не прыжки может быть:

int *i = malloc(sizeof(int)); 
int *j = malloc(sizeof(int)); 

if(i && j) 
{ 
    // do some work 
} 

free(i); 
free(j); 
0
int main(void) 
{ 
    int* i = NULL; 
    int* j = NULL; 
    bool success = false; 

    do { 
     i = malloc(sizeof(int)); 
     if (NULL == i) break; 

     j = malloc(sizeof(int)); 
     if (NULL == j) break; 

     success = true; 
    } while (0); 

    if (!success) 
    { 
     printf("Something failed!"); 
    } 
    else 
    { 
     printf("All succeeded!"); 
     // Do more work 
    } 

    free(i); 
    free(j); 
    return (success? 0 : 1); 
} 
-1
int *i=NULL,*j=NULL; 

    if(!(i=malloc(sizeof(int)))) 
      goto EXIT; 
    if(!(j=malloc(sizeof(int)))) 
      goto EXIT; 
    /* do some work */ 
    return 0; 
    EXIT: 
      free(i); 
      free(j); 
      exit(EXIT_FAILURE); 

Хотя Гото считается плохой практикой программирования , но здесь мы можем использовать его, чтобы получить нашу задачу сделать с легкостью и простотой

0

Избегайте нескольких точек выхода. Избегайте переплетения распределения и обработки ошибок. Выполняет чистый порядок:

  1. Заявить, выделить и инициализировать ресурсы ..
  2. Если все успешное, выполните задачу.
  3. Очистка.
  4. Статус возврата.

// Do all allocations first, test their `NULL`-ness, then free them all. 
int main(void) { 
    // Allocate resources 
    // declare and allocate in one step 
    int* i = malloc(sizeof *i); 
    double* j = malloc(sizeof *j); 

    // Test for acceptability 
    bool ok = i && j; 

    // Perform the main body of code 
    if (ok) { 
     ; // do normal process in the code; 
    } 

    // free resources 
    free(i); 
    free(j); 

    // return status 
    return ok ? 0 : -1; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^