Подтвердить что ты не робот

Свободная память выделяется в другой функции?

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

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

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

typedef struct Entry {
    struct Entry *previous;
    int value;
} Entry;

void destroyEntry(Entry entry);

int main(int argc, char *argv[])
{
    Entry* apple;
    apple = malloc(sizeof(Entry));
    destroyEntry(*(apple));
    return 0;
}

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Когда я запускаю его через valgrind с --leak-check=full --track-origins=yes, я получаю следующую ошибку:

==20674== Invalid free() / delete / delete[] / realloc()
==20674==    at 0x4028E58: free (vg_replace_malloc.c:427)
==20674==    by 0x80485B2: destroyEntry (testing.c:53)
==20674==    by 0x8048477: main (testing.c:26)
==20674==  Address 0xbecc0070 is on thread 1 stack

Я думаю, что эта ошибка означает, что функции destroyEntry не разрешено изменять память, явно выделенную в основном. Это правильно? Почему я не могу выделить free память, которую я выделил в main, в другой функции? (и такое поведение как-то специфично для основного?)

4b9b3361

Ответ 1

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

Вы должны изменить свою функцию, чтобы взять указатель, а затем вы можете вызвать вызов free непосредственно на этом указателе.

Ответ 2

Это передача по значению, что означает, что создается копия, поэтому вы пытаетесь освободить память, где находится локальная переменная entry. Обратите внимание, что entry - это объект с автоматической продолжительностью хранения, и память, в которой он находится, будет автоматически освобождена, когда ваша программа выходит из области действия destroyEntry.

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

Ваша функция должна взять указатель (передача по ссылке):

void destroyEntry(Entry *entry)
{
    free(entry);
}

Затем вместо destroyEntry(*(apple)); вы просто вызываете destroyEntry(apple);. Обратите внимание: если нет другой функции, связанной с функцией destroyEntry, она избыточна и лучше просто вызвать free(apple).

Ответ 3

Другие ответы здесь указывают на основную проблему - потому что вы разыскиваете свое яблоко, когда вы вызываете destroyEntry в main(), он передает по ссылке, создавая копию.

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

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

int main()
{
  Entry * apple;
  apple = malloc(sizeof(Entry));
  printf("apple address = %p", apple);  // Prints the address of 'apple'
  free(apple);   // You know this will work
}

После этого вы заметите, что оператор printf() дал вам адрес, похожий на 0x8024712 (просто составляющий адрес в правом общем диапазоне), но ваш вывод valgrind дал 0x4028E58. Вы заметили бы, что они находятся в двух разных местах (фактически, "0x4..." находится в стеке, а не куче, из которой выделяется malloc(), но я предполагаю, что вы только начинаете а не красный флаг для вас), поэтому вы знаете, что пытаетесь освободить память из-за неправильного места, следовательно, "invalid free()".

Итак, оттуда вы можете сказать себе: "Ладно, как-то мой указатель становится испорченным". Вы уже свалили свою проблему на небольшой, компилируемый пример, поэтому вам не потребуется много времени, чтобы решить эту проблему.

TL; DR - при работе с ошибками, связанными с указателями, попробуйте распечатать адреса или найти их в своем любимом отладчике. Это часто по крайней мере указывает вам в правильном направлении.

Ничто из этого не должно препятствовать отправке вашего вопроса на Stack Exchange, конечно. Сотни программистов, скорее всего, выиграют от того, что вы это сделали.