2015-06-15 2 views
3

Я работаю над проектом C (назначение для школы). Одним из требований является то, что в случае отказа malloc() программа должна free() выделить выделенную память и exit().Освобождение всей выделенной памяти в случае отказа

Рассмотрим случай, когда функция A() создает связанный список и на каждой итерации обращается к другой функции, B(). Теперь, если произошел сбой malloc на B(), он должен free() выделенной памяти, но функция A() должна это сделать.

Все становится довольно сложным, если у вас есть дерево вызовов функций больше двух.

В моем предыдущем проекте я использовал флаг для уведомления об ошибке malloc() - если функция использует другую функцию, которая может использовать malloc(), она должна проверить флаг сразу после. Это сработало, но код стал бесполезным.

Есть ли четкое решение этой проблемы?
Конечно, с «настоящими» приложениями вся память выделяется ОС, но я думаю, что этот спрос является педагогическим.

+0

вы ищете типа ответа псевдо-код? –

+0

Совсем нет. Я прошу об идее подхода \. – eggbaz

+0

Я не уверен, почему я получаю эти downvotes. – eggbaz

ответ

2

Да. Лучший (и обычный) способ - инициализировать каждое значение указателя до нуля. Затем установите его во время назначения malloc(). Пример: myPtr = malloc (10);

Это будет нуль в случае отказа, и вы это проверите. И, наконец, когда вы идете об освобождении, вы всегда проверить значение указателя перед вызовом свободными():

if (myPtr != 0) 
    free(myPtr); 

Там нет необходимости для дополнительного флага.

+2

нет необходимости проверять указатель перед освобождением, если это ничего не происходит –

+2

Не все реализации - это надежные встроенные библиотеки в частности. – donjuedo

+2

в соответствии со стандартом C. если он не реализован, это ошибка в компиляторе. в противном случае он сломал бы тонны устаревшего кода. –

1

Вы можете посмотреть в функции atexit(), чтобы зарегистрировать код, который будет выполнен, когда программа завершается. Такой код может затем проверить, есть ли что-то, что должно быть free() d.

Отметьте, что atexit() не имеет возможности отменить регистрацию. Поэтому вам нужно убедиться, что вы регистрируете каждую функцию очистки только один раз и что она делает правильные вещи, когда нет ничего, что можно было бы очистить.

#include <stdlib.h> 
#include <stdio.h> 

int *ptr1; 
char *ptr2; 
int clean1_registered, clean2_registered; 

void clean1(void) 
{ 
    printf("clean1 called\n"); 
    if (ptr1) { 
     free(ptr1); 
     ptr1 = NULL; 
    } 
} 

void clean2(void) 
{ 
    printf("clean2 called\n"); 
    if (ptr2) { 
     free(ptr2); 
     ptr2 = NULL; 
    } 
} 

void B(void) 
{ 
    ptr2 = malloc(100); 
    if (!clean2_registered) { 
     atexit(clean2); 
    } 
} 

void A(void) 
{ 
    ptr1 = malloc(100 * sizeof(int)); 
    if (!clean1_registered) { 
     atexit(clean1); 
    } 
    B(); 
} 

int main(int argc, char **argv) 
{ 
    A(); 
} 
1

У вас есть проблема с проверкой ошибок или их обработкой? Если вы хотите, чтобы информация об их ловле, используйте предложение donjuedo.

Для идей на освобождение памяти в случае ошибки, попробуйте один из этих двух методов:

1) Для однонаправленного связанного списка, сохранить специальный указатель, который указывает на начало списка. В вашей каскадной свободной функции начинайте с головы, захватывайте следующий указатель в переменной temp, освобождайте голову, переходите к следующей структуре в списке с помощью временного указателя и повторяете процесс до следующего указателя == 0.

2) Для двунаправленного связанного списка (по моему предпочтению) вам не нужно указывать специальный указатель на голову списка. Предполагая, что вы все еще на хвосте, просто захватите предыдущий указатель во временную переменную, освободите хвост, вернитесь назад с помощью указателя температуры и повторите процесс до тех пор, пока предыдущий указатель == 0

2

Я думаю, что Самый простой подход - создать пользовательский распределитель (как кто-либо уже отметил в удаленной записи), чтобы отслеживать все ваши распределения, а затем делать пользовательский дезактиватор, использовать их для всех ваших потребностей в памяти кучи.

Если сбой malloc у вас есть список ранее выделенных блоков с легкостью досягаемости.

например. (вам нужно повторить эту причину он не эффективен и должен быть оптимизирован, но показывает принцип и только глазное сборник)

typedef struct 
{ 
    void* pMemory; /* for the allocated memory */ 
    size_t size; /* for better debugging */ 

} MemoryBlock; 

#define MAXBLOCKS 1000 

MemoryBlock myheap[MAXBLOCKS]; // global so zero:ed 
static int block = 0; 

void* myalloc(size_t size) 
{ 
    static int block = 0; 

    // you should check vs MAXBLOCKS 

    myheap[block].pMemory = malloc(size); 
    myheap[block].size = size; 

    // check if it failed. 
    if (myheap[block].pMemory == NULL) 
    { 
     for (int i = 0; i < block; ++i) 
     { 
     myfree(myheap[i].pMemory); 
     } 
     fprintf(stderr, "out of memory\n"); 
     exit(EXIT_FAILURE); 
    } 
    else 
    { 
    return myheap[block++].pMemory; 
    } 
} 

void myfree(void* p) 
{ 
    for (int i = 0; i < block; ++i) 
    { 
     if (p == myheap[i].pMemory) 
     { 
     free(myheap[i].pMemory); 
     myheap[i].pMemory = NULL; 
     return; 
     } 
    } 
} 
+0

. Многие вспомогательные средства для отладки отслеживают распределение и де-распределение, а затем могут предоставить отчет об утечке в конце или после любой «консервативной» обработки (которая должна содержать нетто-распределение). Обычно они заканчиваются связанным списком или хеш-таблицей под капотом, а не массивом. Но для начинающего упражнения я был бы в порядке с этим. Короче говоря, прохождение через выделенную кучу, чтобы найти элемент 'free'ed, будет неприемлемо медленным почти в любом практическом приложении. – Persixty

+1

@ DanAllen да, конечно, это неэффективно, но я не собирался решать его школьное задание, просто указал ему в одном направлении –

+0

Я знаю, мои комментарии не предназначены для критики. Я просто указывал, где заканчивается ваше «игрушечное» решение. – Persixty

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