Автор: Пользователь скрыл имя, 18 Января 2012 в 14:01, курсовая работа
Цель курсовой работы является изучение указателя и динамической памяти в Турбо Паскаль.
Для достижения поставленной цели необходимо решить следующие задачи:
- изучить понятия указателей и динамической памяти.
- рассмотреть процедуры и функции с использованием указателей.
- рассмотреть применение указателей и работы с динамической памятью.
- привести пример программы, использующей динамическую память.
ВВЕДЕНИЕ 3
1. ПОНЯТИЕ УКАЗАТЕЛЕЙ И ДИНАМИЧЕСКОЙ ПАМЯТИ. 5
1.1 ПОНЯТИЕ ДИНАМИЧЕСКОЙ ПАМЯТИ. 5
1.2 АДРЕСА И УКАЗАТЕЛИ. 6
1.3 ОБЪЯВЛЕНИЕ УКАЗАТЕЛЕЙ. 7
2. ВЫДЕЛЕНИЕ И ОСВОБОЖДЕНИЕ ДИНАМИЧЕСКОЙ ПАМЯТИ. 9
2.1 ИСПОЛЬЗОВАНИЕ УКАЗАТЕЛЕЙ. 13
2.2. ПРОЦЕДУРЫ И ФУНКЦИИ ДЛЯ РАБОТЫ С ДИНАМИЧЕСКОЙ ПАМЯТЬЮ. 16
2.3 АДМИНИСТРАТОР КУЧИ. 19
3. КОНТРОЛЬ ЗА ДИНАМИЧЕСКОЙ ПАМЯТЬЮ. 21
4. ПРИМЕР ПРОГРАММЫ С УКАЗАТЕЛЕМ И ДИНАМИЧЕСКОЙ ПАМЯТЬЮ. 23
ЗАКЛЮЧЕНИЕ 26
Function BlbckSize(Size: pointer): Longint;
{Функция
преобразует ненормализованную
длину свободного блока в
type
PtrRec = record
Lo, Hi : word
end;
var
LengthBlock: Longint;
begin
BlockSize := Longint(PtrRec(Size).Hi)*16 + PtrRec(Size).Lo
end;
Сразу после загрузки программы указатели HEAPPTR и FREELIST содержат один и тот же адрес, который совпадает с началом кучи (этот адрес содержится в указателе HEAPORG). При этом в первых 8 байтах кучи хранится запись, соответствующая типу TFREEREC (поле NEXT содержит адрес, совпадающий со значением HEAPEND, a поле SIZE - ноль, что служит дополнительным признаком отсутствия «ячеек» в динамической памяти). При работе с кучей указатели HEAPPTR и FREELIST будут иметь одинаковые значения до тех пор, пока в куче не образуется хотя бы один свободный блок ниже границы, содержащейся в указателе HEAPPTR. Как только это произойдет, указатель FREELIST станет ссылаться на начало этого блока, а в первых 8 байтах освобожденного участка памяти будет размещена запись TFREEREC. Используя FREELIST как начало списка, программа пользователя всегда сможет просмотреть весь список свободных блоков и при необходимости модифицировать его.
Описанный механизм вскрывает один не очень существенный недостаток, связанный с работой администратора кучи, а именно: в любой освободившийся блок администратор должен поместить описатель этого блока, а это означает, что длина блока не может быть меньше 8 байтов. Администратор кучи всегда выделяет память блоками, размер которых кратен размеру записи TFREEREC, т.е. кратен 8 байтам. Даже если программа запросит 1 байт, администратор выделит ей фактически 8 байт. Те же 8 байт будут выделены при запросе 2, 3 ,..., 8 байт; при запросе 9 байт будет выделен блок в 16 байт и т.д.
Если
при очередном обращении к
функции NEW или GETMEM администратор не
может найти в куче нужный свободный
блок, он обращается к функции, адрес
которой содержит переменная HEAPERROR.
Эта функция соответствует
type
HeapErrorFun = function (Size:word): Integer;
Здесь SIZE - размер той переменной, для которой нет свободной динамической памяти. Стандартная функция, адрес которой при запуске программы содержит переменная HEAPERROR, возвращает 0, что приводит к останову программы по ошибке периода счета с кодом 203. Можно переопределить эту функцию и таким образом блокировать останов программы. Для этого необходимо написать собственную функцию и поместить ее адрес в указатель HEAPERROR. Например:
Function HeapFunc(Size: Word): Integer; far;
begin
HeapFunc := 1 end;
begin {Основная программа}
HeapError := @HeapFunc;
.......
end.
Функция типа HEAPERRORFUN вызывается только в том случае, когда обращение с требованием выделения динамической памяти было неуспешным. Она может возвращать одно из трех значений:
0 - прекратить работу программы;
1
- присвоить соответствующему
2 - повторить выделение памяти; разумеется, в этом случае внутри функции типа HEAPERRORFUN необходимо освободить память нужного размера.
Как правило, объекты в Turbo Vision размещаются в куче. Это отвечает специфике диалоговых программ: на этапе разработки программист обычно не может учесть все возможные действия пользователя программы. Чтобы не накладывать неестественные ограничения на те или иные ее возможности, не следует злоупотреблять статическими определениями объектов, так как в этом случае программа не сможет гибко учитывать специфические требования пользователя.
В
правильно разработанной
В Turbo Vision имеются средства, упрощающие этот контроль: глобальная функция LowMemory будет возвращать True, если размер свободного участка кучи стал слишком мал (по умолчанию меньше 4 Кбайт). Таким образом, вместо того, чтобы контролировать кучу перед каждым обращением к New, можно обратиться к функции LowMemory перед началом размещения динамического объекта или сразу после того, как объект размещен в куче. Если LowMemory возвращает True, дальнейшая работа с кучей возможна только после ее очистки. Резервный участок кучи длиной в 4 Кбайт называется пулом надежности. Предполагается, что его размеры достаточны для размещения любого объекта Turbo Vision, поэтому обычно контроль с помощью LowMemory осуществляется сразу после процедуры динамического размещения нового видимого элемента.
В следующем примере создается простое диалоговое окно:
Uses Memory,...;{Функция LowMemory определена в модуле Memory}
.....
R.Assign(20,3,60,10);
D := New(Dialog, Init(R, 'Диалоговое окно'));
with D do
begin
R.Assign(2,2,32,3);
Insert(New(PStaticText, Init(R, 'Сообщение-вопрос')));
R.Assign(5,5,14,7);
Insert(New(PButton, Init(R, '~Y~es (Да)', cmYes)));
RAssign(16,5,25,7);
Insert(New(PButton, Init(R, ' ~N~o (Нет)', cmNO)))
end;
if LowMemory then
begin
Dispose(D,Done); {Нет памяти: удаляем распределение}
OutOfMemory; {Сообщаем об этом}
DoIt := False {Признак ошибки}
end
else
Dolt := DeskTop.ExecView(D)=cmYes;
Если используется вызов LowMemory сразу после динамического размещения объекта, то в ходе самого размещения не должен произойти аварийный останов, связанный с нехваткой памяти. Таким образом, размер пула надежности должен быть достаточным для размещения всего объекта. Переменная LowMemSize задает размер пула надежности в параграфах (участках, длиной по 16 байт). По умолчанию она имеет значение 4096 div 16 = 256, т.е. размер пула надежности составляет 4 Кбайт.
На практике вместо прямого обращения к LowMemory чаще используется вызов метода TProgram.ValidView (P: Pointer): Pointer. Этот метод получает в качестве параметра обращения указатель Р на динамический объект и осуществляет следующие действия:
Метод TProgram.ValidView осуществляет стандартные действия по контролю надежности использования кучи. Обычно его используют перед тем, как поместить новый видимый элемент в группу, например:
DeskTop.Insert(ValidView(
Нехватка памяти вызывает обращение к виртуальному методу OutOfMemory, предназначенному для выдачи сообщения о ненормальной ситуации. По умолчанию этот метод ничего не делает и просто возвращает управление вызывающей программе. Вы должны перекрыть его, если хотите сообщить пользователю о возникшей проблеме.
В ряде случаев может оказаться полезной глобальная функция Function MemAlloc (Size: Word): Pointer, которая осуществляет те же действия, что и New или GetMem, но в отличие от них не распределяет пул надежности. Функция возвращает указатель на выделенную область кучи или NIL, если в куче нет свободного блока нужного размера. Аналогичные действия осуществляет функция MemAllocSeg, отличающаяся от MemAlloc только тем, что выделяет память, выровненную на границу параграфа (на границу сегмента).
С использованием указателей можно отказаться от массива и использовать динамические структуры. Самой простой из них является список, который схематично изображается так:
Прямоугольники на этой схеме – динамические переменные типа запись, Data – поле (или поля), содержащие полезную информацию (например фамилии и номера телефонов), поле, которое изображено ниже Data – это указатель на следующую запись. Переменная List также является указателем на запись. Жирная точка в поле «следующий элемент» в самой последней записи означает, что там лежит значение nil, чтобы показать, что эта запись – последняя в списке.
Для описания списка на Паскале достаточно описать тип указателя на запись и тип самой записи. Выглядит всё это так:
type tItemPtr = ^tItem; {указатель на элемент}
tItem = record
Data: tData; {полезные данные}
Next: tItemPtr; {указатель на следующий элемент списка}
end;
В
первой строке этого объявления бросается
в глаза использование
Объявить сам список можно как указатель на элемент: var List : tItemPtr; пока наш список пуст, в List следует положить значение nil. При создании первого элемента будем выполнять действия New(List); List^.Next:=nil.
В списках всегда хранится ровно столько элементов, сколько нужно; если какой-либо элемент данных потерял свою ценность, то его всегда можно удалить из списка; если появились новые данные, то можно добавить новый элемент.
Теперь нужно написать модуль для работы со списками. В нём содержатся процедуры первоначальной подготовки списка; добавления элемента в начало списка; удаления элемента, следующего за указанным; нахождения элемента с заданным номером; подсчета элементов и очистки списка.
unit Lists;
interface
type tData = record
Name: string[50];
Phone: longint;
end;
tItemPtr = ^tItem;
tItem = record
Data: tData;
Next: tItemPtr;
end;
procedure InitList(var l: tItemPtr);
procedure AddItemToHead(var l: tItemPtr; d: tData);
function DeleteItemAfter(var l: tItemPtr; num: word): boolean;
function Count(l: tItemPtr): word;
function GetItem(l: tItemPtr; num: word; var d: tData): boolean;
procedure ClearList(var l: tItemPtr);
{-------------------------
implementation
procedure InitList(var l: tItemPtr);
begin l:=nil end;
procedure AddItemToHead(var l: tItemPtr; d: tData);
var p: tItemPtr;
begin
new(p);
p^.data:=d;
p^.next:=l;
l:=p;
end;
function DeleteItemAfter(var l: tItemPtr; num: word): boolean;
var p,q: tItemPtr;
i: word;
Информация о работе Указатель и динамическая память в Турбо Паскаль