Чтобы написать надежную программу на C, как вы избегаете слишком много разных бесплатных() комбинаций?

Например, Мне нужно malloc две части памяти, поэтому:

void *a = malloc (1);

if (!a)
  return -1;

void *b = malloc (1);

if (!b)
{
  free (a);
  return -1;
}

Обратите внимание, если второй malloc терпит неудачу, я должен сначала освободить "a". Проблема в том, что это может быть очень грязно, если таких проверок malloc и ошибок достаточно, если я не использую пресловутое предложение "goto" и аккуратно упорядочиваю порядок свободных вместе с метками:

void *a = malloc (1);

if (!a)
  goto X;

void *b = malloc (1);

if (!b)
  goto Y;

return 0; //normal exit

Y:
  free (a);
X:
  return -1;

Есть ли у вас лучшее решение этой ситуации? Спасибо заранее.

Ответ 1

Мы делаем следующее:

void *a = NULL;
void *b = NULL;
void *c = NULL;
a = malloc(1);
if (!a) goto errorExit;
b = malloc(1);
if (!b) goto errorExit;
c = malloc(1);
if (!b) goto errorExit;

return 0;
errorExit:
//free a null pointer is safe.
free(a);
free(b);
free(c);
return -1;

Ответ 2

Использование goto - это не плохая вещь, на мой взгляд. Использование его для очистки ресурсов подходит именно ему.

Исходный код, известный как ядро ​​Linux, использует технику.

Просто не используйте goto, чтобы вернуться назад. Это приводит к катастрофе и путанице. Только перепрыгнуть вперед - это моя рекомендация.

Ответ 3

Как уже упоминалось Zan Lynx, используйте инструкцию goto.

Вы также можете выделить большую часть памяти для дальнейшего использования.

Или вы можете потратить свое время на разработку чего-то вроде пула памяти.

Ответ 4

Или сделайте это.

 void *a,*b;
 char * p = malloc(2);
 if (!p) return -1;
 a = p;
 b = p+1;

Ответ 5

Я думаю, что методы ООП могут дать вам хорошее и чистое решение этой проблемы:

typedef struct {
    void *a;
    void *b;
} MyObj;

void delete_MyObj(MyObj* obj)
{
    if (obj) {
        if (obj->a)
            free(obj->a);
        if (obj->b)
            free(obj->b);
        free(obj);
    }
}

MyObj* new_MyObj()
{
    MyObj* obj = (MyObj*)malloc(sizeof(MyObj));
    if (!obj) return NULL;
    memset(obj, 0, sizeof(MyObj));

    obj->a = malloc(1);
    obj->b = malloc(1);

    if (!obj->a || !obj->b) {
        delete_MyObj(obj);
        return 0;
    }

    return obj;
}

int main()
{
    MyObj* obj = new_MyObj();
    if (obj) {
        /* use obj */
        delete_MyObj(obj);
    }
}

Ответ 6

В вашем коде goto нет IMO (я бы использовал более подробные метки).

В этом случае все написанные команды goto создают точно такую ​​же структуру, как и обращение к if s.

То есть условный forward goto, который не оставляет какой-либо области, делает то же самое, что и оператор if без else. Разница заключается в том, что goto не покидает область видимости, тогда как if не ограничивается областью видимости. Вот почему if обычно легче читать: у читателя есть больше подсказок.

void *a = malloc (1);
if (a) {
    void *b = malloc (1);
    if (b) {
        return 0; //normal exit
    }
    free(a);
}
return -1;

Для нескольких уровней это нормально, хотя за слишком далеко вы получаете "код стрелки" со слишком большим количеством отступов. Это становится нечитаемым по совершенно другим причинам.