http://sulfurzona.com/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Forum
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 

Паскаль для новичков (часть 21)

 

Ссылочные типы. Динамические переменные

 
Паскаль для новичков

Указатели

 
Переменные типа указатель могут быть типизированными и нетипизированными. Такая переменная служит для хранения адреса начала некоторой области памяти. Занимает она 4 байта и состоит из двух неразрывных частей сегмент:смещение, причём первые два байта (младшее слово) – это смещение, а вторые два байта (старшее слово) – это номер сегмента.
 
Для тех, кто ещё не в курсе, поясню, что номер сегмента может иметь значение в диапазоне 0..$FFFF, и по сути является номером 16-байтного параграфа памяти, т.е. чтобы получить точный адрес соответствующего параграфа в байтах, центральный процессор умножает номер сегмента на 16 (логическим сдвигом влево на 4 бита). Смещение – это дополнительный адрес относительно номера сегмента, но уже в байтах, т.е. он может иметь значение 0..$FFFF, и при вычислении линейного (прямого) адреса на указываемую область памяти, центральный процессор к уже полученному адресу параграфа прибавляет смещение. В итоге линейный адрес указываемой области памяти равен=Сегмент*16+Смещение. Таким образом, процессор вычисляет 20-битный адрес, применяющийся в реальном режиме центрального процессора или в режиме виртуального i8086 (V86), в которых может работать ОС MS-DOS.
 
Указатель может быть типизированным, т.е. при описании типа указатель или переменной типа указатель можно предопределить, на переменные какого типа может указывать данная переменная типа указатель. В сущности, самой переменной типа указатель всё равно адрес какой области памяти хранить, но компилятору не всё равно. В примере:
 
type
PWord = ^Word;
var
PW1, PW2 : PWord;
PWPtr : ^PWord;
UserPtr : ^TPerson;
 
объявляется тип указателя PWord на тип Word и переменные-указатели PW1 и PW2 сами хранить значения типа Word не могут, но могут хранить адрес переменных типа Word. Для типа PWord тип Word является базовым. Переменная UserPtr описана как указатель на структуру комбинированного типа TPerson.(см. главу “Комбинированные типы (записи) ).
 
При описании ссылочных типов действует одно удобное исключение, которое позволяет объявить тип указателя на тип (структуру), который ещё не описан выше в описании типов, но должен быть обязательно объявлен ниже в пределах данного описания type. Например:
 
type
PVector = ^TVector;
TVector = record
               X, Y, Z : Real;
               end;
 
Так как неинициализированные указатели могут содержать случайный адрес области памяти, то их следует инициализировать, записав в них адрес используемой области данных. Для этого необходимо использовать унарную операцию взятия адреса, которая может состоять из знака операции взятия адреса – символа ‘@’ (амперсант) и имени переменной любого типа. Пример:
 
var
W, J : Word;
List : array [0..100] of Word;
Users : array [1..10] of TPerson;
Begin
PW1 := @W;
J := 50;
PW2 := @List[ j ];
UserPtr := @Users[5];
PWPtr := @PW1;
End.
 
т.е. переменная указатель PW1 получает адрес переменной W. Кто сказал, что такие манипуляции допустимы только для типа Word? Всё это справедливо для любых типов переменных. Например, для массивов, где PW2:=@List[j] загрузит адрес J-го элемента массива List в указатель PW2, а UserPtr:=@Users[5] запишет адрес 5-го элемента массива Users в указатель UserPtr. Таким образом, можно образовывать ссылочные типы от любых типов, а значит и на указатель, т.е. указатель на указатель. Это мне напоминает старую бюрократическую басню “Дайте мне справку, о том, что мне нужна справка, …”. Так что выражение PWPtr := @PW1 запишет адрес переменной-указателя PW1 в указатель PWPtr.
 
