CFA LogoCFA Logo Computer
Новости Статьи Магазин Драйвера Контакты
Новости
RSS канал новостей
В конце марта компания ASRock анонсировала фирменную линейку графических ускорителей Phantom Gaming. ...
Компания Huawei продолжает заниматься расширением фирменной линейки смартфонов Y Series. Очередное ...
Компания Antec в своем очередном пресс-релизе анонсировала поставки фирменной серии блоков питания ...
Компания Thermalright отчиталась о готовности нового высокопроизводительного процессорного кулера ...
Компания Biostar сообщает в официальном пресс-релизе о готовности флагманской материнской платы ...
Самое интересное
Программаторы 25 SPI FLASH Адаптеры Optibay HDD Caddy Драйвера nVidia GeForce Драйвера AMD Radeon HD Игры на DVD Сравнение видеокарт Сравнение процессоров

АРХИВ СТАТЕЙ ЖУРНАЛА «МОЙ КОМПЬЮТЕР» ЗА 2003 ГОД

Мысли о Паскале

Владислав ДЕМЬЯНИШИН nitromanit@mail.ru

Продолжение, начало см. в МК №46, 51-52, 4, 6-7, 10, 12-13, 16-18, 22, 24, 29, 34, 41, 46, 4, 6, 17, 21, 23, 28 (165, 170-171, 175, 177-178, 181, 183-184, 187-189, 193, 195, 200, 205, 212, 217, 227, 229, 240, 244, 246, 251).

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

Указатели

Переменные типа указатель могут быть типизированными и нетипизированными. Такая переменная служит для хранения адреса начала некоторой области памяти. Занимает она 4 байта и состоит из двух неразрывных частей сегмент:смещение, причем первые два байта (младшее слово) — это смещение, а вторые два байта (старшее слово) — номер сегмента.

Для тех, кто еще не в курсе, поясню: номер сегмента может иметь значение в диапазоне 0..$FFFF и по сути является номером 16-байтного параграфа памяти, т.е. чтобы получить точный адрес соответствующего параграфа в байтах, центральный процессор умножает номер сегмента на 16 (логическим сдвигом влево на 4 бита). Смещение — это дополнительный адрес относительно номера сегмента, но уже в байтах, т.е. он может иметь значение 0..$FFFF. При вычислении линейного (прямого) адреса на указываемую область памяти центральный процессор к уже полученному адресу параграфа прибавляет смещение. В итоге линейный адрес указываемой области памяти равен Сегмент*16+Смещение. Таким образом, процессор вычисляет 20-битный адрес, применяющийся в реальном режиме центрального процессора или в режиме виртуального i8086 (V86), в которых может работать ОС MS-DOS.

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

объявляется тип указателя PWord на тип Word — переменные-указатели PW1 и PW2 сами хранить значения типа Word не могут, но могут хранить адрес переменных типа Word. Для типа PWord тип Word является базовым. Переменная UserPtr описана как указатель на структуру комбинированного типа Tperson (см. главу «Комбинированные типы (записи)», МК №16 (187)).

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

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

т.е. переменная-указатель PW1 получает адрес переменной W. Кто сказал, что такие манипуляции допустимы только для типа Word? Все это справедливо для любых типов переменных. Например, для массивов, где PW2:=@List[j] загрузит адрес J-го элемента массива List в указатель PW2, а UserPtr:=@Users[5] запишет адрес 5-го элемента массива Users в указатель UserPtr. Таким образом, можно образовывать ссылочные типы от любых типов, а значит, и на указатель, т.е. указатель на указатель. Это напоминает старую бюрократическую басню: «Дайте мне справку, о том, что мне нужна справка…» В нашем случае выражение PWPtr := @PW1 запишет адрес переменной-указателя PW1 в указатель PWPtr.

В системе Turbo Pascal предусмотрена константа Nil, совместимая по присваиванию и сравнению с ссылочными типами. Она может служить для инициализации указателя с тем, чтобы пометить указатель как никуда не указывающий. Т.е. это может помочь в осуществлении проверок, был ли указатель инициализирован реальным адресом в области памяти или нет.

Над указателями допустимы операции сравнения = и <>, что позволяет определить, указывают ли два указателя на одну и туже область в памяти или нет. Пример:

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

Как объявлять и инициализировать указатели, я рассказал. Теперь следует объяснить, как их можно использовать. Возьмем переменную W из примера, указанного выше. Привычная конструкция W:=W+5 увеличивает значение переменной на 5, при этом используется прямая адресация, т.е. в операторе указано имя самой переменной типа Word. В том же примере указатель PW1 был проинициализирован адресом переменной W при выполнении оператора PW1:=@W. Начиная с этого момента к переменной W можно обращаться, используя косвенную адресацию, т.е. через указатель PW1. Но для этого надо соблюдать правило разыменования, которое гласит: чтобы получить доступ к переменной (области занимаемой ею памяти), на которую ссылается указатель, следует имя переменной-указателя завершать знаком ^. Таким образом, конструкция PW1^ интерпретируется как «переменная, на которую ссылается указатель PW1». Применение этой конструкции возможно везде, где допустимо вхождение переменной базового типа указателя. В данном случае базовым типом является Word. В соответствии с этим, операторы W:=W+5 и PW1^:=PW1^+5 абсолютно эквивалентны. Пример:

С указателями на переменную вроде бы все ясно. А как тогда использовать указатель на указатель, который указывает на переменную? В одном из приведенных примеров указатель PW1 был инициализирован адресом переменной W, а указатель PWPtr — адресом указателя PW1. Теперь, для доступа к переменной W можно использовать конструкцию, которая называется многократное разыменование:

Следует помнить, что операции над разыменованным указателем, имеющим значение Nil, считаются некорректными, так как указатель указывает на несуществующую переменную. Такие операции могут вызвать некорректную работу программы, но при этом могут никак себя не обнаружить, и программисту будет казаться, что в программе хозяйничает полтергейст :-).

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

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

Ранее я рассказывал, что глобальные переменные хранятся в сегменте данных, а локальные переменные — в стеке. При этом таким переменным отводится память с учетом их размера в байтах. После этого объем памяти под любую такую переменную не может быть изменен ни в сторону увеличения, ни в сторону уменьшения. Таким образом, создавая программу, например, работающую с реестром пользователей с помощью типа TPerson, программист не может предусмотреть, сколько человек может в ней находиться одновременно на том или ином этапе эксплуатации. И если программист опишет структуру пользователей так:

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

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

Динамические переменные размещаются в области памяти, называемой кучей (heap), размер которой может превышать 64 Кб, но не может превышать 640 Кб за вычетом размера сегмента данных, размера сегмента стека, размера кода программы и общего размера загруженных драйверов и резидентов, а также области, используемой MS-DOS. Следует также учесть, что если вы запускаете программу в текстовом процессоре turbo.exe, то хип будет меньше еще на 300 Кб.

Создание динамической переменной заключается в отведении памяти запрашиваемого объема и загрузке адреса начала отведенной области в указатель. Это осуществляется стандартной процедурой New, которая позволяет создать (выделить память) под переменную, представленную типизированным указателем в качестве параметра.

Пример:

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

Если памяти недостаточно, то произойдет ошибка выполнения Error 203: Heap overflow error (куча переполнена, исчерпана).

Чтобы предотвратить прекращение программы вследствие ошибки, можно воспользоваться стандартной функцией MaxAvail, которая возвращает размер наибольшего непрерывного свободного участка памяти в куче. Таким образом, проверяя объем кучи перед каждым вызовом команды New, можно выявить критическую ситуацию до ее возникновения и корректно завершить программу, сохранив результат ее работы и выдав сообщение о причине завершения работы, несанкционированного пользователем. Пример:

Напомню, что функция SizeOf возвращает размер памяти в байтах, занимаемый переменной или типом, указанным в качестве параметра. И хотя в Help-справке указано, что результатом функции является значение типа Integer, не верьте — на самом деле результатом является значение типа Word.

Еще имеется функция MemAvail, которая возвращает суммарный объем всех свободных участков (в куче) в байтах.

Уничтожение динамической переменной сводится к освобождению ранее отведенной области памяти, адрес на начало которой содержится в указателе. Для этого существует процедура Dispose, которая предназначена для освобождения памяти, ранее отведенной командой New. В качестве параметра должен быть указатель на динамическую переменную. Пример:

В том случае, если память выделена не была или значение указателя было ошибочно изменено, то процедура Dispose вызовет ошибку Error 204: Invalid pointer operation” (неверный указатель).

(Продолжение следует)

Рекомендуем ещё прочитать:






Данную страницу никто не комментировал. Вы можете стать первым.

Ваше имя:
Ваша почта:

RSS
Комментарий:
Введите символы или вычислите пример: *
captcha
Обновить





Хостинг на серверах в Украине, США и Германии. © sector.biz.ua 2006-2015 design by Vadim Popov