Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс ПЯВУ 2 сем / Лекции 2 сем / Л№26.Динамическая память / Лекция № 25. Динамическая память t.odt
Скачиваний:
12
Добавлен:
17.04.2015
Размер:
39.75 Кб
Скачать

3. Карта памяти программы на языке с

Скомпилированная программа С имеет четыре логически обособленные области памяти. Первая — это область памяти, содержащая выполнимый код программы. Во второй области хранятся глобальные переменные. Оставшиеся две области — это стек и динамически распределяемая область памяти[1]. Стек используется для хранения вспомогательных переменных во время выполнения программы. Здесь находятся адреса возврата функций, аргументы функций, локальные переменные и т.п. Текущее состояние процессора также хранится в стеке. Динамически распределяемая область памяти, или куча — это такая свободная область памяти, для получения участков памяти из которой программа вызывает функции динамического распределения памяти. На рис. 1.2 показано, как распределяется память во время выполнения программы. Но не следует забывать, что конкретное распределение может быть разным в зависимости от типа процессора и реализации языка.

------------------------------------------------------------------------------------------------------------------------

Стек

------------------------------------------------------------------------------------------------------------------------

Динамически распределяемая область памяти

------------------------------------------------------------------------------------------------------------------------

* Глобальные переменные

------------------------------------------------------------------------------------------------------------------------

Код программы

------------------------------------------------------------------------------------------------------------------------

Рис.1.Распределение памяти(карта памяти)при выполнении программ,написанной на языке C

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

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

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

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

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

      Операторы new и delete языка C++

В языке C++ для захвата и освобождения динамической памяти используются операторы new и delete. Они являются частью языка C++, в отличие от функций malloc и free, входящих в библиотеку стандартных функций Си.

Пусть T - некоторый тип языка Си или C++, p - указатель на объект типа T. Тогда для захвата памяти размером в один элемент типа T используется оператор new:

T *p;

p = new T;

Например, для захвата восьми байтов под вещественное число типа double используется фрагмент

double *p;

p = new double;

При использовании new, в отличие от malloc, не нужно приводить указатель от типа void* к нужному типу: оператор new возвращает указатель на тип, записанный после слова new. Сравните два эквивалентных фрагмента на Си и C++:

double *p;

p = (double*) malloc(sizeof(double));

double *p;

p = new double;

Конечно, второй фрагмент гораздо короче и нагляднее.

Оператор new удобен еще и тем, что можно присвоить начальное значение объекту, созданному в динамической памяти (т.е. выполнить инициализацию объекта). Для этого начальное значение записывается в круглых скобках после имени типа, следующего за словом new. Например, в приведенной ниже строке захватывается память под вещественное число, которому присваивается начальное значение 1.5:

double *p = new double(1.5);

Этот фрагмент эквивалентен фрагменту

double *p = new double;

*p = 1.5;

С помощью оператора new можно захватывать память под массив элементов заданного типа. Для этого в квадратных скобках указывается длина захватываемого массива, которая может представляться любым целочисленным выражением. Например, в следующем фрагменте в динамической памяти захватывается область для хранения вещественной матрицы размера m*n:

double *a;

int m = 100, n = 101;

a = new double[m * n];

Такую форму оператора new иногда называют векторной.

Оператор delete освобождает память, захваченную ранее с помощью оператора new, например,

double *p = new double(1.5); // Захват и инициализация

. . .

delete p; // Освобождение памяти

Если память под массив была захвачена с помощью векторной формы оператора new, то для ее освобождения следует использовать векторную форму оператора delete, в которой после слова delete записываются пустые квадратные скобки:

double *a = new double[100]; // Захватываем массив

. . .

delete[] a; // Освобождаем массив

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

Приятная особенность оператора delete состоит в том, что при освобождении нулевого указателя ничего не происходит. Например, следующий фрагмент вполне корректен:

double *a = 0; // Нулевой указатель

bool b;

. . .

if (b) {

a = new double[1000];

. . .

}

. . .

delete[] a;

Здесь в указатель a вначале записывается нулевой адрес. Затем, если справедливо некоторое условие, захватывается память под массив. Таким образом, при выполнении оператора delete указатель a содержит либо нулевое значение, либо адрес массива. В первом случае оператор delete ничего не делает, во втором освобождает память, занятую массивом. Такая технология применяется практически всеми программистами на C++: всегда инициализировать указатели на динамическую память нулевыми значениями и в результате не иметь никаких проблем при освобождении памяти.

Попытка освобождения нулевого указателя с помощью стандартной функции free может привести к аварийному завершению программы (это зависит от используемой Си-библиотеки: нормальная работа не гарантируется стандартом ANSI).