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

Лекция № 25.ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ.

Оглавление.

    1. Модели памяти.

    2. Статический и динамический.

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

    4. Операторы ДРП.

    5. Основные функции ДРП.

    6. Динамические массивы.

    7. Примеры работы с ДП.

    8. Менеджер ДП.

    9. Дополнительные функции ДРП.

    10. Вопросы для самопроверки.

Введение

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

1. Модели памяти

Виды моделей

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

В BC++2.0 существуют несколько видов моделей памяти: tiny, small, medium, compact, large, huge. Программа может быть откомпилирована в любой из этих моделей, если установить соответствующий флажок в Opions-Compiler-Code Generation-Model. При этом используются различные наборы встроенных библиотек и создаются различные obj-файлы. Например, для модели large в директории Lib имеются файлы C0l.obj, Cl.lib, Mathl.lib и другие. Возможная ошибка на этапе линковки (“Не могу найти файл Сol.obj”) вызвана неполной версией пакета и неверным выбором модели.

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

Когда программа загружается в ОЗУ для выполнения, ей отводится определенное место, в котором можно выделить несколько областей: область кода (ОК); область данных, глобальных и статических переменных(ОД); стек; куча (heap или динамическая память).

Таблица 1

вид модели максимальный размер

памяти Область область стек куча взаимное расположение

кода данных

tiny 64К 64К 64К 64К все области в одном сегменте

small 64К 64К 64К 64К ОД,стек и куча в одном сегменте

medium 1М 64К 64К 64К различные сегменты

compact 1М 64К 64К 1М различные сегменты

large 1М 64К 64К 1М различные сегменты

huge 1М 64К 64К 1М различные сегменты

Модель Large

Модели памяти устроены по-разному. Рассмотрим расположение областей памяти в модели large.

PSP код данные стек куча 640K

Рис. 1

PSP представляет собой префикс программного сегмента, занимает 256 байт и помещается перед исполняемыми файлами при их загрузке в память. Он содержит переменные, используемые MS-DOS для управления программой, а также место для переноса данных окружения.

Область кода содержит машинные коды функций программы. Функции, присоединенные к exe-файлу на стадии линковки, размещаются вне области кода.

Область данных содержит глобальные и статические переменные, строковые константы.

В стеке размещаются локальные переменные, параметры, передаваемые функциям, и ряд других данных. Как правило, стек растет сверху вниз, занимая пульсирующую непрерывную область. В случае переполнения стека происходит его "налезание" стека на область данных и выдается соответствующее сообщение. Проверка стека увеличивает время работы программы и ее можно отключить в Options-Entry/Exit Code Generation-Stack options-Test stack overflow.

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

2. Статический и динамический.

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

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

· глобальные (внешние) переменные размещаются в общем сегменте данных и получают в нем свои адреса уже при трансляции. Аналогично происходит их инициализация (присваивание начальных значений);

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

В Си имеет место некоторая путаница с термином статическая переменная. Если исходить из общего понимания термина «статический», то все известные нам до настоящего времени виды переменных (условно называемые нами глобальные и локальные, см.1.6) являются статическими. Это связано с общим для всех переменных подходом: тип переменной и ее размерность определяются при трансляции и в дальнейшем не меняются. Каждым тип данных имеет, аналогично, фиксированную размерность. Таким образом, транслятором осуществляется статическое распределение памяти под переменные программы, т.е. за переменными уже во время трансляции закрепляются известные адреса (или относительные смещения) в соответствующих сегментах памяти программы.

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

Но при написании многих программ заранее неизвестна размерность обрабатываемых данных. При использовании обычных переменных в таких случаях возможен единственный выход - определять размерность «по максимуму». В ситуации, когда требуется обработать данные еще большей размерности, необходимо внести изменения в текст программы и перетранслировать ее. Для таких целей используется команда препроцессора #define с тем, чтобы не менять значение одной и той же константы в нескольких местах программы.

//-------------------------------------------------------------------------------

// Модель стека в массиве ограниченной размерности

#define SZ 1000

int A[SZ],sp = -1;

int PUSH(int vv){

if (sp==SZ) return 0;

A[++sp]=vv;

return 1;}

Динамические переменные

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

С этой целью на уровне библиотек в Си создан механизм порождения и уничтожения переменных самой работающей программой. Такие переменные называются динамическими, а область памяти, в которой они создаются – динамической памятью или «кучей». Куча организуется в одном или нескольких дополнительных сегментах памяти, выделяется программе операционной системой. Все в целом называется также системой динамического распределения памяти (ДРП). Перечислим основные свойства динамических переменных:

· динамические переменные создаются и уничтожаются работающей программой путем выполнения специальных операторов или вызовов функций;

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

· динамическая переменная не имеет имени, доступ к ней возможен только через указатель;

· функция создания динамической переменной ищет в «куче» свободную память необходимого размера и возвращает указатель на нее (адрес);

· функция уничтожения динамической переменной получает указатель на уничтожаемую переменную.

Наиболее важным свойством динамической переменной является ее «безымянность» и доступность по указателю. Кстати, это хорошо согласуется с одним из названий операции * - разыменование указателя. Таким образом, динамическая переменная не может быть доступна «сама по себе», а только опосредованно через другие переменные или обычные именованные указатели. Динамическая переменная может, в свою очередь, содержать один или несколько указателей на другие динамические переменные. В этом случае мы получаем динамические структуры данных, в которых количество переменных и связи между ними могут меняться в процессе работы программы (списки, деревья);

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

· если динамическая переменная создана, а указатель на нее «потерян» программой, то такая переменная представляет собой «вещь в себе» -существует, но недоступна для использования. Тем не менее, занимаемая ею память остается за программой;

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