Различия между динамической памятью и “обычной” памятью

Epydoc также является хорошим инструментом для документирования исходного кода и обрабатывает перекрестные ссылки на классы, модули, переменные и т. Д. В HTML, PDF, LaTeX. Рисует также хорошие диаграммы наследования классов. Он используется рядом проектов с открытым исходным кодом, поэтому он довольно активно развивается.

11
задан 2 revs, 2 users 94% 21 June 2009 в 14:09
поделиться

5 ответов

Примечание: Этот ответ слишком длинный . Я когда-нибудь его урежу. Между тем, прокомментируйте, если вы можете придумать полезные изменения.


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

Стек

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

Простой пример

int main(int argc, char * argv[])
{
    int a = 3;
    int b = 4;
    return a + b;
}

В этом случае у вас есть один ящик на полу с переменными argc (целое число), argv (указатель на массив символов), 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 .

Это Пример иллюстрирует два интересных эффекта.

  1. Как вы, вероятно, знаете, a и b передавались по значению. Вот почему есть копия этих переменных в поле для do_stuff .

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

Box overflow

    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 , содержащего массив. Что происходит, когда возвращается 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;
}

Поскольку возврат функции не изменяет кучу, ваш драгоценный интаррей уцелел. Не забудьте удалить [] его, когда закончите.

20
ответ дан 3 December 2019 в 02:41
поделиться

Память, выделенная «новым», оказывается в куче.

Память, выделенная в функции, находится внутри функции, в которой функция помещается в стек.

Прочтите о стеке vs распределение кучи здесь: http://www-ee.eng.hawaii.edu/~tep/EE160/Book/chap14/subsection2.1.1.8.html

4
ответ дан 3 December 2019 в 02:41
поделиться

Память, выделенная с помощью оператора new, извлекается из раздела памяти, называемого «куча», в то время как статические выделения для переменных используют раздел памяти, совместно используемый с вызовами процедур / функций («стек»).

Вам нужно только беспокоиться о распределения динамической памяти, которые вы сделали сами с помощью новых переменных, которые известны во время компиляции (определены в исходном коде), автоматически освобождаются в конце своей области (конец функции / процедуры, блок, ...).

2
ответ дан 3 December 2019 в 02:41
поделиться

Большая разница между «динамической» и «обычной» памятью довольно хорошо отражена в самом вопросе.

Динамическая память не очень хорошо поддерживается C ++.

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

Вам нужен дополнительный указатель, так как вам каким-то образом нужен доступ к вашей новой памяти. Некоторая память (динамическая или нет) - это прежде всего то, с чем не может справиться язык программирования. У вас должен быть к нему доступ. Это делается с помощью переменных. Но переменные в таких языках, как C ++, хранятся в «обычной» памяти. Итак, вам нужны «указатели» - указатели - это форма косвенного обращения, которая говорит: «Нет, я не то значение, которое вы ищете, но я указываю на него». Указатели - единственная возможность в C ++ для доступа к динамической памяти.

Напротив, к «обычной» памяти можно обращаться напрямую, выделение и освобождение выполняется автоматически самим языком.

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

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

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

1
ответ дан 3 December 2019 в 02:41
поделиться

Динамическая память находится в куче, а не в стеке. Время жизни динамической памяти - от времени выделения до момента освобождения. С локальными переменными их время жизни ограничено функцией / блоком, в котором они определены.

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

Что касается автоматической памяти. управления, Стандартная библиотека C ++ предоставляет для этого auto_ptr.

4
ответ дан 3 December 2019 в 02:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: