Установка условия, чтобы избежать двойного освобождения выделенной памяти - PullRequest
0 голосов
/ 23 ноября 2010

Ubuntu 10.10 gcc 4.4.4

Я просто экспериментирую с выделением и освобождением.

Однако я пытаюсь избежать проблемы, когда объект освобождается более одного раза.

Однако, когда я тестирую, я замечаю, что объект, который создан и освобожден, не возвращается в нулевое состояние.Так есть ли какие-либо условия, которые я могу установить, чтобы избежать, если это произойдет?

Я также попытался установить объект в NULL после освобождения.Тем не менее, он все еще пытался освободить объект.

Это ссылка на этот вопрос, просто вы знаете, не является дубликатом: освобождение выделенной памяти

Мой кодниже:

#include <stdio.h>
#include "objects.h"

int main(void)
{
    obj_t *obj = NULL;

    obj = create_object();

    destroy_object(obj);

    destroy_object(obj);

    return 0;
}

==

#ifndef OBJECTS_H_INCLUDED
#define OBJECTS_H_INCLUDED

typedef struct Obj_t obj_t;

obj_t* create_object();
void destroy_object(obj_t *obj);

#endif /* OBJECTS_H_INCLUDED */

==

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

#include "objects.h"

struct Obj_t {
    int obj_id;
};

obj_t* create_object()
{
    obj_t *obj = malloc(sizeof obj);

    return obj;
}

void destroy_object(obj_t *obj)
{
    if(obj != NULL) {
        printf("Object [ %d ] will be deleted\n", obj->obj_id);
        free(obj);
    }
}

==

OBJECT_FILES = objects.o main.o
CFLAGS = -Wall -Wextra -Wunreachable-code -ggdb -O0
CC = gcc
TARGET = obj

$(TARGET): $(OBJECT_FILES)
    $(CC) $(CFLAGS) $(OBJECT_FILES) -o $(TARGET)

main.o: main.c objects.c
    $(CC) $(CFLAGS) -c main.c

objects.o: objects.c
    $(CC) $(CFLAGS) -c objects.c 

clean:
    rm -f $(OBJECT_FILES) $(TARGET) *~

Ответы [ 6 ]

5 голосов
/ 23 ноября 2010

Проблема в том, что значение указателя в Main никогда не обновляется, потому что вы передаете копию указателя в destroy_object.Что вам нужно, это указатель на указатель.Попробуйте это:

void destroy_object(obj_t **obj)
{
    if(*obj != NULL) {
        printf("Object [ %d ] will be deleted\n", *obj->obj_id);
        free(*obj);
        *obj = NULL;
    }
}

Тогда:

destroy_object(&obj);
4 голосов
/ 23 ноября 2010

Вы не можете установить указатель на NULL внутри вашей функции destroy_object, потому что значение указателя внутри функции является копией указателя.

В C и C ++ то, как программисты избегают освобождения или удаления объекта дважды, заключается в том, чтобы не делал этого .

Если вы пишете программу, в которой вы не можете определить, сколько ссылок имеет объект, первое, что нужно сделать, это переосмыслить свой дизайн.

Тогда, если вам действительно нужно, используйте C ++ shared_ptr или реализуйте свою собственную схему подсчета ссылок в C. И Perl, и Python делают это таким же образом в своих реализациях на Си.

2 голосов
/ 23 ноября 2010

Как уже отмечали другие, установка obj в NULL внутри destroy_object() меняет локальную переменную и не действует в main().

Однако, если вы хотите иметь destroy_object() попытка предупредить программиста о двойном освобождении в качестве средства отладки, тогда вы можете использовать концепцию значения яд .В вашем примере вы можете зарезервировать какое-то конкретное недопустимое значение obj_id для значения «уже освобождено», скажем, -1.Ваша функция destroy_object() будет выглядеть следующим образом:

#define OBJ_POISON (-1)

void destroy_object(obj_t *obj)
{
    if (obj != NULL) {
        if (obj->obj_id == OBJ_POISON) {
            fprintf(stderr, "Double-free of object @ %p detected, aborting.\n", obj);
            abort();
        }

        printf("Object [ %d @ %p ] will be deleted\n", obj->obj_id, obj);
        obj->obj_id = OBJ_POISON; 
        free(obj);
    }
}

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

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

1 голос
/ 23 ноября 2010

Обычно для объекта рекомендуется иметь только одного владельца, который имеет право удалить его.В то же время вы можете сохранить один единственный указатель на этот объект и убедиться, что вы установили этот указатель в NULL после удаления объекта.Или всегда есть стандартное решение C ++: умный указатель.

0 голосов
/ 23 ноября 2010

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

0 голосов
/ 23 ноября 2010

Это не совсем новая идея, поэтому вы можете прочитать о Подсчет ссылок и Умные указатели .

Стандартная библиотека шаблонов (STL) и Boost оба интенсивно используют эти понятия.

...