Чтобы написать надежную программу на 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;
Для нескольких уровней это нормально, хотя за слишком далеко вы получаете "код стрелки" со слишком большим количеством отступов. Это становится нечитаемым по совершенно другим причинам.
Ответ 7
Используйте сборщик мусора, например, boehmgc.
Он работает, он прост в использовании, нет замедления, противоречащего общему мнению.
https://en.wikipedia.org/wiki/Boehm_garbage_collector
http://www.hpl.hp.com/personal/Hans_Boehm/gc/