Семинар №11

Динамическая память

Для начала обсудим особенности каждого вида памяти.

Статическая память

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

Туда попадают глобальные переменные (объявленные вне любых блоков, в частности, до начала метода main) и статические переменные (перед типом которых указывается ключевое слово static, при этом переменная может находится где угодно, в том числе и в теле какой-то функции, но вести статическая переменная внутри функции себя будет специфически: сохранять своё значения между повторными вызовами функции). Разница между статической и глобальной переменной проявляется, когда программа состоит из нескольких файлов: глобальные переменные доступны в смежных файлах, а статические — только в том файле, где были объявлены.

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

Локальная память (стековая память)

Выделяется при входе в блок программы, освобождается — при выходе из блока.

Туда попадают переменные объявленные внутри блоков (в частности, в теле функций).

Динамическая память

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

Доступ к динамической памяти возможен только через указатели, т. е. нельзя создавать в неё переменные, но можно выделять её фрагменты и связывать из с некоторым указателем.

Работа с динамической памятью

Выделяется память с помощью оператора new, а освобождается — с помощью оператора delete.

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

int* p;
p = new int;
*p = 10;
cout << *p; // 10
delete p; // память освобождена

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

При выделении одной динамической переменной (одной ячейки памяти), можно сразу инициализировать её значение:

int* p;
p = new int(10);
cout << *p; // 10
delete p; // память освобождена

Можно выделять сразу несколько ячеек динамической памяти, получая динамический массив. Для этого его размер указывается в квадратных скобках после типа. Чтобы удалить динамический массив и освободить память используется оператор delete[].

int* p;
p = new int[13];
for (int i=0; i<13; i++) {
    *(p+i) = i + 1;
    cout << *(p+i) << ' '; // 1 2 3 ... 13
}
delete[] p; // память освобождена, из неё удалены все элементы

Cразу после создания динамический массив автоматически заполняется нулями (в отличии от обычного массива в статической или стековой памяти).

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

int* p;
p = new int(13);
int a = 666;
p = &a; // теперь до 13 никак не добраться

Проблема становится особенно острой, когда в памяти теряются целые массивы (они занимают больше места, чем отдельные переменные).

int* p;
for (int i=1; i<=10; i++) {
    p = new int[100];
}
delete[] p;

На каждом шаге цикла создаётся динамический массив из 100 элементов. Всего таких массивов будет создано 10, но только от последнего из них память будет освобождена после выхода из цикла. 9 массивов продолжат занимать место в памяти до конца программы. 9 массивов * 100 элементов * 4 байта = 3600 байт потерянной памяти, которую никак нельзя использовать (ни в этой программе, не в других запущенных).

Очень важно после использования динамической памяти не забывать освобождать её в нужный момент!

Задачи

  1. Программа должна создать два массива со случайными элементами из диапазона [-25;25], по 10 элементов в каждом массиве, вывести каждый массив на экран на отдельной строке. Затем программа должна создать динамический массив нужного размера и скопировать в него все положительные элементы из первых двух массивов. Вывести получившийся динамический массив на экран на отдельной строке.

    Пример работы программы программы:

    0 -4 5 -2 13 25 -25 7 -3 -1
    -6 11 23 -2 -12 0 9 0 -1 -3
    5 13 25 7 11 23 9
  2. Пользователь вводит строку с клавиатуры (максимальная длина строки — 80 символов). Программа должна выбрать из строки все чётные цифры (нуль отнести к ним), если они есть в строке, и поместить их в первый динамический массив, и все нечётные цифры, если они есть — поместить их во второй динамический массив. Вывести оба динамических массива (если они появились) на экран.

    Пример работы программы программы (на второй строке показан ввод пользователя):

    Введите строку:
    Я родился 17.03.1972 в 23 часа 45 минут
    0 2 2 4
    1 7 3 1 9 7 3 5
    
  3. Программа должна создать динамический массив из 20 случайных элементов из диапазона [1;9] и вывести его на экран. Далее программа должна скопировать во второй динамический массив только те элементы, которые в первом массиве встретились ровно 2 раза (если такие будут) и вывести второй массив на экран на отдельной строке.

← К списку семинаров