Уже освободил память - PullRequest
1 голос
/ 30 июля 2011

Есть ли способ в C узнать, был ли блок памяти ранее освобожден с помощью free ()? Могу ли я сделать что-то вроде ...

if(isFree(pointer))
{ 
    //code here
}

Ответы [ 5 ]

8 голосов
/ 30 июля 2011

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

Единственное место, где я видел, что это не работает, - это монолитные ядра, потому что страницы в памяти нуждаются в подсчете использования из-за общих сопоставлений между прочим.

В вашем случае просто установите неиспользуемые указатели в NULL и проверьте это. Это дает вам гарантированный способ узнать в случае, если у вас есть неиспользуемые поля в структурах, которые были неправильно размещены. Простое правило: везде, где вы освобождаете указатель, который необходимо проверить указанным выше способом, просто установите его в NULL и замените isFree () на if pointer == NULL. Таким образом, не нужно отслеживать количество ссылок, и вы точно знаете, правильный ли указатель и не указывает ли он на мусор.

5 голосов
/ 30 июля 2011

Нет, нет пути.

Однако вы можете использовать небольшую дисциплину кода следующим образом:

Всегда всегда всегда выделение защиты с помощью malloc:

void * vp;
if((vp = malloc(SIZE))==NULL){
   /* do something dreadful here to respond to the out of mem */
   exit(-1);
}

После освобождения указателя установите его на 0

free(vp); vp = (void*)0;
/* I like to put them on one line and think of them as one peration */

В любом месте, где у вас будет соблазн использовать вашу функцию «свободен», просто скажите

if(vp == NULL)[
    /* it's been freed already */
}

Обновление

@ Иисус в комментариях говорит:

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

Обычно это хорошая практика, когда это возможно; проблема в том, что в реальной жизни на С это часто невозможно. В качестве примера рассмотрим текстовый редактор, содержащий двусвязный список строк. Список действительно прост:

struct line {
    struct line * prev;
    struct line * next;
    char * contents;
}

Я определяю guarded_malloc функцию, которая выделяет память

void * guarded_malloc(size_t sz){
    return (malloc(sz)) ? : exit(-1); /* cute, eh? */
}

и создать список узлов с newLine()

struct line * newLine(){
    struct line * lp;
    lp = (struct line *) guarded_malloc(sizeof(struct line));
    lp->prev = lp->next = lp-contents = NULL ;
    return lp;
}

Я добавляю текст в строку s в мою строку

lp->contents = guarded_malloc(strlen(s)+1);
strcpy(lp->contents,s);

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

Теперь, как я могу реализовать удаление содержимого line, которое я создал, когда char * contents выходит из области видимости после освобождения?

3 голосов
/ 30 июля 2011

Я вижу, что никто не обратился к причине , почему то, что вы хотите, в принципе невозможно. Освободить ресурс (в данном случае память, но то же самое относится практически к любому ресурсу) означает вернуть его в пул ресурсов, где он доступен для повторного использования. Единственный способ, которым система могла бы дать разумный ответ: «Блок памяти по адресу X уже освобожден?» чтобы предотвратить повторное использование этого адреса, и сохранить с ним флаг состояния, указывающий, был ли он «освобожден». Но в этом случае он фактически не был освобожден , поскольку он не доступен для повторного использования.

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

1 голос
/ 30 июля 2011

Для решения для конкретной платформы вас может заинтересовать функция Win32 IsBadReadPtr (и другие подобные).Эта функция сможет (почти) предсказать, возникнет ли ошибка сегментации при чтении из определенного фрагмента памяти.

Примечание. IsBadReadPtr устарела в Microsoft.

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

Указатели не имеют никакой информации, кроме той, на которую они указывают.Лучшее, что вы можете сделать, это сказать: «Я знаю, как именно эта версия компилятора распределяет память, поэтому я разыменую память, переместу указатель назад на 4 байта, проверим размер, убедитесь, что он соответствует ...» и так далее.Вы не можете сделать это стандартным способом, так как распределение памяти определяется реализацией.Не говоря уже о том, что они, возможно, вообще не выделяли его динамически.

В дополнение к этому я рекомендую прочитать «1011 * Написание твердого кода » Стива МакГвайра.Отличные разделы по управлению памятью.

0 голосов
/ 30 июля 2011

В общем, единственный способ сделать это мобильно - это заменить функции выделения памяти.Но если вас беспокоит только ваш собственный код, то довольно распространенным методом является установка указателей на NULL после их free() их, поэтому любое последующее использование вызовет исключение или ошибку segfault:

  free(pointer);
  pointer = NULL;
...