Свободная память выделяется в другой функции?
Я пытаюсь изучить 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
, в другой функции? (и такое поведение как-то специфично для основного?)
Ответы
Ответ 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, конечно. Сотни программистов, скорее всего, выиграют от того, что вы это сделали.