В системе Turbo Pascal предусмотрена константа Nil, совместимая по присваиванию и сравнению с ссылочными типами. Она может служить для инициализации указателя с тем, чтобы пометить указатель как никуда не указывающий. Т.е. это может помочь в осуществлении проверок, был ли указатель инициализирован реальным адресом в области памяти или нет.
 
Над указателями допустимы операции сравнения ‘=’ и ‘<>’, что позволяет определить, указывают ли два указателя на одну и туже область в памяти или нет. Пример:
 
If PW1 <> nil then
If PW1 = PW2 then
 

Доступ к переменной по указателю

 
Как объявлять и инициализировать указатели я рассказал. Теперь следует объяснить, как их можно использовать. Возьмём переменную W из примера, указанного выше. Привычная конструкция W:=W+5 увеличивает значение переменной на 5, при этом используется прямая адресация, т.е. в операторе указано имя самой переменной типа Word. В том же примере указатель PW1 был проинициализирован адресом переменной W при выполнении оператора PW1:=@W. Начиная с этого момента, к переменной W можно обращаться, используя косвенную адресацию, т.е. через указатель PW1. Но для этого надо соблюдать правило разыменования, которое гласит: чтобы получить доступ к переменной (области занимаемой ею памяти), на которую ссылается указатель, следует имя переменной-указателя завершать знаком ‘^’. Таким образом, конструкция PW1^ интерпретируется как “переменная, на которую ссылается указатель PW1”. Применение этой конструкции возможно везде, где допустимо вхождение переменной базового типа указателя. В данном случае, базовым типом является Word. В соответствии с этим, операторы W:=W+5 и
PW1^:=PW1^+5 абсолютно эквивалентны. Пример:
 
If PW1^=PW2^ then
    with UserPtr^ do begin
            BirthDay.Year := 2000;
            Name := ‘Людмила’;
            SurName := ‘Добрый-Вечер’;
 
 
 
 
           end
   else UserPtr^.BirthDay.Year := 1991;
PW2^ := PW1^ + 10;
 
С указателями на переменную вроде бы всё ясно. А как тогда использовать указатель на указатель, который указывает на переменную? В одном из примеров, указанных выше был инициализирован указатель PW1 адресом переменной W, а указатель PWPtr адресом указателя PW1. Теперь, для доступа к переменной W, можно использовать конструкцию, которая называется многократное разыменование:
 
PWPtr^^ := PWPtr^^ + 5;
 
Следует помнить, что операции над разыменованным указателем, имеющим значение Nil, считаются некорректными, так как указатель указывает на не существующую переменную. Такие операции могут вызвать некорректную работу программы, но при этом могут никак себя не обнаружить, и программисту будет казаться, что с программой творится какой-то полтергейст.
 

Статические и динамические переменные.

Создание и уничтожение динамических переменных

 
Ранее я рассказывал, что глобальные переменные хранятся в сегменте данных, а локальные переменные хранятся в стеке. При этом таким переменным отводится память с учётом их размера в байтах. После этого объём памяти под любую такую переменную не может быть изменён ни в сторону увеличения, ни в сторону уменьшения. Таким образом, создавая программу, например, о реестре пользователей с использованием типа TPerson, программист не может предусмотреть, сколько пользователей может оказаться на том или ином этапе эксплуатации его программы. И если программист опишет структуру пользователей так:
 
Const MaxUsers = 100;
Var Users : array [1..MaxUsers] of TPerson;
 
то окажись реальное число пользователей больше, чем MaxUsers,
программа окажется бесполезной в дальнейшей работе, так как не сможет зарегистрировать больше пользователей и обрабатывать их информацию. Исходя из этого, глобальные и локальные переменные называются статическими, так как могут занимать неизменный размер памяти и как правило, в программе представляются собственными идентификаторами, потому что место их размещения в памяти известно заранее.
 
