Почему я должен использовать указатель free, а не обычное объявление? - PullRequest
12 голосов
/ 09 марта 2009

Почему я должен использовать free (), когда я объявляю указатель, такой как:

int *temp = (int*)malloc(sizeof(int))
*temp = 3;

но не тогда, когда я делаю:

int temp = 3;

Ответы [ 8 ]

27 голосов
/ 09 марта 2009

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

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

12 голосов
/ 09 марта 2009

Вам не всегда нужно использовать free для указателя, только те, которые объявлены с помощью malloc. Вы можете объявить указатель, указывающий на область памяти в стеке

int a = 3;
int* p = &a;

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

8 голосов
/ 09 марта 2009

Потому что язык позволяет выбирать между стеком и кучей.

Причины, по которым вы хотите выбирать между стеком и кучей:

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

Почему куча не может быть автоматически освобождена:

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

Больше в стеке против кучи:

Язык Си позволяет вам выбрать, хотите ли вы определить свои переменные в стеке или в куче.

  • Переменные в стеке автоматически освобождаются, когда они выпадают из scope .
  • Переменные в куче не освобождаются автоматически.

malloc создает переменные в куче. Простое объявление, такое как int x; создает переменную в стеке.

См. Дальнейшее чтение стека против кучи в моем ответе здесь .

Указатели:

Просто для пояснения: переменные указателя создаются в стеке и содержат адрес памяти для данных, размещенных в куче. Они занимают 4 байта в стеке в 32-разрядной системе и 8 байтов в стеке в 64-разрядной системе.

5 голосов
/ 09 марта 2009

Следует отметить, что C не имеет понятия о стеке или куче, хотя предыдущие ответы верны ~ 99% времени и дают отличное понимание.

C определяет три срока хранения объектов: статический , автоматический и выделенный . (§6.2.4.1)

Статические объекты (например, глобальные переменные) доступны на протяжении всей программы.

Автоматические объекты существуют, пока их переменная находится в области видимости. Они перестают существовать, как только это выходит за рамки.

Обратите внимание, что это две крайности. C дает вам точку между: Выделенными объектами. (Поисковым термином будет динамически выделенная память .) С этими словами вы сообщаете компьютеру, когда объекты должны начинаться и заканчивать свое существование. И это делается с помощью стандартных функций malloc () (или производных) и free () .

Строго говоря, вам не нужно звонить бесплатно () . Или, может быть, вы делаете (вам нужно прочитать стандарт для авторизованного пункта по этому вопросу), но вы можете сделать все это в конце main () , непосредственно перед завершением программы. Или оставьте это для операционной системы, чтобы сделать это для вас (что большинство, если не все, делают). Но это опять-таки было бы конечностью - объекты появляются, когда вы вызываете malloc () , go после завершения вашей программы.

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

-

Теперь некоторые заметки:

{
    int *temp = malloc(sizeof(int));
    *temp = 5;
    //free(temp);
}

Обратите внимание, что temp здесь - автоматический объект. Он будет жить только до тех пор, пока его область действия заканчивается на}. Объект указывает на , однако выделен. Он будет существовать до тех пор, пока вы не вызовете free () по его адресу. Поскольку temp содержит единственную копию этого адреса, вы потеряете возможность звонить free (), как только temp выйдет за рамки. Некоторая память будет постоянно выделена, но недоступна. Это называется утечка памяти .

Сборка мусора - это еще один метод управления хранилищем объектов. Реализация в C может выглядеть так:

{
    int *temp = gc_malloc(sizeof(int));
    *temp = 5;
}

Где в} компьютер решит, что последняя ссылка, temp, на выделенный объект была потеряна, и что было бы неплохо освободить его.

Это компромисс, когда вам не нужно беспокоиться о свободных () объектах (что не является незначительной вещью, так как простые примеры могут заставить вас думать), но здесь gc_malloc () более сложна, чем просто malloc (), и там выполняется невидимый код}, где temp выходит из области видимости. И это совсем другая тема, как компьютер может решить, что temp было последней ссылкой. (Некоторые практические решения могут включать в себя написание большего кода вокруг «int * temp».)

3 голосов
/ 09 марта 2009

Альнитак правильный. Я хотел бы указать, что на самом деле означает «в стеке ».

Когда программа выполняет вызов функции, она ожидает, что функция выполнит некоторое количество работы, затем вернется и перейдет к следующей строке кода. Функция не может знать, куда возвращаться после завершения функции. Таким образом, машина имеет стек вызовов для каждой программы, используемой для передачи адреса следующего оператора в программе перед вызовом функции. Оператор return просто выводит адрес программы и переходит на него.

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

Напротив, вызов malloc() выделяет память из кучи, которая явно выделяет память для программы и остается в области действия до тех пор, пока программа выполняется. Таким образом, если вы не free() памяти, она останется выделенной и считается утечкой памяти.

2 голосов
/ 09 марта 2009

Необходимость free() зависит не от того, объявили ли вы указатель, а от того, есть ли у вас malloc() ed память.

Как сказал ранее Брайан Бонди, переменные ("int number", "char string[10]", "float your_boat" и т. Д.) Исчезают, когда выходят из области видимости, например, когда ваш код покидает функциональный блок. Таким образом, указатель в вашем вопросе ("temp") не исчезает, когда вы вызываете free() - скорее, независимо от того, какой код был выделен при вызове malloc(), он исчезает. Ваш указатель все еще остается там, то есть сразу после вашего примера кода вы можете сказать «temp = &some_other_variable», не говоря (снова) «int *temp;».

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

int * temp = (int*)malloc(sizeof(int));

без последующего высказывания

free(temp);

Но это не тот способ, которым реализовано malloc().

0 голосов
/ 09 марта 2009

В дополнение к сделанным пунктам этот пост проясняет ситуацию дальше.

0 голосов
/ 09 марта 2009

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

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

Более новые языки, такие как Java и C #, сделали это.

...