Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

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

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

 

 

Большие адреса

Меньшие адреса

Стек
Динамическая область памяти (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

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...