Распределение памяти при работе программы

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

Схема распределения памяти под программу показана на следующем рисунке:

  Большие адреса Меньшие адреса Стек
Динамическая область памяти (heap - куча)
Область глобальных данных
Код программы

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

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

Однако во многих задачах невозможно заранее предсказать, сколько места (количество переменных, объемы массивов и т.д.) потребуется для решения задачи – это так называемые задачи с неопределенной размерностью. Решить эту проблему можно лишь в том случае, если иметь механизм, позволяющий создавать новые объекты по мере возникновения необходимости в этих объектах или изменять объемы памяти, выделенные под эти объекты (например, объемы массивов).

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

Динамическое выделение и освобождение памяти в стиле C++

Для динамического управления памятью в языке C++ используются две инструкции new и delete. Формат этих инструкций:

<Переменная-указатель> = new <Тип данных переменной-указателя>

delete <Переменная-указатель>

Инструкция new выделяет в динамической области участок памяти, достаточный для размещения данных, тип которых определяется типом данных переменной-указателя, и возвращает адрес этого участка. Этот адрес присваивается переменной-указателю. Например:

double *p; // Переменная-указатель на тип double

p = new double; // Выделение памяти

или так:

double *p = new double;

В этом примере инструкция new выделяет в динамической области участок памяти объемом sizeof (double) и присваивает адрес этого участка переменной-указателю p. Дальнейшая работа с переменной-указателем осуществляется как с обычным указателем на тип данных double. Например:

*p = 3.14;

cout << *p * 2 << endl; // На экран выведено значение 6.28

cin >> *p; // Вводим с клавиатуры некоторое вещественное значение

cout << *p << endl; // На экран выведено значение, введенное с клавиатуры

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

delete p;

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

Замечание. Инструкции new и delete это парные инструкции, то есть они всегда должны использоваться совместно – каждой инструкции new должна соответствовать инструкция delete. Динамическая область памяти автоматически освобождается только при завершении программы, поэтому неконтролируемое использование инструкции new может привести к переполнению динамической области памяти и, следовательно, к ошибкам в работе программы.

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

struct t_Person // Тип данных для "персоны"

{

char Fam[20]; // Фамилия

char Name[20]; // Имя

int Year; // Год рождения

};

setlocale (0, ""); // Русификация консоли

t_Person *p = new t_Person; // Создаем структуру в динамической памяти

strcpy ((*p).Fam, "Иванов"); // Заносим фамилию

strcpy ((*p).Name, "Иван"); // Заносим имя

(*p).Year = 1995; // Заносим год рождения

cout << "Фамилия: " << (*p).Fam << endl; // Выводим фамилию

cout << "Имя: " << (*p). Name << endl; // Выводим имя

cout << "Год рождения: " << (*p).Year << endl; // Выводим год рождения

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

Для обращения к отдельным полям структуры через переменную-указатель мы использовали следующие конструкции:

(*p).Fam, (*p).Name, (*p).Year

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

Существует другой способ доступа к полям структур через указатель на структуру с помощью оператора “стрелка” (не требующий предварительного разыменования указателя). Это делается так:

p -> Fam, p -> Name, p -> Year

То есть следующий вариант той же программы будет также корректным:

struct t_Person // Тип данных для "персоны"

{

char Fam[20]; // Фамилия

char Name[20]; // Имя

int Year; // Год рождения

};

setlocale (0, ""); // Русификация консоли

t_Person *p = new t_Person; // Создаем структуру в динамической памяти

strcpy (p -> Fam, "Иванов"); // Заносим фамилию

strcpy (p -> Name, "Иван"); // Заносим имя

p -> Year = 1995; // Заносим год рождения

cout << "Фамилия: " << p -> Fam << endl; // Выводим фамилию

cout << "Имя: " << p -> Name << endl; // Выводим имя

cout << "Год рождения: " << p -> Year << endl; // Выводим год рождения

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

Для некоторых типов данных одновременно с динамическим выделением памяти можно осуществлять и ее инициализацию. Например:

double *p = new double (3.14); // Инициализация значением 3.14

cout << *p << endl; // На экран выведено значение 3.14


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: