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

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

void *a = malloc (1);

if (!a)
  return -1;

void *b = malloc (1);

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

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

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;

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

Ответы [ 7 ]

18 голосов
/ 28 марта 2012

У нас так:

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;
13 голосов
/ 28 марта 2012

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

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

Просто не используйте goto для возврата назад.Это приводит к катастрофе и растерянности.Единственный прыжок вперед - моя рекомендация.

3 голосов
/ 28 марта 2012

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

 void *a,*b;
 char * p = malloc(2);
 if (!p) return -1;
 a = p;
 b = p+1;
3 голосов
/ 28 марта 2012

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

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

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

2 голосов
/ 28 марта 2012

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

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

То есть условный форвард 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;

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

2 голосов
/ 28 марта 2012

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

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);
    }
}
0 голосов
/ 07 апреля 2012

Используйте сборщик мусора, такой как boehmgc.

Работает, прост в использовании, замедление не противоречит общему мнению.

https://en.wikipedia.org/wiki/Boehm_garbage_collector

http://www.hpl.hp.com/personal/Hans_Boehm/gc/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...