Выход для программиста из создавшегося положения может лежать через применение динамических переменных, т.е. переменных, под которые может отводиться память произвольного объёма по мере надобности. В ходе программы под такую переменную можно отвести определённый объём памяти с некоторым адресом, затем через некоторое время может понадобиться больше памяти для неё, и тогда старый блок памяти освобождается и выделяется новый ещё большего размера. При этом новый блок памяти может иметь совершенно другой адрес. Поэтому в программе такая переменная не может быть представлена собственным идентификатором, но может быть представлена разыменованным указателем на отведённую под неё область память.
 
Динамические переменные размещаются в области памяти, называемой кучей (heap), размер которой может превышать 64К, но не может превышать 640К за вычетом размера сегмента данных, размера сегмента стека, размера кода программы и общего размера загруженных драйверов и резидентов, а так же области, используемой MS-DOS. Следует также учесть, что если вы запускаете программу в текстовом процессоре TURBO.EXE, то хип будет меньше ещё на 300К.
 
Создание динамической переменной заключается в отведении памяти запрашиваемого объёма и загрузке адреса начала отведённой области в указатель. Это осуществляется стандартной процедурой New, которая позволяет создать (выделить память) под переменную, представленную типизированным указателем в качестве параметра.
Пример:
 
begin
New(UserPtr);
UserPtr^.Name := ’Вова’;
 
Процедура New самостоятельно определяет размер необходимой памяти по базовому типу указателя, и если в наличии имеется достаточно свободной памяти, то память выделяется, а адрес загружается в указатель. Процедура New может быть использована как функция. Тогда в качестве параметра следует указать ссылочный тип, а результатом будет адрес выделенной области памяти:
 
type
TPersonPtr = ^TPerson;
var
User : TPersonPtr;
begin
User := New(TPersonPtr);
User^.Name := ‘Вова’;
 
Если памяти не достаточно, то произойдёт ошибка выполнения “Error 203: Heap overflow error.” (куча переполнена, исчерпана).
Чтобы предотвратить прекращение программы вследствие ошибки, можно воспользоваться стандартной функцией MaxAvail, которая возвращает размер наибольшего непрерывного свободного участка памяти в куче. Таким образом, проверяя объём кучи перед каждым вызовом команды New, можно выявить критическую ситуацию до её возникновения и корректно завершить программу, сохранив результат её работы и выдав сообщение о причине завершения работы, несанкционированного пользователем. Пример:
 
begin
if MaxAvail < SizeOf(TPerson) then begin
   writeln(‘Ошибка: не хватает памяти’);
   Halt;
   end;
User := New(TPersonPtr);
User^.Name := ‘Вова’;
 
Напомню, что функция SizeOf возвращает размер памяти в байтах, занимаемый переменной или типом, указанным в качестве параметра. И хотя в Help-справке указано, что результатом функции является значение типа Integer, не верьте, так как на самом деле результатом является значение типа Word.
 
Ещё имеется функция MemAvail, которая возвращает суммарный объём всех свободных участков (в куче) в байтах.
Уничтожение динамической переменной сводится к освобождению ранее отведённой области памяти, адрес на начало которой содержится в указателе. Для этого существует процедура Dispose, которая предназначена для освобождения памяти, ранее отведённой командой New. В качестве параметра должен быть указатель на динамическую переменную. Пример:
 
Dispose(User);
end.
 
В том случае, если память выделена не была или значение указателя было ошибочно изменено, то процедура Dispose вызовет ошибку “Error 204: Invalid pointer operation.” (неверный указатель).
 
Продолжение следует…
 
© Владислав Демьянишин
 
 
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте Вы можете бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Все программы чистые, т.е. не содержат вирусов и иного вредоносного ПО.
 
Среди доступных программ есть мобильная читалка книг, менеджер переноса файлов с фото- и видеокамер на компьютер, текстовый редактор, WYSIWYG редактор, 3D аниматор, GIF аниматор, AVI аниматор, пакетный конвертор изображений, редактор электрических схем, программа для скриншотов, диспетчер тем рабочего стола и другие.
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 21): Ссылочные типы. Динамические переменные
 
 
 
5
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украинский портАл Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG Rambler's Top100