двойная бесплатная проблема - PullRequest
3 голосов
/ 17 сентября 2009

Как решить проблему двойного освобождения, написав функцию-обертку с именем free, чтобы мне не приходилось менять каждый бесплатный вызов в моем исходном коде?

Ответы [ 6 ]

28 голосов
/ 17 сентября 2009

Не делай этого.

Нет, правда. Исправить актуальную проблему. Как только функция free () была вызвана для указателя, ваш код должен не удерживать его по любой причине. Обнулите его, чтобы вы не смогли его снова освободить; это также сделает видимыми любые другие проблемы, вызванные разыменованием устаревшего указателя.

8 голосов
/ 17 сентября 2009

Не делай этого

Простой способ - определить вашу функцию-обертку и #define бесплатно вызывать вашу функцию.

#undef free
#define free(x) wrapper_free(x)
/* ... */
int *data = malloc(42);
free(data); /* effectively calls wrapper_free(data) */

но ... не делай этого!

7 голосов
/ 18 сентября 2009

Следующий код перехватывает вызовы malloc(), realloc() и calloc() для регистрации.

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

Заголовок memdebug.h:

#undef free
#define free(PTR) memdebug_free(PTR, #PTR, __FILE__, __func__, __LINE__)

#undef malloc
#define malloc(SIZE) memdebug_log(malloc(SIZE))

#undef realloc
#define realloc(PTR, SIZE) memdebug_log(realloc(PTR, SIZE))

#undef calloc
#define calloc(COUNT, SIZE) memdebug_log(calloc(COUNT, SIZE))

#ifndef MEMDEBUG_H
#define MEMDEBUG_H

extern void memdebug_free(void *ptr, const char *ptr_arg, const char *file, 
    const char *func, int line);
extern void *memdebug_log(void *ptr);

#endif

Источник memdebug.c:

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

#ifndef MEMDEBUG_TABLE_SIZE
// log 16k allocations by default
#define MEMDEBUG_TABLE_SIZE 0x4000
#endif

static void *alloc_table[MEMDEBUG_TABLE_SIZE];
static size_t top;

void *memdebug_log(void *ptr)
{
    assert(top < sizeof alloc_table / sizeof *alloc_table);
    alloc_table[top++] = ptr;
    return ptr;
}

void memdebug_free(void *ptr, const char *ptr_arg, const char *file,
    const char *func, int line)
{
    if(!ptr)
    {
        fprintf(stderr,
            "%s() in %s, line %i: freeing null pointer `%s` -->continue\n",
            func, file, line, ptr_arg);

        return;
    }

    for(size_t i = top; i--; )
    {
        if(ptr == alloc_table[i])
        {
            free(ptr);
            --top;
            if(i != top) alloc_table[i] = alloc_table[top];
            return;
        }
    }

    fprintf(stderr,
        "%s() in %s, line %i: freeing invalid pointer `%s`-->exit\n",
        func, file, line, ptr_arg);

    exit(EXIT_FAILURE);
}
1 голос
/ 18 сентября 2009

Помимо всех других ответов и ссылок на развитие управления памятью и т. Д.

Когда происходит двойное освобождение, в коде появляются ошибки, указывающие на то, что все сделано неправильно, и т. Д.

Когда я начал работать с Java около 10 лет назад, все хвалили Java, поскольку вам не нужно было беспокоиться о новых / удаленных файлах.

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

Поэтому, когда у вас есть эти проблемы, у вас, вероятно, рано или поздно возникают другие проблемы. Ресурсы задержаны, соединения с БД используются, файлы заблокированы в файловых системах.

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

1 голос
/ 17 сентября 2009

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

Если вы ищете однострочный фрагмент кода, который позволит вам освободить и очистить указатель, я бы рекомендовал использовать макрос, хотя есть и другие способы сделать это (создать метод, который принимает указатель на указатель).

#define FREEANDCLEAR(pointer)\
{\
   free(pointer);\
   pointer = 0;\
}

EDIT Как упомянул Кристоф в комментарии, вы также можете убедиться, что пользователь использует макрос как функцию (завершает строку точкой с запятой, используя do, например, так:

#define FREEANDCLEAR(pointer)\
do {\
   free(pointer);\
   pointer = 0;\
} while(0)

, который будет выполнен один раз и потребует завершающей точки с запятой.

0 голосов
/ 17 сентября 2009

Просмотрите ответы на вопрос stackoverflow, "Напишите свой собственный менеджер памяти" .

Использование инструментов или приемов в этих ответах позволит вам включить диспетчер памяти, который проверяет эти типы проблем, чтобы вы могли их идентифицировать и исправить (как показывают многие другие ответы на ваш вопрос, не стоит использовать что-то как это в качестве обходного пути).

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