Ответ 1
Примечание: Этот ответ слишком длинный. Я когда-нибудь позабочусь об этом. Между тем, комментарий, если вы можете думать о полезных изменениях.
Чтобы ответить на ваши вопросы, сначала нужно определить две области памяти, которые называются стек и куча.
Стек
Представьте себе стек как стек ящиков. Каждый ящик представляет собой выполнение функции. В начале, когда вызывается main
, на полу есть одна коробка. Любые локальные переменные, которые вы определяете, находятся в этом поле.
Простой пример
int main(int argc, char * argv[])
{
int a = 3;
int b = 4;
return a + b;
}
В этом случае у вас есть одно поле на полу с переменными argc
(целое число), argv
(указатель на массив char), a
(целое число) и b
(целое число).
Более одного окна
int main(int argc, char * argv[])
{
int a = 3;
int b = 4;
return do_stuff(a, b);
}
int do_stuff(int a, int b)
{
int c = a + b;
c++;
return c;
}
Теперь у вас есть поле на полу (для main
) с argc
, argv
, a
и b
. В верхней части этого окна у вас есть еще один ящик (для do_stuff
) с a
, b
и c
.
В этом примере показаны два интересных эффекта.
-
Как вы, наверное, знаете,
a
иb
были переданы по значению. Вот почему есть копия этих переменных в поле дляdo_stuff
. -
Обратите внимание, что вам не нужно
free
илиdelete
или что-либо другое для этих переменных. Когда ваша функция вернется, поле для этой функции будет уничтожено.
Переполнение контейнера
int main(int argc, char * argv[])
{
int a = 3;
int b = 4;
return do_stuff(a, b);
}
int do_stuff(int a, int b)
{
return do_stuff(a, b);
}
Здесь у вас есть поле на полу (для main
, как и раньше). Затем у вас есть поле (для do_stuff
) с a
и b
. Затем у вас есть еще одна коробка (для вызова do_stuff
), снова с a
и b
. А потом еще один. И скоро у вас переполнение стека.
Сводка стека
Подумайте о стеке как стеке ящиков. Каждый блок представляет собой выполнение функции, и это поле содержит локальные переменные, определенные в этой функции. Когда функция вернется, это окно будет уничтожено.
Более технические характеристики
- Каждый "ящик" официально называется фреймом стека.
- Вы когда-нибудь замечали, как ваши переменные имеют "случайные" значения по умолчанию? Когда старый стек стека "разрушен", он просто перестает быть релевантным. Он не обнуляется или ничего подобного. В следующий раз, когда кадр стека использует этот раздел памяти, вы видите биты старого стекового кадра в ваших локальных переменных.
Куча
Здесь происходит динамическое распределение памяти.
Представьте себе кучу как бесконечный зеленый луг памяти. Когда вы вызываете malloc
или new
, в куче выделяется блок памяти. Вам предоставляется указатель на доступ к этому блоку памяти.
int main(int argc, char * argv[])
{
int * a = new int;
return *a;
}
Здесь в куче выделяется новое целочисленное значение памяти. Вы получаете указатель с именем a
, который указывает на эту память.
-
a
- локальная переменная, поэтому она находится в полеmain
"
Обоснование для распределения динамической памяти
Конечно, использование динамически распределенной памяти, похоже, направлено на несколько байтов здесь и там для указателей. Однако есть вещи, которые вы просто не можете (легко) сделать без динамического распределения памяти.
Возврат массива
int main(int argc, char * argv[])
{
int * intarray = create_array();
return intarray[0];
}
int * create_array()
{
int intarray[5];
intarray[0] = 0;
return intarray;
}
Что здесь происходит? Вы "возвращаете массив" в create_array
. В действительности вы возвращаете указатель, который просто указывает на часть create_array
"box", которая содержит массив. Что произойдет, когда create_array
вернется? Его коробка уничтожена, и вы можете ожидать, что ваш массив станет поврежденным в любой момент.
Вместо этого используйте динамически выделенную память.
int main(int argc, char * argv[])
{
int * intarray = create_array();
int return_value = intarray[0];
delete[] intarray;
return return_value;
}
int * create_array()
{
int * intarray = new int[5];
intarray[0] = 0;
return intarray;
}
Поскольку возвращаемая функция не изменяет кучу, ваш драгоценный intarray
выходит невредимым. Помните delete[]
его после того, как вы закончите.