Для типизированных указателей
ЛАБОРАТОРНАЯ РАБОТА
РАБОТА С ДИНАМИЧЕСКИМИ ПЕРЕМЕННЫМИ
И УКАЗАТЕЛЯМИ В ИНТЕГРИРОВАННОЙ СРЕДЕ
ТУРБО ПАСКАЛЬ
ЦЕЛЬ РАБОТЫ
Целью настоящей работы является изучение методов программирования алгоритмов с разветвляющейся и циклической структурой.
ТЕОРЕТИЧЕСКАЯ ЧАСТЬ
Переменные обычно создаются при запуске программы (занимая при этом соответствующий объем памяти) и существуют вплоть до конца работы программы – это так называемые статические переменные. Легко прийти к выводу, что это не самый рациональный способ использования памяти. Познакомимся с возможной альтернативой – динамическими переменными, которые создаются непосредственно перед началом их использования и ликвидируются (освобождая при этом занимаемую память) сразу, как только это использование завершено.
Типичная проблема
Предположим, нам необходимо создать программу, обрабатывающую массив из 15 тысяч элементов, который содержит целочисленные значения типа LongInt. Как может выглядеть соответствующая программа, демонстрирует пример 1.
Пример 1
Program array1;
Const number=15000;
Type
a=array [0..number] of longint;
var
ch:a ;
i:word ;
Begin
Writeln ;
For i:=1 to 15000 do
If (i mod 1000=0) or (i =number) then
Begin
ch[I]:=I;
Write(I;10, ch[I]:6)
End ;
End .
В этой программе объявлен массив, содержащий 15 тысяч значений типа Longint. В теле программы организован цикл (с помощью оператора FOR...TO...DO), в котором каждому элементу массива присваивается значение его индекса. Кроме того, значения элементов массива, а также значения параметра цикла (совпадающие между собой), кратные 1000, выводится на экран для контроля. Для того чтобы сразу же видеть результаты работы программы, в конце имеется оператор ReadLn. Благодаря этому результаты можно наблюдать на экране до тех пор, пока вы не введете что-нибудь и не нажмете клавишу <Enter>.
|
|
В принципе, в этой программе нет ничего нового для нас. Однако допустим, что возникла необходимость иметь дело с массивом, содержащим не 15, а 20 тысяч элементов. В представленной выше программе изменим значение константы Number с 15 000 на 20 000 и запустим программу на выполнение, чтобы посмотреть, что получится.
При попытке запуска (напомним, что это осуществляется комбинацией клавиш <Ctrl+F9> или выбором пункта Run в одноименном меню) на экране появляется сообщение:
Error 22: Structure too large.
Это значит: "Ошибка 22: Структура чересчур объемиста".
Если теперь в меню Help выбрать пункт Error messages, а затем в появившемся окне дважды щелкнуть на номере этой ошибки (22), появится еще одно окно с соответствующей информацией. Информации в этом окне не так уж и много. Сообщается только, что максимальная размерность для структурированных типов составляет 65 520 байт. Что же это значит?
|
|
Вспомним, что элементы нашего массива содержат целочисленные значения, принадлежащие типу Longint (один из целочисленных типов), для каждого из которых требуется четыре байта памяти. Кроме того, массив (его первый вариант) включает 15 тысяч элементов, что в целом составляет 60 000 байт. Затем мы попытались перейти к массиву с 20 000 элементами. Нетрудно вычислить, что для такого массива, каждый элемент которого занимает в памяти четыре байта, в целом потребуется 80 тысяч байт. А теперь вспомним, что информация об ошибке 22 гласит: максимальная размерность для структурированных типов составляет 65 520 байт. Это все объясняет. Если в первом случае допустимый предел превзойден не был (60 тысяч – это меньше чем 65 520), то второй вариант нашего массива (20 тысяч элементов) оказался недопустимо объемным.
Ну и что же дальше? Что если имеет место настоятельная необходимость работать с большими массивами? Неужели Turbo Pascal для этого не подходит? Оказывается, язык программирования Turbo Pascal позволяет справиться и с этой проблемой. Познакомимся с такими понятиями, принятыми в Turbo Pascal, как динамическая память и указатель.
|
|
Статические и динамические переменные
До сих пор мы имели дело с так называемыми статическими данными и статическими переменными. Такие переменные объявляются в начале программы (в разделе описаний) и существуют до завершения ее работы. Соответственно, память для содержания этих переменных выделяется при запуске программы и остается занятой все время, пока программа работает.
Очевидно, что это не самый рациональный способ использования памяти компьютера. Например, некоторая переменная может использоваться только однажды в единственном операторе обширной программы, однако память, выделенная под эту переменную, остается занята все время, пока работает программа. А нельзя ли сделать так, чтобы память для такой переменной выделялась в момент, когда переменная начинает использоваться, и освобождалась сразу же по завершении ее использования? И чтобы эту память тут же можно было выделить для иных данных? Оказывается, это вполне реально и именно для этой цели в Turbo Pascal введено понятие динамической памяти. Динамическая память, известная также как куча, рассматривается в Turbo Pascal как массив байтов.
Кроме того, динамическая память (или куча), имеющая объем приблизительно 300 000 байт, позволяет обрабатывать более обширные структуры данных. В предыдущем разделе нам не удалось, используя Обычную (статическую) память, создать массив из 20 000 элементов типа LonInt. С помощью динамической памяти можно обработать гораздо больший массив, что мы и докажем в конце данной главы.
|
|
Правда, при динамическом размещении к данным не удастся обращаться по именам, как к статическим данным, с которыми мы до сих пор имели дело. Кроме того, количество и тип динамически размещаемых данных заранее неизвестны. Динамическая память выделяется для данных (и освобождается) в ходе работы программы. Для управления динамической памятью Turbo Pascal предоставляет гибкое средство, известное как указатели.
Указатели
Мы уже знаем, что оперативная память компьютера – это множество ячеек, каждая из которых предназначена для хранения одного байта информации, и каждая имеет собственный адрес, по которому к этой ячейке можно обратиться. Указатель – это переменная, объявленная в разделе описаний программы, значение которой и представляет собой адрес одного байта памяти.
Указатели, используемые в Turbo Pascal, бывают типизированные и нетипизированные. Если нетипизированный указатель – это переменная, содержащая адрес данных произвольного типа, то типизированный указатель содержит адрес данных, тип которых оговаривается при объявлении данного указателя.
Нетипизированные указатели объявляются следующим образом:
var
рр : pointer;
где POINTER – стандартный тип данных;
РР – переменная, содержащая адрес памяти, по которому могут храниться данные произвольного типа.
Что касается типизированных указателей, то их объявления в программах на Turbo Pascal выглядят так:
var
рх : ^char;
ру : ^integer;
В этом примере описаны два типизированных указателя: РХ и PY. Значения этих переменных представляют собой адреса в оперативной памяти, по которым содержатся данные типа Char и Integer соответственно. Нетрудно заметить, что описание типизированного указателя отличается от описания статической переменной того же типа только тем, что в случае указателя перед типом присутствует символ "^".
Подытожим: когда требуется воспользоваться динамической памятью, в разделе описаний объявляется не сама переменная, а указатель (или ссылка) на нее. В этом случае указатель представляет собой обычную переменную, а переменная, на которую он ссылается, – динамическую. При этом если в выражении должен присутствовать указатель, используется идентификатор, объявленный в разделе описаний. Вот так: РХ. Однако, если в выражении должна фигурировать динамическая переменная, на которую ссылается указатель, идентификатор указателя дополняется символом "^". Такое действие называется разыменованием. Например, РХ^.
Еще пример:
type
DatePointer = ^Date;
Date=record
year : 1900..2100;
month : 1..12;
day : 1..31;
next : DatePointer
end;
var
pd : DatePointer;
Здесь объявлен тип DatePointer, представляющий собой указатель на тип Date, который описывает запись. Обратите внимание, что тип DatePointer описан до типа Date, на который он ссылается. В то же время одно из полей записи Date принадлежит типу DatePointer. В целом, в Turbo Pascal не допускается ссылаться на еще не описанные типы, однако в данном случае (достаточно частом, когда приходится иметь дело с указателями) как ни располагай описания, все равно ссылки на еще не описанный тип не избежать. Поэтому единственное исключение сделано для указателей: тип указателя на динамические данные может быть объявлен до описания самих данных.
Необходимо заметить, что применительно к типизированным указателямоперация присваивания допустима только для указателей, ссылающихся на данные одного типа. Предположим, в программе объявлены указатели:
var
рх , ру : ^char;
pz : ^integer;
В этом случае операция присваивания допустима для указателей РХ и РY:
px := py
Однако совершенно недопустимыми окажутся операторы:
рх : = pz;
или
pz :=ру;
В то же время нетипизированный указатель может присутствовать в операторе присваивания в паре с любым типизированным указателем. Например, в программе объявлены указатели:
var
рх : ^ char;
py : ^ integer;
pz : pointer;
Для этих переменных допустимы операторы присваивания:
рх := pz; ру := pz;
pz :=ру; pz := рх;
Нетрудно прийти к выводу, что нетипизированные указатели – очень удобное средство преобразования типов данных.
2.3.1. Состояния указателя.Для указателя, после того как он объявлен в разделе описания переменных, возможны три состояния. Указатель может содержать адрес некоторой переменной, "пустой" адрес NIL или иметь неопределенное состояние. Первый случай в объяснениях не нуждается. Во втором случае, когда требуется, чтобы указатель никуда не указывал, ему присваивается специальное значение NIL. Что же касается неопределенного состояния, то оно имеет место сразу после начала работы программы (до того как указателю будет присвоен какой-нибудь адрес в памяти или значение NIL), либо после освобождения памяти, на которую данный указатель ссылается.
Если кого-то смущает "указатель, который никуда не указывает", то это можно представить как ссылку на область памяти, в которой никакая информация никогда не размещается. Значение NIL – это константа, которую можно присвоить любому указателю.
Может возникнуть вопрос, в чем разница между неопределенным состоянием указателя и случаем, когда его значение равно NIL? Поскольку NIL – значение конкретное, хотя и никуда не указывающее, можно сказать, что два указателя, содержащие NIL, имеют равные значения. В то же время значения двух указателей в неопределенном состоянии равными признать нельзя.
2.3.2. Выделение и освобождение динамической памяти.Познакомимся с тем, как выделяется (или резервируется) динамическая память для типизированных и нетипизированных указателей. Кроме того, узнаем как освободить память, которая была выделена ранее.
Для типизированных указателей
Для динамически размещаемых переменных, на которые ссылаются типизированные указатели, память выделяется и освобождается с помощью процедур New и Dispose соответственно. Вот как схематически может выглядеть программа, в которой используются указатели на переменные, размещаемые в динамической памяти:
var
рх , ру : ^char;
pz : ^integer;
begin
new(px);
new(py);
new(pz);
px ^ : ='A';
py ^ : ='7';
pz ^ : =667;
…
dispose(px);
dispose(py);
dispose(pz );
…
end .
В этой программе в разделе описания переменных объявлены три типизированных указателя. Затем в теле программы под динамические переменные, на которые ссылаются эти указатели, с помощью процедуры New выделена динамическая память. После этого динамическим переменным можно присваивать подходящие по типу значения (это есть в теле программы) и использовать их в разного рода выражениях (операторы с этими выражениями в теле программы заменены тремя точками).
Когда надобность в переменных РХ, PY и PZ отпадает, выделенная для них память освобождается с помощью процедуры Dispose. После этого освободившуюся память можно резервировать для других переменных, объявленных в программе и использующихся в оставшихся операторах.
Дата добавления: 2019-02-26; просмотров: 267; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!