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

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

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

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

int temp = 3;

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

Подробнее о стеке и куче:

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

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

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

В следующем ответе см. дальнейшее чтение в стеке против кучи.

Указатели

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

Ответ 4

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

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

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

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

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

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

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

-

Теперь, некоторые примечания:

{
    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".)

Ответ 5

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

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

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

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

Ответ 6

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

Как сказал ранее Брайан Бонди, переменные ( "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().

Ответ 7

В дополнение к точкам, сделанным этот пост, разъясняет далее.

Ответ 8

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

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

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