Реферат Основы программирования в паскале
Работа добавлена на сайт bukvasha.net: 2015-10-28Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
от 25%
договор
Гипероглавление:
Введение
Характеристика и особенности языка
Основные особенности языка Pascal.
TURBO PASCAL
Основы языка Pascal.
Лингвистическая концепция языка Паскаль
Алфавит языка и специфика использования символов
Понятие лексемы и разделителя
Идентификаторы
Структура программы
Общая структура программ в ТП 7.0
Раздел текстов процедур и функций
Раздел основного блока программы
Заголовок программы
Раздел объявлений и соглашений
Основной блок программы
Типы переменных.
О приводимости типов.
Операторы языка Pascal
Составной и пустой операторы.
Операторы ветвлений.
Условный оператор.
Операторы повторений.
Цикл с предопределенным числом повторений.
Условный цикл с проверкой условия перед исполнением блока операторов.
Условный цикл с проверкой после выполнения блока операторов.
Оператор выбора одного из вариантов.
Оператор безуслов ного перехода на строку с меткой.
Простые и структурные типы данных.
Перечисляемый и ограниченный типы.
Символьные строки.
Множества.
Записи.
Запись с вариантами.
Совместимость и преобразования типов.
Типизированные константы
Процедуры и функции.
Блочная структура программ.
Общая структура подпрограммы.
Области видимости объектов.
Механизм передачи параметров.
Параметры - значения.
Параметры - переменные.
Безтиповые параметры.
Вычисление значения функции, завершение подпрограммы.
Предварительные и внешние описания подпрограмм.
Специальные случаи.
Рекурсия и побочный эффект.
Распределение памяти под данные.
Процедурные типы.
Правила корректной работы с процедурными типами.
Приведение типов переменных для процедурных типов.
Динамическая память.
Распределение памяти IBM PC.
Типы прерываний.
Объявление указателей.
Выделение и освобождение динамической памяти.
Примеры использования указателей.
Процедуры и функции для работы с динамической памятью.
Проблема потерянных ссылок
Модули
Структура модулей.
Заголовок модуля и связь модулей друг с другом
Интерфейсная часть.
Исполняемая часть
Инициирующая часть
Компиляция модулей
Доступ к объявленным в модуле объектам
Стандартные модули
Ключи и директивы компилятора.
Директивы-переключатели.
Директивы:
Директивы с параметрами.
Условные директивы.
Основные определения.
Доступ к файлам.
Имена файлов
Логические устройства
Инициализация файла
Процедуры и функции для работы с файлами.
ДРУГИЕ ВОЗМОЖНОСТИ ТУРБО-ПАСКАЛЯ
Внешние процедуры (функции)
Использование встроенных машинных кодов.
Обращение к функциям операционной системы
Процедура MSDOS.
Поддержка процедур обработки прерываний.
Запуск внешних программ.
Оверлей
Прямое обращение к памяти и портам ввода-вывода
ТурбоПаскаль 6.0 и структурное программирование.
ТурбоПаскаль и объектно-ориентированное программирование.
Основные определения типа <объект>.
Основные свойства объектов.
Инкапсуляция
Наследование.
Полиморфизм.
Объектно-ориентированная библиотека Turbo Vision
Основные определения
Встроенная справочная система
В кратком изложении история языков программирования такова: изначально вычислительные машины программировались в машинном коде. Тоесть в их оперативную память напрямую вводили последовательность чисел, являющиеся кодами команд, которые процессор может выполнить. При этом программа составлялась с периодическим заглядыванием в таблицу кодов команд процессора и была отнюдь не наглядной. Затем появилась идея обозначить коды какими-то короткими, но осмысленными, и потому легко запоминаемыми словами – мнемониками, и создать программу, которая бы, руководствуясь таблицей команд, переводила последовательность мнемоник – мнемокод в последовательность машинных кодов. Такую программу называют ассемблером (assembler - сборочное устройство, транслятор, ассемблер). Программы стали гораздо нагляднее, но решение практических задач требовало написания очень длинных программ (например, файловый менеджер Volkov Commander имеет размер около 64000 байт). Тогда появились языки программирования высокого уровня. При их создании использовали то обстоятельство, что в программе часто встречаются участки одинакового кода, выполняющие какое либо одно действие: вывод строки, запись в файл, вычисление математической функции и т.д. В языках высокого уровня таким последовательностям кода присвоены имена, и программа составляется на условном языке, каждое, из слов которого заменяет десятки, ато и сотни команд процессора. Таким образом, программа становится еще нагляднее и короче. Существует множество условных языков высокого уровня, для каждого из них написано немало вариантов пограммы, переводящей условный код в последовательность машинных команд. Один из таких языков – Паскаль.
Язык программирования Pascal был разработан в 1968-1971 гг. Никлаусом Виртом в Цюрихском Институте информатики (Швейцария), и назван вчесть Блеза Паскаля – выдающегося математика, философа и физика 17-го века. Первоначальная цель разработки языка диктовалась необходимостью создания инструмента "для обучения программированию как систематической дисциплине". Однако очень скоро обнаружилась чрезвычайная эффективность языка Pascal в самых разнообразных приложениях, от решения небольших задач численного характера до разработки сложных программных систем - компиляторов, баз данных, операционных систем и т.п. К настоящему времени Pascal принадлежит к группе наиболее распространенных и популярных в мире языков программирования:
· существуют многочисленные реализации языка практически для всех машинных архитектур;
· разработаны десятки диалектов и проблемно-ориентированных расширений языка Pascal;
· обучение программированию и научно-технические публикации в значительной степени базируются на этом языке.
Существует ряд объективных причин, обусловивших выдающийся успех языка Pascal. Среди них в первую очередь необходимо указать следующие:
1. Язык в естественной и элегантной форме отразил важнейшие современные концепции технологии разработки программ:
2. Благодаря своей компактности, концептуальной целостности и ортогональности понятий, а также удачному первоначальному описанию, предложенному автором языка, Pascal оказался весьма легок для изучения и освоения. В противоположность громоздким многотомным описаниям таких языков, как PL/l, Cobol, FORTRAN, достаточно полное описание языка Pascal занимает около 30 страниц текста, а его синтаксические правила можно разместить на одной странице.
3. Несмотря на относительную простоту языка, он оказался пригоден для весьма широкого спектра приложений, в том числе для разработки очень больших и сложных программ, например, операционных систем.
4. Pascal весьма технологичен для реализации практически для всех, в том числе и нетрадиционных, машинных архитектур. Утверждается, что разработка Pascal-транслятора "почти не превышает по трудоемкости хорошую дипломную работу выпускника вуза". Благодаря этому для многих ЭВМ существует несколько различных реализаций языка, отражающих те или иные практические потребности программистов.
Язык Pascal стандартизован во многих странах. В 1983 году был принят международный стандарт (ISO 7185:1983)
1. Pascal является традиционным алгоритмическим языком программирования, продолжающим линию Algol-60. Это означает, что программа на языке Pascal представляет собой специально организованную последовательность шагов по преобразованию данных, приводящую к решению некоторой задачи. Это отличает Pascal от так называемых непроцедурных языков типа Prolog, по существу, представляющих собой формализмы для записи начальных условий некоторой задачи и синтезирующих решение посредством встроенных механизмов логического вывода.
2. Язык Pascal содержит удобные средства для представления данных. Развитая система типов позволяет адекватно описывать данные, подлежащие обработке, и конструировать структуры данных произвольной сложности. Pascal является типизированным языком, что означает фиксацию типов переменных при их описании, а также строгий контроль преобразований типов и контроль доступа к данным в соответствии с их типом (как на этапе компиляции, так и при исполнении программ).
3. Набор операторов языка Pascal отражает принципы структурного программирования и позволяет записывать достаточно сложные алгоритмы в компактной и элегантной форме. Pascal является процедурным языком с традиционной блочной структурой и статически определенными областями действия имен. Процедурный механизм сочетает в себе простоту реализации и использования и гибкие средства параметризации.
4. Синтаксис языка достаточно несложен. Программы записываются в свободном формате, что позволяет сделать их наглядными и удобными для изучения.
5. Паскаль – компилятор, тоесть, прежде чем начать испоолнение программы, Паскаль полностью прочитывает исходный текст, написанный программистом, и составляет последовательность машинных кодов, выполняющую те действия, которые описал программист в hqundmnl тексте. Эта последовательность сохраняется в файл с расширением “.EXE” и является самостоятельным исполняемым файлом, который может быть запущен сам по себе, уже без участия Паскаля и, даже, на другом компъютере, на котором Паскаль может быть не установлен.
Введение
Характеристика и особенности языка
Основные особенности языка Pascal.
TURBO PASCAL
Основы языка Pascal.
Лингвистическая концепция языка Паскаль
Алфавит языка и специфика использования символов
Понятие лексемы и разделителя
Идентификаторы
Структура программы
Общая структура программ в ТП 7.0
Раздел текстов процедур и функций
Раздел основного блока программы
Заголовок программы
Раздел объявлений и соглашений
Основной блок программы
Типы переменных.
О приводимости типов.
Операторы языка Pascal
Составной и пустой операторы.
Операторы ветвлений.
Условный оператор.
Операторы повторений.
Цикл с предопределенным числом повторений.
Условный цикл с проверкой условия перед исполнением блока операторов.
Условный цикл с проверкой после выполнения блока операторов.
Оператор выбора одного из вариантов.
Оператор безуслов ного перехода на строку с меткой.
Простые и структурные типы данных.
Перечисляемый и ограниченный типы.
Символьные строки.
Множества.
Записи.
Запись с вариантами.
Совместимость и преобразования типов.
Типизированные константы
Процедуры и функции.
Блочная структура программ.
Общая структура подпрограммы.
Области видимости объектов.
Механизм передачи параметров.
Параметры - значения.
Параметры - переменные.
Безтиповые параметры.
Вычисление значения функции, завершение подпрограммы.
Предварительные и внешние описания подпрограмм.
Специальные случаи.
Рекурсия и побочный эффект.
Распределение памяти под данные.
Процедурные типы.
Правила корректной работы с процедурными типами.
Приведение типов переменных для процедурных типов.
Динамическая память.
Распределение памяти IBM PC.
Типы прерываний.
Объявление указателей.
Выделение и освобождение динамической памяти.
Примеры использования указателей.
Процедуры и функции для работы с динамической памятью.
Проблема потерянных ссылок
Модули
Структура модулей.
Заголовок модуля и связь модулей друг с другом
Интерфейсная часть.
Исполняемая часть
Инициирующая часть
Компиляция модулей
Доступ к объявленным в модуле объектам
Стандартные модули
Ключи и директивы компилятора.
Директивы-переключатели.
Директивы:
Директивы с параметрами.
Условные директивы.
Основные определения.
Доступ к файлам.
Имена файлов
Логические устройства
Инициализация файла
Процедуры и функции для работы с файлами.
ДРУГИЕ ВОЗМОЖНОСТИ ТУРБО-ПАСКАЛЯ
Внешние процедуры (функции)
Использование встроенных машинных кодов.
Обращение к функциям операционной системы
Процедура MSDOS.
Поддержка процедур обработки прерываний.
Запуск внешних программ.
Оверлей
Прямое обращение к памяти и портам ввода-вывода
ТурбоПаскаль 6.0 и структурное программирование.
ТурбоПаскаль и объектно-ориентированное программирование.
Основные определения типа <объект>.
Основные свойства объектов.
Инкапсуляция
Наследование.
Полиморфизм.
Объектно-ориентированная библиотека Turbo Vision
Основные определения
Встроенная справочная система
Введение
В кратком изложении история языков программирования такова: изначально вычислительные машины программировались в машинном коде. Тоесть в их оперативную память напрямую вводили последовательность чисел, являющиеся кодами команд, которые процессор может выполнить. При этом программа составлялась с периодическим заглядыванием в таблицу кодов команд процессора и была отнюдь не наглядной. Затем появилась идея обозначить коды какими-то короткими, но осмысленными, и потому легко запоминаемыми словами – мнемониками, и создать программу, которая бы, руководствуясь таблицей команд, переводила последовательность мнемоник – мнемокод в последовательность машинных кодов. Такую программу называют ассемблером (assembler - сборочное устройство, транслятор, ассемблер). Программы стали гораздо нагляднее, но решение практических задач требовало написания очень длинных программ (например, файловый менеджер Volkov Commander имеет размер около 64000 байт). Тогда появились языки программирования высокого уровня. При их создании использовали то обстоятельство, что в программе часто встречаются участки одинакового кода, выполняющие какое либо одно действие: вывод строки, запись в файл, вычисление математической функции и т.д. В языках высокого уровня таким последовательностям кода присвоены имена, и программа составляется на условном языке, каждое, из слов которого заменяет десятки, ато и сотни команд процессора. Таким образом, программа становится еще нагляднее и короче. Существует множество условных языков высокого уровня, для каждого из них написано немало вариантов пограммы, переводящей условный код в последовательность машинных команд. Один из таких языков – Паскаль.
Язык программирования Pascal был разработан в 1968-1971 гг. Никлаусом Виртом в Цюрихском Институте информатики (Швейцария), и назван вчесть Блеза Паскаля – выдающегося математика, философа и физика 17-го века. Первоначальная цель разработки языка диктовалась необходимостью создания инструмента "для обучения программированию как систематической дисциплине". Однако очень скоро обнаружилась чрезвычайная эффективность языка Pascal в самых разнообразных приложениях, от решения небольших задач численного характера до разработки сложных программных систем - компиляторов, баз данных, операционных систем и т.п. К настоящему времени Pascal принадлежит к группе наиболее распространенных и популярных в мире языков программирования:
· существуют многочисленные реализации языка практически для всех машинных архитектур;
· разработаны десятки диалектов и проблемно-ориентированных расширений языка Pascal;
· обучение программированию и научно-технические публикации в значительной степени базируются на этом языке.
Характеристика и особенности языка
Существует ряд объективных причин, обусловивших выдающийся успех языка Pascal. Среди них в первую очередь необходимо указать следующие:
1. Язык в естественной и элегантной форме отразил важнейшие современные концепции технологии разработки программ:
- развитая система типов,
- ориентация на принципы структурного программирования,
- поддержка процесса пошаговой разработки.
2. Благодаря своей компактности, концептуальной целостности и ортогональности понятий, а также удачному первоначальному описанию, предложенному автором языка, Pascal оказался весьма легок для изучения и освоения. В противоположность громоздким многотомным описаниям таких языков, как PL/l, Cobol, FORTRAN, достаточно полное описание языка Pascal занимает около 30 страниц текста, а его синтаксические правила можно разместить на одной странице.
3. Несмотря на относительную простоту языка, он оказался пригоден для весьма широкого спектра приложений, в том числе для разработки очень больших и сложных программ, например, операционных систем.
4. Pascal весьма технологичен для реализации практически для всех, в том числе и нетрадиционных, машинных архитектур. Утверждается, что разработка Pascal-транслятора "почти не превышает по трудоемкости хорошую дипломную работу выпускника вуза". Благодаря этому для многих ЭВМ существует несколько различных реализаций языка, отражающих те или иные практические потребности программистов.
Язык Pascal стандартизован во многих странах. В 1983 году был принят международный стандарт (ISO 7185:1983)
Основные особенности языка Pascal.
1. Pascal является традиционным алгоритмическим языком программирования, продолжающим линию Algol-60. Это означает, что программа на языке Pascal представляет собой специально организованную последовательность шагов по преобразованию данных, приводящую к решению некоторой задачи. Это отличает Pascal от так называемых непроцедурных языков типа Prolog, по существу, представляющих собой формализмы для записи начальных условий некоторой задачи и синтезирующих решение посредством встроенных механизмов логического вывода.
2. Язык Pascal содержит удобные средства для представления данных. Развитая система типов позволяет адекватно описывать данные, подлежащие обработке, и конструировать структуры данных произвольной сложности. Pascal является типизированным языком, что означает фиксацию типов переменных при их описании, а также строгий контроль преобразований типов и контроль доступа к данным в соответствии с их типом (как на этапе компиляции, так и при исполнении программ).
3. Набор операторов языка Pascal отражает принципы структурного программирования и позволяет записывать достаточно сложные алгоритмы в компактной и элегантной форме. Pascal является процедурным языком с традиционной блочной структурой и статически определенными областями действия имен. Процедурный механизм сочетает в себе простоту реализации и использования и гибкие средства параметризации.
4. Синтаксис языка достаточно несложен. Программы записываются в свободном формате, что позволяет сделать их наглядными и удобными для изучения.
5. Паскаль – компилятор, тоесть, прежде чем начать испоолнение программы, Паскаль полностью прочитывает исходный текст, написанный программистом, и составляет последовательность машинных кодов, выполняющую те действия, которые описал программист в hqundmnl тексте. Эта последовательность сохраняется в файл с расширением “.EXE” и является самостоятельным исполняемым файлом, который может быть запущен сам по себе, уже без участия Паскаля и, даже, на другом компъютере, на котором Паскаль может быть не установлен.
TURBO PASCAL
Прошло много времени с момента появления Паскаля на рынке программных продуктов, прежде чем он получил всеобщее признание. Признание программистов и простых пользователей пришло вследствие появления языка программирования Турбо Паскаль (ТП) -диалекта языка, созданного американской фирмой Борланд. Эта фирма объединила очень быстрый компилятор с редактором текста и добавила к стандартному Паскалю мощное расширение, что способствовало успеху первой версии этого языка.
В 1985 году на рынке ПЭВМ появился язык программирования Турбо Паскаль (версия 3.0) с компилятором стандартного Паскаля. С тех пор Паскаль стал применяться в общеобразовательных, профессионально-технических школах и в сфере высшего образования в качестве "первого" языка программирования. Благодаря простоте использования язык Турбо Паскаль получил широкое распространение и в любительских кругах. Повышению популярности Турбо Паскаля способствовал набор небольших сопутствующих программ (т.н. Tools), позволяющих получать чрезвычайно компактную, быструю и легко читаемую программу.
Эти качества Турбо Паскаля были высоко оценены и в среде профессиональных программистов. Встроенный редактор текста использует достаточно широко распространенную систему команд, берущую начало от пакета WordStar и хорошо знакомую каждому, кто интенсивно использует ПЭВМ.
В появившемся со временем пакете Турбо Паскаль 4.0 было устранено большинство подвергавшихся критике ограничений компилятора и была повышена производительность системы. Кроме того, новый компилятор версии 4.0 имел существенные отличия от предыдущей версии. Наиболее важным нововведением была UNIT- концепция, заимствованная из языка Модула-2. Это дало возможность реализовать в рамках ТП разработку крупных программных продуктов.
С выходом в свет версии 5.0 ТП получил еще большие шансы на благосклонную реакцию со стороны профессиональных пользователей благодаря встроенному в среду программирования интегрированному отладчику, который позволил повысить производительность труда.
Существенно улучшила технические характеристики ТП реализация аппарата перекрытий (overlays), позволяющего строить мощные программные комплексы, рассчитанные на эксплуатацию в малых по объему областях памяти. Суть механизма перекрытий сводится к делению программы на части, поочередно загружаемые по мере необходимости с дискеты или магнитного диска (винчестера) в одну и ту же область памяти, заменяя при этом находившуюся там часть программы.
Кроме того, в ТП 5.0 были расширены возможности отладки (debugging) программ и обеспечена возможность поддержки расширенной помята в стандарте Lotus-Intel-Microsoft (LIMS/EMS 4.0). Сокращение EMS обозначает Expanded Memory Specification (спецификация расширенной памяти). Нельзя путать этот вид дополнительной памяти с другим - Extended Memory (сокращенно - XMS). EMS имеется на обычных ПЭВМ класса ХТ, в то время как Extended Memory -только на машинах АТ-класса (с процессором 286, 386 и выше) при объеме памяти свыше 1 Мбайта.
В этой версии были также исправлены и улучшены библиотеки графических процедур, поставляемые вместе с пакетом ТП. При этом обеспечивалась полная совместимость с графическими адаптерами класса VGA (Video Graphics Array).
В рамках версии ТП 5.5 были осуществлены дальнейшие преобразования в направлении улучшения технических характеристик пакета. Наряду с внутренними улучшениями и новыми возможностями встроенной справочной системы Help и большим набором учебных примеров, важным нововведением явилась реализация в языке концепции объектно-ориентированного программирования (ООП).
Через некоторое время на рынке появилась версия 6.0 ТП, в которой чисто теоретическая концепция объектно-ориентированного программирования была реализована практически с полным набором объектов, которые могли использоваться для решения прикладных задач пользователя. Кроме того, реализация системы меню приведена b соответствие со стандартом SAA (Turbo Vision). В качестве практического примера использования новых возможностей был реализован текстовый редактор, встроенный в IDE - Integrated Development Environment - интегрированную инструментальную оболочку. При этом сторонники программирования на ТП 6.0 получили возможность не только работать со встроенным многооконным текстовым редактором, но и использовать мышь, которая значительно облегчает работу пользователя.
В 1992 году фирма Borland International представила пользователям очередную версию языка программирования Паскаль - Турбо Паскаль 7.0. Наряду со всеми преимуществами, которые ТП 7.0 унаследовал от предыдущей версии ТП (многооконный режим работы, возможность использования мыши, возможность использования при написании программ языка программирования низкого уровня Ассемблер или прямого ввода машинного кода, возможность создавать объектно- ориентированные программы), в нем были произведены изменения и улучшения:
1. Появилась возможность выделять определенным цветом различные элементы исходного текста (зарезервированные слова, идентификаторы, числа и т.д.), позволяющая даже неопытным пользователям устранять ошибки на этапе ввода исходного текста.
2. Язык программирования ТП 7.0 был расширен (появилась возможность использовать типизированный адресный оператор, открытые массивы и строки и т.д.), что предоставило пользователю дополнительные возможности при решении повседневных задач.
3. Был улучшен компилятор, вследствие чего "коды программ" стали более эффективными.
4. Был улучшен интерфейс пользователя. Кроме того, в ТП 7.0 расширены возможности объектно-ориентированного программирования (в частности, расширены и улучшены возможности Turbo Vision).
Основы языка Pascal.
Лингвистическая концепция языка Паскаль
Целью работы И. Вирта было создание языка, который
- строился бы на небольшом количестве базовых понятий,
- имел бы простой синтаксис,
- допускал бы перевод программ в машинный код простым компилятором.
По природе своей компьютер может выполнять только простейшие операции, которые можно вводить одну за другой в его память прямо в машинных кодах. Изнурительная монотонность такой работы привела когда-то первых программистов к естественному решению - созданию Ассемблеров, т.е. средств, упрощающих подготовку машинных кодов программ пользователя за счет написания их в некоторых мнемонических обозначениях с последующим автоматическим переводом. Дальнейшее развитие этих идей привело к созданию языков программирования высокого уровня, в которых длинные и сложные, но часто применяемые последовательности машинных операций были заменены каждая одним-единственным обозначающими ее словом - оператором. В области малых ЭВМ среди языков программирования высокого уровня следует в первую очередь назвать БЕЙСИК. Программы, написанные на этом языке, к сожалению, часто содержат запутанные последовательности операторов, называемые иногда на жаргоне программистов блюдо спагетти. Лингвистическая концепция Паскаля отрицает методы программирования, которые могут привести к подобным эффектам. В mei, напротив, пропагандируется системный подход, выражающийся, в частности, в расчленении крупных проблем на меньшие по сложности и размеру задачи, легче поддающиеся решению. Основные принципы Паскаля таковы:
· Структурное программирование. Суть его заключается в оформлении последовательностей команд как замкнутых функций или процедур и в объединении данных, связанных по смыслу, в сложные структуры данных. Благодаря этому повышается наглядность текста и упрощается его отладка.
· Проектирование сверху вниз. Программист разбивает свою задачу на несколько более простых, после чего каждая из задач решается по отдельности. Затем компонуются результаты проектирования простых задач и решается задача проектирования сверху вниз в целом.
· Объектно-ориентированное программирование делает следующий шаг от ремесла к науке программирования. Данные объединяются со свойственными им операциями обработки в некоторые объекты (Инкапсулирование). Например, данным "Человек" присуща операция "Идти". При этом свойства одних объектов могут передаваться другим, переноситься на другие классы объектов (Наследование). С другой стороны, в объектно-ориентированном программировании существует явление полиморфизма: часы тоже могут "Идти", но не ногами.
Влияние Паскаля ощущается в настоящее время в разных языках программирования. Так, среди новых диалектов Бейсика есть Паскаль с символикой Бейсика. Даже в язык С встраивается все больше элементов, порожденных Паскаль-концепцией. Необходимо отметить, что все эти явления находятся в русле характерной для современных языков программирования тенденции к конвергенции. Набор операторов стандартного Паскаля относительно мал и легко изучаем. Но это порождает проблему расширения языка в приложениях. В ТП эта проблема решается за счет поставок большого количества библиотек разнообразных процедур, готовых к употреблению в прикладных программах. Широкое распространение Паскаля привело к появлению на рынке программного обеспечения большого числа инструментальных и прикладных программ. Подобные программы разработаны для многих проблемных областей, однако задача их настройки в соответствии с требованиями пользователей продолжает оставаться достаточно важной.
Алфавит языка и специфика использования символов
Язык программирования ТП 7.0, как и любой другой, имеет свой алфавит. Как правило, алфавитом языка программирования называют набор символов, разрешенный к использованию и воспринимаемый компилятором, с помощью которого могут быть образованы величины, выражения и операторы данного языка. Алфавит языка ТП 7.0 включает в себя все символы, представленные в кодировочной таблице, которая в настоящий момент загружена в оперативную память или хранится в ПЗУ Вашего компьютера. Каждому символу алфавита соответствует индивидуальный числовой код от 0 до 255.
Примечание:
Символы с кодами от 0 до 127 представляют собой так называемую основную таблицу кодов ASCII. Их состав и порядок определены американским стандартом на коды обмена информацией. Часть кодировочной таблицы с кодами от 0 до 127, т.е. "Основная таблица кодов ASCII, идентична на всех IBM-совместимых компьютерах.
Алфавит языка ТП 7.0 составляют:
- Символы, используемые для составления идентификаторов:
- латинские строчные и прописные буквы,
- арабские цифры от 0 до 9 (в идентификаторах цифры могут использоваться наряду с буквами, начиная со второй позиции),
- символ подчеркивания (ASCII, код 95).
- Символы-разделители:
- Символ пробела (ASCII, код 32). Как уже отмечалось, символ пробела является разделителем в языке ТП 7.0. Основное назначение этого символа - разделение ключевых слов и имен.
- Управляющие символы (имеют ASCII-коды от 0 до 31). Эти символы могут применяться при описании строчных и символьных констант. Управляющие символы с ASCII-кодом 9 (табуляция), а также 10 и 13 (замыкающее строку) используются в качестве разделителей при написании программ на ТП 7.0.
В любом месте программы, где можно расположить один символ- разделитель, их можно разместить сколько угодно, т.е. для компилятора следующие записи будут эквивалентны:
A:=B+C-D;Write(A);
А := В + С - D; Write (A);
A:=
В+С
- D ; Write (А);
3. Специальные символы - символы, выполняющие определенные функции при построении различных конструкций языка:
+ - * / { } [ ] ( ) < > . , ' : ; ^ @ # $
4. Составные символы - группа символов, которые воспринимаются компилятором как единое целое:
<= => := (* *) (. .) ..
5. "Неиспользуемые" символы.
Символы так называемой расширенной таблицы ASCII, т.е. символы, имеющие коды от 128 до 255 (в этой области находятся символы алфавита русского языка и символы псевдографики на IBM-совместимых компьютерах), а также некоторые символы из основной таблицы ASCII (например: (&), (!), (%), (-), (") и некоторые другие) не входят в алфавит языка, т.е. эти символы не используются в идентификаторах. Тем не менее, их можно использовать в тексте комментариев, а также в виде значений констант строк или констант символов.
6. Зарезервированные слова.
ТП 7.0, как и его предшественники, включает в себя так называемые ключевые или зарезервированные слова (BEGIN, END, PROGRAM). В качестве имен идентификаторов в программе зарезервированные слова использоваться не могут. Если Вы будете использовать зарезервированные слова не по назначению, это вызовет ошибку при попытке откомпилировать программу.
Понятие лексемы и разделителя
Программа на Паскале состоит из последовательности лексических единиц - лексем.
Лексема - минимальная единица языка, имеющая самостоятельный смысл.
В Паскале лексемы условно делятся на несколько классов:
- идентификаторы;
- числа без знака;
- специальные знаки (слова-символы и специальные знаки);
- символьные константы (строки),
- директивы;
- метки.
Блок лексического анализа Паскаль-компилятора, рассматривая символы входного языка, должен определить, какому классу принадлежит лексема. Между лексемами разрешено вставлять один и более разделителей. В качестве разделителей в стандарте Паскаля используются пробелы, комментарии, символы «конец строки». В Турбо-Паскале кроме этих в качестве разделителей разрешено использование и других символов. Комментарии заключают в фигурные скобки. Вместо них также могут использоваться пары символов «b>(**)
Примеры записи лексем:
12–А
12 - A+5
В первом примере записаны три лексемы “
А{присвоить}:=1;
Между лексемами “А” и “:=” в качестве разделителя использован комментарий.
А:{присвоить}=1
Последний пример ошибочен, так как разделитель в виде комментария разрывает лексему ":=”.
Идентификаторы
Идентификатор является именем, которое использует программист при обращении к какому-то значению. В качестве имен не могут быть использованы зарезервированные слова (слова-символы, например слово “PROGRAM”). В стандартном Паскале идентификаторы используются для обозначения переменных, констант, типов, процедур и функций. Имена могут быть длинными, но при трансляции рассматривается ограниченное число символов (по стандарту Паскаля - первые восемь символов, тоесть идентификаторы “dlinniy_identifikator1” и “dlinniy_identifikator2” будут восприняты компилятором как одно и тоже слово). В Турбо-Паскале идентификатор кроме символов букв и цифр может содержать символ “_” (подчеркивание). Подчеркивание полезно, когда имя состоит из нескольких осмысленных слов. В общем случае следом за зарезервированным словом PROGRAM в программе стоит пробел, разделяющий в Паскале слова, и далее - имя, данное программе. Это имя представляет собой пример идентификатора. Идентификатор -имя, свободно избираемое программистом для элементов программы (процедур, функций, констант, переменных и типов данных). При обозначении какого-либо элемента программы с помощью идентификатора Вы должны руководствоваться следующими постулатами:
· Идентификатор должен начинаться буквой или символом подчеркивания “_”.
· ТП 7.0 не различает прописные и строчные буквы. Поэтому можно записать WriteLN, Writeln или даже wRITeLn, не опасаясь быть непонятым компилятором. Для него все три записи эквивалентны.
· Начиная со второй позиции, в идентификаторе можно применять наряду с буквами цифры
· Пробел в ТП 7.0 является разделителем и не может стоять внутри идентификатора. Для создания идентификаторов, состоящих из двух слов, можно воспользоваться большими буквами (например,
ReadText) или символом подчеркивания (Read_Text), но не пробелом (bRead Text – два отдельных слова).
· Применение других символов (букв неанглийского алфавита, знаков препинания, псевдографических символов и т.п.) в идентификаторах не допускается.
· Зарезервированные слова (такие как BEGIN, END или PROGRAM) в качестве идентификаторов не используются.
· Идентификаторы могут быть любой длины, но сравнение их между собой производится по первым 63 символам.
Кроме того, при написании программы рекомендуется следовать ряду правил хорошего тона, которые упрощают редактирование программы и повышают ее наглядность:
· Изобретая идентификаторы, старайтесь делать их "осмысленными", не экономьте на именах - имя ReadTest всегда лучше, чем RT. Не бойтесь потратить время на написание длинных идентификаторов. Встроенный в ИПО (Интегрированная Пользовательская Оболочка) редактор предоставляет возможность копировать и переносить фрагменты текста.
· Все структуры языка имеют англоязычные идентификаторы. Для своих элементов Вы можете изобрести русскоязычные идентификаторы (записанные латинскими литерами), например PROGRAM Privetstvie. Но для удобства, создавая идентификаторы, выполняйте не транслитерацию русских слов в английские, а перевод на английский язык (приветствие - welcome).
В нашей учебной программе слово PROGRAM и последующий идентификатор представляют собой незаконченный оператор программы. Для правильного оформления его необходимо завершить символом точка с запятой (;). Таким образов завершается каждый оператор программы на языке Паскаль, за исключением последнего оператора END, после которого всегда ставится точка, тем самым информируя компилятор об окончании текста программы.
Структура программы
Общая структура программ в ТП 7.0
Программы, написанные на языке программирования ТП 7.0, строятс в соответствии с правилами, представляющими собой несколько расширенные и "ослабленные" правила синтаксиса стандартного Паскаля. Но эти "ослабленные" правила (т.е. порядок размещения в тексте программы различных смысловых блоков) должны неукоснительно соблюдаться при написании программы. Любую программу, написанную на ТП 7.0, можно условно разделить на три основные части:
- раздел объявлений и соглашений (декларационная часть),
- раздел текстов процедур и функций,
- раздел основного блока (сама программа).
Раздел объявлений и соглашений.
PROGRAM Заголовок программы;
{$ ... } Глобальные директивы компилятора;
USES Подключаемые библиотеки;
LABEL Подраздел объявления глобальных меток;
CONST Подраздел объявления глобальных
констант;
TYPE Подраздел объявления глобальных типов;
VAR Подраздел объявления глобальных
переменных;
В первой части программы программист сообщает компилятору, какими идентификаторами он обозначает данные (константы и переменные), а также определяет собственные типы данных, которые он в дальнейшем намеревается использовать в данной программе. Например, можно объявить переменные как локальные, допустив тем самым создание объектов с одинаковыми идентификаторами внутри функций и процедур. При этом необходимо следить за тем, чтобы не возникали конфликты между локальными и глобальными объявлениями различных объектов.
Раздел текстов процедур и функций
В этом разделе записываются подпрограммы, осуществляющие сложные действия, которые необходимо произвести неоднократно на разных этапах выполнения программы. Подпограммы бывают двух типов: прjцедуры (PROCEDURE) и функции (FUNCTION). И те и другие пребставляют собой программы в миниатюре:
PROCEDURE (FUNCTION) Заголовок процедуры (функции);
LABEL Подраздел объявления локальных меток;
CONST Подраздел объявления локальных
констант;
TYPE Подраздел объявления локальных типов;
VAR Подраздел объявления локальных
переменных;
Раздел текстов подпрограмм.
BEGIN Основной блок процедуры или функции;
END;
Они могут иметь всетеже разделы, что и основная программа, в частности, раздел локальных процедур и функций, вызываемых только в педелах данной подпрограммы.
Раздел основного блока программы
BEGIN {Основной блок программы}
{текст программы}
END.
В этом разделе содержится смысловая часть программы.
Заголовок программы
Со строкой заголовка Вы уже знакомы. Она состоит из зарезервированного слова PROGRAM и имени программы. В Турбо Паскале эта строка не обязательна, и ее можно без ущерба исключить. Но правила хорошего тона в программировании требуют задания некоторого имени программы, чтобы уже при первом знакомстве можно было получить хоть какую-нибудь информацию об ее назначении. Однако не стремитесь привести здесь всю известную Вам информацию о программе - для этих целей можно воспользоваться дополнительными комментариями. Обычно в заголовке достаточно указать имя и версию программы. Следующее за оператором PROGRAM имя является идентификатором и обладает всеми его свойствами. В частности, внутри тела программы не могут быть объявлены объекты, имя которых совпадает с именем программы.
Раздел объявлений и соглашений
Глобальные директивы компилятора
В этом разделе программы компилятору можно дать указания, определяющие режимы его работы при трансляции последующей программы. Эти указания оформляются в тексте программы как комментарии, начинающиеся парой символов “{$” и заканчивающиеся символом “}”. Такие указания могут содержать указания на включение в текст программы фрагментов других программ (из соответствующих файлов), информацию для отладчика или сведения о необходимости использования арифметического сопроцессора.
Оператор USES
Оператор USES играет важную роль в подключении к тексту программы системных модулей из библиотек. В этом операторе Вы указываете компилятору, из какой библиотеки использует модули данная программа, чтобы компилятор выбрал соответствующие модули из этой библиотеки и включил их в текст программы. Понятия "библиотека", "модуль", "блок" составляют основу терминологии программирования на Паскале. Библиотека включает набор модулей, каждый из которых замкнут, имеет свое имя, компилируется отдельно и к нашей программе подключается уже как "черный ящик" с известным интерфейсом. Каждый модуль (блок (UNIT), как его называют на Паскале) представляет собой программу, включающую декларации типов и переменных, процедуры и функции.
Оператор USES может быть использован в программе только один раз, при этом у него есть свое четко определенное место, (он предваряет все прочие операторы и разделы). Названия библиотек, подключаемых к программе с помощью оператора USES, должны разделяться запятой. Например:
USES Crt, Graph, String, Overlay;
Объявления глобальных меток, констант, типов и переменных
Следом за строкой, содержащей оператор USES, идут строки объявляющие:
· метки (LABEL) (хотя их использование противоречит классической технике программирования на Паскале, дающей превосходную стройность и однозначность понимания кода программы);
- константы (CONST);
- определенные пользователем типы данных (TYPE);
- переменные (VAR).
В Турбо Паскале жесткое соблюдение именно такого порядка объявлений не требуется. В этом отношении данный диалект весьма "либерален". На практике в большинстве программ часть, заключающая в себе объявления глобальных объектов, непосредственно предшествует основному блоку программы.
В разделе описания меток LABEL содержатся перечисленные через запятую имена меток переходов. Следует помнить, что имена меток переходов не должны дублировать друг друга. Имя метки перехода может представлять собой целое число (от 0 до 9999), строку символов или символьно-цифровую конструкцию. Например:
LABEL, 1, 5, 9999, h2, 4t32e, metka_l;
В разделе CONST содержатся определения констант, используемых в программе. Например:
CONST
Year=1995;
Month='Июль';
Day='Понедельник';
Примечание:
Заметьте, что при присвоении значений константам вместо оператора присвоения “:=” используется просто знак равенства “=”. Тип константы определяется автоматически по виду значения, присваемового константе и не может быть сложным.
Раздел описания типов TYPE позволяет программисту определить новый тип в программе. В данном разделе могут быть использованы ранее определенные в разделе CONST константы.
В разделе описания глобальных переменных VAR содержится список переменных, используемых в программе, и определяется их тип. Например:
VAR
А, В, С: INTEGER; {Переменным А, В и С присваивается тип INTEGER}
DDT: REAL; {Переменной DDT присваивается тип REAL }
Примечание:
Разделы LABEL, CONST, TYPE и VAR могут располагаться в произвольном месте программы. При этом каждый из этих разделов может встречаться в программе несколько раз или вообще не встречаться в ней.
Основной блок программы
Непосредственно за заголовком программы следует основной блок программы, ограниченный операторами BEGIN и END.. Как уже говорилось, оператор END. указывает компилятору, что программа закончена, в отличие от операторов END;, которые завершают блоки, процедуры, модули и т.п.
Текст, следующий за оператором END., игнорируется транслятором.
Объем основного блока программы, написанной нами, невелик:
writeLn (' привет. Ваня !' );
writeLn;
Wlitelin(' Я надеюсь, что мы отлично');
WrlteLn (' сработаемся !');
Оператор WriteLn (Write Line - записать строку) - представляет собой стандартную процедуру, с помощью которой можно вывести на экран или на другие носители и средства отображения информации текст и числа. Подробное истолкование применения этой процедуры в первом операторе программы может быть, например, таким: "Вывести (Write) строку символов (String) "Привет, Ваня!" на экран и перевести курсор на начало следующей строки (Line) экрана". Перевод курсора на начало следующей строки вызван окончанием Ln в имени процедуры. Существует также стандартная процедура Write для вывода информации на экран без перевода курсора. После выполнения оператора WriteLn последующий вывод приведет к выдаче информации в следующую строку экрана, а после оператора Write - в ту же строку, следом за уже выведенным текстом, пока хватит места, а затем вывод продолжится уже на следующей строке экрана. Оператор WriteLn; без параметров просто переведет курсор на начало следующей строки. Операторы WriteLn и Write присутствуют практически в каждой Паскаль-программе. Скобки, следующие за оператором, необходимы для задания параметров процедуры. Причем, компилятор не рассматривает слова "Привет" и "Ваня" как идентификаторы для каких-либо переменных, т.к. они стоят внутри фрагмента текста, ограниченного апострофами. Заканчивается этот оператор, как и любой другой, точкой с запятой.
Еще одна характерная особенность Паскаль-программ - ступенчатая форма записи. Строки, относящиеся к одной конструкции или связанные по смыслу, записываются с одной и той же позиции. Строки, относящиеся к подчиненной конструкции, записываются правее ("глубже"), например, на две позиции, благодаря чему наглядно представляется структура программы. В длинных программах этот подход позволяет фиксировать соответствие пар операторов BEGIN - END. Редактор текста в ИПО поддерживает такую технологию оформления текстов программ: после нажатия клавиши [Enter] курсор переходит на следующую строку в ту позицию, с которой начинается текст в предыдущей, а клавиша [Backsрасе] из этого положения переводит курсор на конец предыдущей строки.
Примечание:
Ступенчатое оформление программы преследует только "эстетические" цели и не влияет на эффективность работы компилятора или программы. Компилятор обрабатывает Паскаль- программы с любым расположением операторов: как разделенные построчно нажатием клавиши [Enter] при подготовке текста программы, так и записанные подряд в одну строку.
Например:
program hello; begin writeln('
Привет
,
Ваня
!'); writeln;
writeln('Я надеюсь, что мы отлично'); writeln ('
сработаемся!'); end.
Однако "воспитанные" программисты не могут себе позволить оформлять программы подобным образом, ведь таже программа, записанная с табуляцией, читается гараздо легче:
program hello; {заголовок программы}
begin {начало сегмента команд программы}
writeln('Привет, Ваня!'); {вывод строки}
writeln; {перевод позиции курсора на строку ниже}
writeln('Я надеюсь, что мы отлично'); {вывод строки}
writeln('сработаемся!'); {вывод строки}
end. {конец программы}
Вернемся еще раз к символу точка с запятой, завершающему каждый оператор. Наличие точки с запятой обязательно, т.к. этот символ показывает Паскаль-компилятору, где заканчивается один оператор и начинается следующий. Благодаря тому, что Паскаль - язык со "свободной формой записи", можно без опасений "растянуть" оператор на несколько строк. С помощью символа точка с запятой Вы сообщаете Паскаль-компилятору, какую часть текста программы следует рассматривать как цельный, неделимый фрагмент. Зарезервированное слово BEGIN, с которого начинаются блоки программы, не требует после себя символа точка с запятой.
Типы переменных.
Тип переменной задает вид того значения, которое ей присваивается и правила, по которым операторы языка действуют с переменной, например:
Если переменные A и B целочисленного типа, то программа:
A:=3.14;
B:=2.71;
WRITELN(A,’ ‘,B,’ ‘,A+B);
Выведет на экран строку: “3.14 2.71
Если же они строкового типа, то программа:
A:=’3.14’;
B:=’2.71’;
WRITELN(A,’ ‘,B,’ ‘,A+B);
Выведет: “3.14 2.71 3.142.71”, так как оператор сложения просто добавит строку B в конец строки A.
В Паскале предопределены следующие простейшие типы переменных:
· Тип константы определяется способом записи ее значения:
·
· Const
· C1=17;
· C2=3.14;
· C3='A';
· C4=False;
· C5=C2+C1;
Можно использовать выражения. Выражения должны в качестве операторов содержать только константы, в том числе ранее объявленные, а так же знаки математических операций, скобки и стандартные функции.
- BYTE – целое число от 0 до 255, занимает одну ячейку памяти (байт).
- BOOLEAN – логическое значение (байт, заполненный единицами, или нулями), true, или false.
- WORD – целое число от 0 до 65535, занимает два байта.
- INTEGER – целое число от –32768 до 32767, занимает два байта.
- LONGINT – целое число от –2147483648 до 2147483647, занимает четыре байта.
- REAL – число с дробной частью от 2.9*10-39.до 1.7*1038, может принимать и отрицательные значения, на экран выводится с точностью до 12-го знака после запятой, если результат какой либо операции с REAL меньше, чем 2.9*10-39, он трактуется как ноль. Переменная типа REAL занимает шесть байт.
- DOUBLE - число с дробной частью от 5.0*10-324.до.1.7*10308, может принимать и отрицательные значения, на экран выводится с точностью до 16-го знака после запятой ,если результат какой либо операции с DOUBLE меньше, чем 5.0*10-324, он трактуется как ноль. Переменная типа DOUBLE занимает восемь байт.
- CHAR – символ, буква, при отображении на экран выводится тот символ, код которого хранится в выводимой переменной типа CHAR, переменная занимает один байт.
- STRING – строка символов, на экран выводится как строка символов, коды которых хранятся в последовательности байт, занимаемой выводимой переменной типа STRING; в памяти занимает от 1 до 256 байт – по количеству символов в строке, плюс один байт, в котором хранится длина самой строки. При обьявлении переменной строкового типа можно заранее указать ее длину в байтах – X:
MyString:STRING[X];
При присвоении этой переменной строки длиннее X, присваиваемая строка будет обрезана с конца после X-того символа.
Размер переменной типа STRING в памяти можно узнать следующим способом:
Size:=SizeOf(MyString);
Функция SizeOf() возвращает размер, занимаемый переменной, служащей параметром. Параметром может служить и тип переменной; строка:
Writeln(SizeOf(STRING));
Выведет на экран число 256, так как по умолчанию под все строки отводится по 256 байт.
Кроме того, можно узнать, сколько символов в строке (индекс последнего непустого символа в строке):
Size:=Ord(MyString[0]);
Используется ибращение к нулевому элементу (символу) строки, в котором хранится ее длина, но MyString[0] – значение типа CHAR, тоесть символ, код которого равен длине строки, нужный нам код – число возвращает функция Ord()Таким же образом можно обратиться к любому N – тому элементу строки:
MyChar:=MyString[N]; {MyChar:CHAR}
- ARRAY[a..b,c..d,….] OF “тип элемента”; - массив некоторой размерности, содержащий элементы указанного типа. Диапазоны индексов для каждого измерения указываются парами чисел или констант, разделенных двумя точками, через запятую (a..b,c..d). После OF записывается тип элементов массива. В памяти массив занимает место, равное: (b-a)*(d-c)*..* SizeOf(“тип элемента”). Размер массива не может превосходить 65536 байт. Обращение к элементам массива происходит следующим образом:
X:=MyArray[a,b,c,..];
При этом переменная X должна быть того же типа, что и элементы массива или приводимого типа. Число индексов (a,b,c,..) должно быть равно числу обьявленных при описании измерений массива.
О приводимости типов.
В паскале существуют ограничения на присвоение значений одних переменных другим. Если переменные которую и которой присваивают одного типа, то никаких проблем не возникнет. Но если они разных типов, присвоение не всегда может быть произведено. Это связано стем, что при таком присвоении необходимо отсечь часть информации, а какую – компьютер “не знает”. Проблема возникает при следующих присвоениях:
I:=J; {I:INTEGER; J:REAL}
A:=B; {A:CHAR; B:STRING}
В то же время, такие присвоения будут выполнены вполне корректно:
J:=I;
B:=A;
При этом переменная J примет значение с нулевой дробной частью, а B – станет строкой, содержащей один символ – из A. В первом же случае, можно поизвести следующие операции:
I:=Trunc(J); {функция trunc() возвращает целую часть аргумента}
I:=Round(J); {round() – округляет аргумент стандартным способом}
Кроме рассмотренного случая может существовать множество других, все не описать, но наиболее общее правило таково: следить за однозначностью присвоения с потерями информации и не удивляться, а экспериментировать переделывать программу, если компилятор выдает сообщение о невозможности присвоения.
Операторы языка Pascal
Составной и пустой операторы.
Составной оператор - это последовательность произвольных операторов программы, заключенная в операторные скобки.
Турбо-Паскаль допускает произвольную глубину вложенности:
Begin
...
Begin
...
Begin
...
End;
End;
End;
Наличие ; перед End - пустой оператор.
Операторы ветвлений.
Условный оператор.
IF <условие> THEN <оператор1> [ELSE <оператор2>]
Условие – значение типа BOOLEAN или логическая операция. Если условие верно, выполняется оператор, или блок операторов, следующий за THEN, в противном случае выполняется блок операторов после ELSE, если он есть.
Условия могут быть вложенными и в таком случае, любая встретившаяся часть ELSE соответствует ближайшей к ней "сверху" части THEN.
Пример:
Var
A, B, C, D: Integer;
begin
A:=1;
B:=2;
C:=3;
D:=4;
If A > B Then
If C < D Then
If C < 0 Then
C:=0 {обратите внимание, что перед Else пустой оператор ";"не ставится}
Else
A:=B;
end.
а могло быть и так:
If A > B Then
If C < D Then
If C < 0 Then
C:=0
Else
Else
Else A:=B
Рассмотрим программу, которая вводит произвольное целое число от 0 до 15 и выводит его в шестнадцатеричной системе:
Program Hex;
Var
Ch: Char;
N: Integer;
Begin
Write ('N = ');
Readln(N);
If (N >= 0) And (N <= 15) Then
Begin
If N < 10 Then
Ch:= Chr(Ord('0')+N)
Else
Ch:=Chr(Ord('A')+N-10);
End
Else
Writeln('
Ошибка
');
End.
Операторы повторений.
Цикл с предопределенным числом повторений.
For <переменная цикла>:=<начальное значение> To(DownTo) <конечное значение> Do <блок операторов>
Переменная должна быть целого или перечислимого типа. При исполнении цикла переменная цикла изменяется от начального до конечного значения с шагом 1. Если стоит to, то переменная увеличивается, если downto – уменьшается.
Условия выполнения цикла проверяются перед выполнением блока операторов. Если условие не выполнено, цикл For не выполняется. Следующая программа подсчитывает сумму чисел от 1 до введенного:
Program Summa;
Var
I, N, S: Integer;
Begin
Write('N = ');
Readln(N);
S:=0;
For I:=1 To N Do
S:=S + I;
Writeln ('C
умма
= ', S)
End.
Условный цикл с проверкой условия перед исполнением блока операторов.
While <условие> Do <блок операторов>
Блок операторов будет исполняться, пока условие имеет значение true. Необходимо, чтобы значение условия имело возможность изменения при исполнении блока операторов, иначе исполнение цикла не закончится никогда (в DOS это приведет к зависанию компыютера). Если условие зарание ложно, блок операторов не исполнится ни разу.
Найдем машинное "эпсилон" для переменной типа Real:
Program Epsilondetect;
Var
Epsilon: Real;
Begin
Epsilon:=1;
While Epsilon + 1 > 1 Do
Epsilon: = Epsilon/2;
Writeln ('
Эпсилон
= ', Epsilon);
End.
Условный цикл с проверкой после выполнения блока операторов.
Repeat <тело цикла> Until <условие>
Блок операторов независимо от значения условия будет выполнен хотябы один раз. Цикл заканчивается, если после очередного исполнения блока операторов условие имеет значение true.
Пример: программа запрашивает ввод одного символа и выводит его ASCII - код, пока не будет введен пустой символ:
Program Code;
Const
Cr = 13;
Var
Ch:Char;
Begin
Repeat
Readln (Ch);
Writeln (Ch,' = ', Ord (Ch));
Until Ord (Ch) = Cr
End.
Оператор выбора одного из вариантов.
Case <ключ выбора> Of <список выбора> Else <оператор> End;
<ключ выбора> - выражение любого перечислимого типа,
<список выбора> - одна или более конструкций вида <значение ключа>:<блок операторов>.
Составим программу, имитирующую калькулятор. Программа вводит две строки: первая содержит два числа, разделенные пробелом или запятой, вторая - символ арифметического действия.
2 2
*
Признаком конца работы служит ввод любого символа, отличного от +, -, /, *.
Программа:
Program Calc;
Var
Operation: Char; {
Знак
Операции
}
X, Y, Z: Real;
Stop: Boolean;
Begin
Stop:= False;
repeat
Writeln; {Пустая Строка - Разделитель}
Write ('X, Y = ');
Readln (X,Y);
Write ('
Операция
: ');
Readln (Operation);
Case Operation Of
'+': Z: = X+Y;
'-': Z: = X-Y;
'*': Z: = X*Y;
'/': Z: = X/Y;
Else
Stop:= True;
End;
If Not Stop Then
Writeln('Z = ',Z);
Until Stop;
End.
Любому из блоков операторов списка может предшествовать не одно, а несколько значений выбора, разделенных запятыми.
Оператор безуслов ного перехода на строку с меткой.
Goto <метка>
Метка, должна быть описана в разделе описаний. Метка, описанная в процедуре (функции) локализуется в ней, поэтому передача управления извне процедуры (функции) на метку внутри нее невозможна.
Простые и структурные типы данных.
Перечисляемый и ограниченный типы.
Ранее рассматривались простые стандартные типы данных (Integer, Boolean,Char ...) - порядковые типы то есть к переменным этих типов применимы Succ и Pred. Real - не порядковый тип.
В Паскале разрешено введение новых типов. Секция Type располагается между секцией констант и секцией переменных. Введение своих типов повышает "читабельность" программ.
Например: Месяцы.
Type
Month = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
Var
M: Month;
Максимальная мощность перечисляемого типа - 256 значений, поэтому перечисляемый тип фактически задаёт подмножество типа Byte.
В Паскале отсутствуют средства, которые бы позволяли осуществить непосредственный ввод - вывод переменных перечисляемого типа, однако можно вывести код: Write(Ord(M)).
Кроме того применимы операции сравнения:
If (M > FEB) And (M < JUN) Then
Write ('Весенний месяц');
Следует иметь ввиду, что упорядоченность элементов перечисляемого типа определяется порядком их следования. Самый левый элемент имеет минимальное значение, правый - максимальное.
Пример:
процедура, в которой переменной перечисляемого типа присваивается значение, в зависимости от введенного дня недели; если введённые символы ошибочны, возвращается соответствующее сообщение.
Program DAY (Input, Output);
Type
DayOfWeek = (Sun, Mon, Tue, Wen, Thu, Fri, Sat);
Var
D: DayOfWeek;
Well: Boolean;
Procedure Read_WD (Var I: DayOfWeek;Well: Boolean;);
Var
C: Char;
Begin
Read(C);
Well:= False;
Case C Of
"S": Begin
Readln(C);
Case C Of
"U": Begin
I:= Sun;
Well:= True;
End;
"A": Begin
I:= Sat;
Well:= True;
End;
End;{Of Case}
"M": Begin
Well:= True;
I:= Mon;
End;
"T": Begin Readln(C);
Case C Of
. . . . . . . . .
End;
"W": Begin
Well:= True;
I:= Wen;
End;
"F": Begin
Well:= True;
I:= Fri;
End;
End;
End;
Begin
Read_WD(D, Well);
If Not Well Then
Writeln ('
Ошибка
');
End.
Кроме перечисленного типа можно вводить ограниченные или интервальные типы.
Пример:
Type
Year = 1900..2000;
Letter = "A".."Z";
Левая и правая константы задают диапазон значений и их называют соответственно верхней и нижней границей ограниченного типа.
Константы в определении ограниченного типа должны относиться к одному и тому же базовому типу (целому, символьному, порядковому). Базовый тип констант определяет допустимость операций над данными ограниченного типа.
Пример:
Type
Days = (Mo, Tu, We, Th, Fr, Sa, Su);
WeekEnd = Sa..Su;
Var
W: WeekEnd;
. . . . . . . . . . . .
Begin
. . . . . . . . . . . .
W:= Sa;
. . . . . . . . . . . ,
Так как тип-диапазон наследует все свойства базового типа, но с ограничениями, связанными с его меньшей мощностью, то Ord(W) вернёт значения S , в то время как Pred(W) приведёт к ошибке.
И перечисляемый и ограниченный типы относят к простому типу.
Простой тип - это такой тип, который в некоторый момент времени хранит только одно значение.
Структурированные типы данных - совокупность связанных данных и множество правил, определяющих, как их организацию так и способ доступа к элементам данных.
Массив - это упорядоченный набор переменных одного типа.
Массив содержит фиксированное число компонент, которое задаётся при определении переменных типа массива. Тип компоненты массива может быть любым. Каждый элемент масива имеет свой индекс. Тип индекса - простой, поэтому все элементы массива могут быть перебраны при помощи операторов цикла.
Описание массивов:
Var
Mas: Array [1..15] Of Real.
Work: Array [(Mon, Tue, Wed)] Of Integer.
B: Array ['A'..'Z'] Of Boolean.
C: Array [1..3, 1..5] Of Real.
D: Array [(Black, White)] Of 11..20.
В Паскале многомерный массив можно описать как одномерный:
Type
Mas = Array [1..3] Of Array [1..5] Of Integer;
Var
A, B: Mas;
C: Array [1..3, 1..5] Of Integer; {такая же структура но определена как двумерный массив}
Ссылка на элемент матрицы А, лежащий на пересечении I-той строки и J-ого столбца выглядит следующим образом A [I] [J]; законно и такое обращение A[2]:= B[1], для массива такая запись не верна. Все элементы структурированных типов допускают A:= B (исключение - переменные файлового типа).
Можно использовать комформант - массивы. Массивы с переменными границами в Турбо-Паскале использовать нельзя. Нельзя, также и сравнивать массивы:
If A = B Then ...
В Турбо-Паскале предварительно определены два массива Mem и Port.
Символьные строки.
Переменную типа Packed Array [0..N] of Char принято называть символьной строкой.
Строка - это упакованный массив, компоненты которого имеют тип Char и тип индекса имеет нижнюю границу равную одному. К строкам применимы все 6 операций отношений, но строки при этом должны иметь равную длину.
В Турбо-Паскале введён тип данных String вместо описанного Packed Array of Char.
Операции с Char:
'5' < '25' {ошибка, так как разные длины строк}
'Var' = 'Var' {
верно
}
Var
Age: String [3];
Begin
Age:="тринадцать"; {Лишние символы после "и" усекаются}
Множества.
Паскаль позволяет оперировать с множествами как с типами данных.
Пример:
Type
Symbolset = Set Of ' ' .. '_';
Color = (White, Blue, Red);
Colorset = Set Of Color;
T1= Set Of 0..9;
Var
C: Color;
Colset: Colorset;
T: Integer;
Tset: T1;
Множества - наборы однотипных объектов, каким-либо образом связанных между собой. Характер связей лишь подразумевается программистом и никак не контролируется Турбо-Паскалем. Максимальное количество элементов множества - 256.
Два множества считаются эквивалентными тогда и только тогда, когда все элементы их одинаковы, причём порядок следования элементов безразличен.
Описание:
<имя типа> = Set Of <базовый тип>, где <базовый тип> есть любой порядковый тип кроме Word, Integer, Longint. Для задания множества используется конструктор множества (((: : =))). Список спецификаций элементов множества, отделяемых друг от друга запятыми; список обрамляется [ ]. Спецификациями элементов могут быть константы или выражения базового типа, а так же тип - диапазон того же базового типа:
Type
Digitchar = Set Of '0'..'9';
Digit =Set Of 0..9;
Var
S1, S2, S3: Digitchar;
S4, S5, S6: Digit;
begin
. . . . . . . . . . . . . . . .
S1 : = ['1', '2', '3'];
S2 : = ['3', '2', '1'];
S3 : = ['2', '3'];
S4 : = [0..3, 6];
S5 : = [4, 5];
S6 : = [3..9];
Операции над множествами:
* - пересечение (S4*S6 = [3,6]; S4*S5 = [4])
+ - объединение (S4+S5 = [0, 1, 2, 3, 4, 5, 6])
- -разность, содержит элементы из 1-го, которые не принадлежат второму (S6-S5 = [3, 6, 7, 8, 9])
= - проверка эквивалентности (S4=S4 = true; S5=S4 = false)
<> - проверка неэквивалентности (S4<>S4 = false; S5<>S4 = true)
<= - проверка вхождения (True если 1-е содержится во втором).
>= - (наоборот).
in - проверка принадлежности.
Пример:
Алгоритм получение простых чисел.
Const
N = 100;
Type
Set_Of_Num = Set Of 1..N;
Var
N1, Next, I: Word;
Begset, Primerset: Set_Of_Num;
Begin
Begset:= [2..N];
Primerset:= [1];
Next:=2;
While Begset <> [ ] Do
Begin
N1:= Next;
While N1 <= N Do
Begin
Begset:= Begset - [N1];
N1:= N1 + Next;
End;
Primerset:= Primerset + [Next];
Repeat
Inc(Next);
Until (Next In Begset) Or (Next > N);
End;
For I:=1 To N Do
If I In Primerset Then
Write(I: 8);
Writeln;
End.
Итак, над множествами допустимы четыре операции:
- объединение (+)
- пересечение (*)
- разность (-) (содержит элементы из 1-го, которых нет во 2-ом).
- операция in - позволяет определить, принадлежит элемент множеству или нет.
В программе множество задаётся в виде списка элементов, заключённого в квадратные скобки:
Colset:= [White, Red];
Colset:= [ ];
Tset:= [1, 7, 5];
Tset:= [0 ... 3, 6, 9];
Tset:= [8 Mod4, 15 Div 5];
Можно использовать операции сравнения.
Пример: из файла вводится текст, содержащий символы от "+" до "[". Требуется рассчитать символы текста в порядке кода ASCII (из повторно встречающихся выводить только один):
Program Sort
Var
S: Char;
Sets: Set Of '+'..'[';
I: '+'..'[';
Begin
Sets:= [ ];
Read(S);
While Not Eof Do
Begin
While Not Eof Do
Begin
Sets: = Seets + [S];
Read(S);
End;
End;
Writeln;
For I: ='+' To '[' Do
If I In Sets Then
Write (I);
Writeln;
End.
Записи.
Запись - наиболее общий и гибкий структурированный тип в Паскале.
Запись состоит из фиксированного числа компонентов, называемых полями, которые могут быть различных типов. Этим запись существенно отличается от массива, все компоненты которого должны быть одного и того же типа и доступ к компонентам осуществляется не по индексам (номерам), а по именам.
Пример:
Type
Date = Record
Year: Integer;
Month: 1..12;
Day: 1..31;
End;
Book = Record
Title: String [40];
Author: String [50];
Entry: Date;
End;
Var
D1: Date;
B: Book;
В Паскале разрешено использовать массивы записей. Записи также можно использовать в качестве компонент файлов. Однако поля записи не могут быть файлового типа. Чтобы обратиться к отдельной компоненте записи необходимо задать имя записи, за ним точку и сразу за точкой написать название нужного поля.
Пример1:
D1.Day:=25;
B.Author:= "Levy";
B.Entry.Year:= 1980;а можно и
B. Entry: = 1980; так как Year первое поле Date.
Пример2: В памяти находится массив из 1000 элементов, каждый из которых имеет тип записи Book. Определить количество книг, год издания которых меньше или равен 1600 году. Для каждого из таких элементов массива распечатать название книги, имя автора, год издания:
Program Rarity;
Type
Date = Record
. . . . . . . . . . .
End;
Book = Record
. . . . . . . . . . .
End;
Var
S, I: Integer;
Mas: Array[1..1000] Of Book;
Begin
S:= 0;
For I:= 1 To 1000 Do
If Mas[I].Entry.Year <= 1600 Then
Begin
Write (Mas[I].Title,' , ',Mas[I].Author,',' , ',Mas[I].Entry.Year: 6);
S:= S+1;
End;
Writeln ('
Число
Книг
' , S: 4);
End.
Оператор присоединения With.
Можно сократить длинные обозначения элементов записи:
With имя записи Do оператор.
Оператор With открывает область действия, содержащую имена полей указанной переменной типа записи так, что эти имена фигурируют в качестве имён переменных. Кроме экономии места при написании программ, оператор With экономит так же время при выполнении программы, так как ссылка на запись подготавливается только один раз.
Пример:
With B Do
Begin
Title: = ' ... ';
Author:= ' ...
';
End;
эквивалентно
B.Title: = ' ...
';
B.Author:= ' ... ';
Запись с вариантами.
Вариантная часть начинается со слова Case. Это означает, что в записях можно задавать тип, содержащий определения нескольких вариантов структуры. Различие может касаться как числа компонент, так и их типов. Запись может содержать только одну вариантную часть (экономия памяти). Вариантная часть сама может содержать варианты (вложения).
Пример:
Type
N = String [20];
Status = (
Женат
,
Вдов
,
Разведён
,
Холост
);
Date = Record
Mo: 1..12;
Day: 1..31;
Year: Integer;
End;
Person = Record
Name: N;
Sex: (
Муж
,
Жена
);
Birth: Date;
Case Ms: Status Of
Женат
,
Вдов
: (MDate: Date);
Разведён
: (Date: Date; First: Boolean);
Холост: (Indept: Boolean);
End.
Замечательная особенность - наложение в памяти вариантных полей, то есть дополнительная возможность преобразования типов:
Var
M = Record
Case Byte Of
0: (By: Array[0..3] Of Byte);
1: (Wo: Array[0..3] Of Word);
2: (Lo: Longint);
End;
. . . . . . . . . . . . . .
With M Do
Begin
Lo: . . . . . .;
If By [3] = 2 Then ... ;
Case Of, открывающее вариантную часть не имеет ничего общего с ветвлением Case Of; в данном случае это директива компилятору, сигнализирующая о том, что последующие поля нужно разместить начиная с одной и той же ячейки памяти, поэтому, если изменяется одно из полей - вариантов, изменяются и все остальные. Поле выбора должно быть порядкового типа.
В Турбо-Паскале, в отличие от классики, переменной, описанной в поле ключа выбора, можно присваивать значения, но это ни как не влияет на выбор поля. Значения констант выбора могут быть произвольными и даже повторяющимися. Вместо поля выбора можно иставить лишь идентификатор какого - либо типа, но в таком случае в программе невозможно будет узнать, каким из альтернативных полей используется память, а значит и то, значение какого типа там лежит, но это не всегда и нужно.
Пример:
Type
Rec1 = Record
A: Byte;
B: Word;
End;
Rec2 = Record
C: Longint;
Case X: Byte Of
1: (D: Word);
2: (E: Record);
Case Boolean Of
3: (F: Rec1);
3: (G: Single);
'3': (C: Word);
End;
End;
Var
2: Rec2
. . . . . . . . . .
R.X: = 255;
If R.E.G = 0 Then
Writeln (0K)
Else
Writeln (R.E.G);
Имена полей должны быть уникальными в пределах той записи, где они объявлены, однако если записи содержат поля-записи то есть вложены одна в другую, имена могут повторяться на разных условиях вложенности.
Совместимость и преобразования типов.
Турбо-Паскаль - типизированный язык, следовательно, все применяемые операции определены только над операндами совместимых типов.
Два типа считаются совместимыми, если
- оба они есть один и тотже тип.
- один тип есть тип-диапазон второго типа.
- оба они являются типами-диапазонами одного и того же базового типа.
- один тип есть строка, а другой - строка или символ.
- оба они есть процедурные типы с одинаковым типом результата (для типа-функции), одинаковым количеством параметров и одинаковым типом взаимно соответствующих параметров.
В Турбо-Паскалевской программе данные одного типа могут преобразовываться в данные другого, явным или неявным образом.
При явном преобразовании используются специальные функции Ord, Trunc, Round, Chr, Ptr (преобразует четырёхбайтный целочисленный аргумент к типу-указателю).
Преобразование может достигаться применением идентификатора (имени) стандартного типа, или определённого пользователем типа, в качестве идентификатора функции преобразования к выражению преобразуемого типа (так называемое автоопределённое преобразование типов). Например, допустимы следующие вызовы функций:
Type Mytype = (A, B, C, D);
. . . . . . . . . . . . . . . . .
Mytype (2);
Integer (D);
Pointer (Longint (A) + $FF);
Char (127 Mod C);
Byte (K);
При автоопределённом преобразовании типа выражения может произойти изменение длины его внутреннего представления (уменьшение или увеличение).
В Турбо-Паскале есть ещё один явный способ: в ту область памяти, которую занимает переменная некоторого типа, можно поместить значение выражения другого типа, если только длина внутреннего представления вновь размещаемого значения в точности равна длине внутреннего представления переменной. С этой целью вновь используется автоопределённая функция преобразования типов, но уже в левой части оператора присваивания:
Type
Byt = Array [1..2] Of Byte;
Int = Array [1..2] Of Integer;
Rec = Record
X: Integer;
Y: Integer;
End;
Var
VByt: Byt;
VInt: Int;
VRec: Rec;
Begin
Byt (VInt[1])[2]:= 0;
Int (VRec)[1]:= 256;
End.
Неявное преобразование типов возможно только в двух случаях:
- выражение из целых и вещественных приводится к вещественным
- одна и та же область памяти трактуется попеременно как содержащая данные то одного, то другого типа.
Совмещение данных может произойти при использовании записей с вариантами, типизированных указателей, содержащих одинаковый адрес, а также при явном размещении данных разного типа в одной области памяти (используется Absolute - за ним помещается либо абсолютный адрес, либо идентификатор ранее определённой переменной).
Абсолютный адрес - пара чисел, разделённых двоеточием - первое - сегмент, второе - смещение.
Пример:
B: Byte Absolute $0000:$0055;
W: Longint Absolute 128:0; Если за Absolute указан идентификатор переменной, то происходит совмещение в памяти данных разного типа, причём первые байты внутреннего представления данных будут располагаться по одному и тому же абсолютному адресу:
Var
X: Real;
Y: Array [1..3] Of Integer Absolute X;
Типизированные константы
Описываются в разделе констант:
<идентификатор> : <тип> = < значение >
В ходе выполнения программы можно присваивать другие значения. При повторном входе в блок (процедуру или функцию), в котором объявлена типизированная константа, переинициализации не происходит и она сохраняет то значение, которое имела при выходе из блока. Могут быть любого типа, кроме файлов. Нельзя так же объявить типизированную константу-запись, если хотя бы одно её поле файлового типа. Нельзя использовать в качестве значения при объявлении других констант или границ типа-диапазона.
Примеры:
- Типизированные константы простых типов и типа String:
2.
3.
Type
4.
Colors = (White, Red, Black);
5.
6.
Const
7.
{Правильные объявления}
8.
Cyrrcol: Colrs = Red;
9.
Name: String = 'Ку-Ку';
10.
Year: Word = 1989;
11.
X: Real = 0.1;
12.
Min: Integer = 0;
13.
Max: Integer = 10;
14.
Days: 1..31 = 1;
15.
Answer: Char = 'Y';
16.
17.
{Неправильные объявления}
18.
Mars: Arrray [Min..Max] Of Real;
19.
A,B,C: Byte = 0;
20.
X: Real = Pi;
21.
22.
Var
23.
Namef: String [22] = 'Prog.Pas';
24. Типизированные константы-массивы
В качестве начального значения используется список констант, отделённых друг от друга запятыми, список заключается в круглые скобки:
25.
26.
Type
27.
Colors = (White, Red, Black);
28.
Const
29.
Colstr: Arrray [Colors] Of String [5] = ('White', 'Red', 'Black');
30.
Vector: Array [1..5] Of Byte = (0, 0, 0, 0, 0);
При объявлении массива символов можно использовать то обстоятельство, что все символьные массивы и строки в Турбо- Паскале хранятся в упакованном формате,.поэтому в качестве значения массива-константы типа Char допускается указывать символьную строку:
Const
Digit: Array [0..9] Of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
Digchr: Aray [0..9] Of Char = ('0123456789');
При объявлении многомерных - множество констант, соответствующее каждому измерению заключается в дополнительные круглые скобки и отделяется от соседей множества запятыми.
Множество констант с максимальной глубиной вложения связывается с изменением самого правого индекса массива.
Пример - вывести на экран три строки с монотонно увеличивающимися целыми числами:
Var
I, J, K, L: Integer;
Const
Matr: Array [1..3, 1..5] Of Byte = ((0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14));
Cube: Array [0..1, 0..1. 0..2] Of Integer = (((0, 1, 2),(3, 4, 5)), ((6, 7, 8),(9, 10, 11)));
Mas4: Array [0..1, 0..1, 0..1, 0..1] Of Word = ((((0, 1), (2, 3)), ((4, 5), (6, 7))), (((8, 9), (10, 11)), ((12, 13), (14, 15))));
Begin
{
Циклы
и
Writeln}
End.
Количество переменных в списке констант должно строго соответствовать объявленной длине массива по каждому измерению.
31. Типизированные константы-записи.
<идентификатор>:<тип> = (<список значений полей>)
<тип> - предварительно объявленный тип записи.
<список значений полей > - список из последовательностей следующего вида: имя поля, двоеточие и константа; элементы списка отделятся друг от друга точкой с запятой.
Пример:
32.
33.
Type
34.
Point = Record
35.
X,Y: Real;
36.
End;
37.
Vect = Array [0..1] Of Point;
38.
Month = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
39.
Date = Record
40.
D: 1..31;
41.
M: Month;
42.
Y: 1900..1999;
43.
End;
44.
Const
45.
Orign: Point = (X: 0; Y: -1);
46.
Line: Vector = ((X: -3.1; Y: 1.5), (X: 5.9; Y: 3.0));
47.
Someday: Date = (D: 16; M: Mar; Y: 1989);
Для записей с вариантными полями указывается только один из возможных вариантов констант:
Type
Forma = Record
Case Boolean Of
True:
(Place: String [40]);
False:
(Country: String [20];
Port: String [20];
Date: Array[1..3] Of Word;
Count: Word)
End;
Const
Con1: Forma = (Country: '
Россия
';
Port: '
Москва
';
Date: (16,3,89);
Count: 10);
Con2: Forma = (Place: '
Петрозаводск
');
48. Типизированные константы множества.
Значение типизированной константы множества задаётся в виде правильного конструктора множества:
49.
50.
Type
51.
Days = Set Of 1..31;
52.
Dige = Set Of '0'..'9';
53.
Error = Set Of 1..24;
54.
Const
55.
Workdays: Days = [1..5, 8..12, 15..19, 22..26, 29, 30];
56.
Evendigits: Dige = ['0', '2', '4', '6', '8'];
57.
Errorflag: Error = [ ];
58. Типизированные константы указатели.
Единственным значением типизированной константы указателя может быть только Nil:
59.
60.
Const
61.
Pr: ^Real = Nil;
62.
P: POINTER = Nil;
Процедуры и функции.
Блочная структура программ.
Подпрограмма - обособленная именованная часть программы со своим собственным локальным контекстом имён.
Или иначе: часть программы, оформленная в виде отдельной синтаксической конструкции и снабжённая именем. Вызов подпрограммы осуществляется по имени. Локальные объекты (константы, переменные, типы, подпрограммы) доступны только внутри подпрограммы. В Паскале подпрограммы бывают двух типов: процедуры и функции.
Общая структура подпрограммы.
Структура подпрограммы в Турбо-Паскале почти буквально повторяет структуру всей программы, и в общем случае состоит из 3-х основных компонент:
1. Интерфейс подпрограммы (информация, необходимая для её вызова). Сосредоточен в заголовке.
2. Локальный контекст подпрограммы (совокупность описаний объектов, с которыми осуществляются действия)
3. Собственно действия, составляющие смысл подпрограммы (последовательность операторов и вызовов подпрограмм, тоесть программа на Паскале).
Процедуры и функции различаются назначением и способом использования.
Процедуры служат для задания совокупности действий, направленных на изменение внешней по отношению к ним программной обстановки. Или по-человечески, процедура, это подпрограмма, которая обрабатывает данные, принимает или выводит информацию, меняет или нет каким-либо образом глобальные и локальные переменные, вообще что-то делает.
Смысл функций - определить алгоритм вычисления нового значения некоторого простого или ссылочного типа. Функция, это подпрограмма, которая, как и процедура, что-то делает, но, помимо этого, она обязательно возвращает значение, тип которого задается при описании заголовка процедуры. Вызов функции является одним из допустимых операндов выражения, обозначая в нём то значение, которое вычисляет функция, тоесть, если подпрограмма с идентификатором “Function1” – функция, то можно произвести следующие действия:
X:=Function1(“параметры – аргументы функции”);
X:=2*Function1(a,e,…,m,…)-1/Function1(c,f,…,k,…); и т.д.
Области видимости объектов.
1. Имена объектов, описанных в некотором блоке, считаются известными в пределах данного блока, включая и все вложенные блоки.
2. Имена объектов, описанные в блоке, должны быть уникальными в пределах данного блока и могут совпадать с именами объектов из других блоков.
3. Если в некотором блоке описан объект, имя которого совпадает с именем объекта, описанного в объемлющем блоке, то последнее становится недоступным в данном блоке (экранировка).
Пример:
Program X;
Var
I,j:integer;
z:real;
Procedure x1(i:integer;);
Var
j:integer;
begin
End;
Begin
End;
Procedure x2;
Var
z:integer;
Procedure x3(z:string);
Begin
End;
Function y4(z:string):integer;
Begin
End;
Begin
End;
Begin
End.
Приведенная ниже диаграмма показывает видимость идентификаторов в примере. Каждый идентификатор виден в пределах того прямоуголльника, в котором он описан и в прямоугольниках, содержащихся в данном. За пределами этого прямоугольника идентификатор либо неизвестен, либо имеет другое значение, тип. Так, например, функция y4 может быть вызвана лишь внутри процедур x2 и x3, аналогично и x3. Переменные z в теле пограммы и в каждой из x2, x3, y4 имеют абсолытно разные и независимые значения, а в x1 видима таже z, что и в теле программы.В тоже время переменные i и j перекрыты в x1 новыми определениями, а в x2, x3, y4, они те же, что и в основной программе.
Механизм передачи параметров.
В заголовке подпрограммы может быть задан список формальных параметров. Каждый параметр, заданный в заголовке, считается локальным в данной подпрограмме.
Идентификаторы формальных параметров можно считать условными обозначениями реальных (фактических) параметров, которые будут переданы в подпрограмму при её вызове.
Внимание:
1. Типы формальных параметров должны обязательно обозначаться идентификаторами.
2.
3.
Недопустимо
: Procedure InCorrect (Var A: Array [1..10] of Byte);
4.
5.
Нужно: Type
6.
MyArray = Array [1..10] of Byte;
7.
Procedure Correct (Var A: MyArray);
8. Допустимы по крайней мере три способа задания формальных параметров:
a. параметры, перед которыми отсутствует служебное слово Var и за которыми следует идентификатор типа;
- параметр, перед которым Var и далее тип;
- параметр со словом Var и не имеющие типа.
Эти три способа задания формальных параметров отражают три различных способа передачи параметров a - по значению; b - по ссылке; c - передача нетипизированных параметров по ссылке (b, c - параметры-переменные).
Параметры - значения.
Наиболее распространенный и простой способ. Параметр - обычная локальная переменная. Может использовать выражение. Любые действия внутри подпрограммы никак не отражаются на значениях переменной вне подпрограммы.
Параметры - переменные.
Передаются по ссылке. Способ используется, когда необходимо передать некоторое значение в точку вызова подпрограммы.
В случае Var - формальные параметры считаются синонимами соответствующих фактических параметров.
При этом фактические параметры должны быть переменными (не выражениями) того же типа, что и формальные параметры:
Procedure Swap (Var X,Y: Real);
Var
T: Real;
Begin
T:= X;
X:= Y;
Y:= T;
End;
Переменные файловых типов могут передаваться в подпрограмму только как параметры - переменные.
Безтиповые параметры.
Var Ident, где Ident - идентификатор формального параметра.
Фактический параметр, соответствующий формальному нетипизированному, должен представлять собой переменную любого типа (но не выражение).
Единственным способом использования таких параметров является “наделение” их определённым типом.
Существуют два способа:
- Применить операцию приведения типа.
2. Описать в подпрограмме локальную переменную определённого типа с совмещением её в памяти с нетипизированным параметром.
Пример1: функция для сравнения значений двух переменных любых типов и, соответственно, любых размеров:
Function Equal (Var Source, Dest; Size: Word): Boolean;
Type
Bytes = Array [0..MaxInt] of Byte;
Var
N: Integer;
Begin
N:= 0;
While (N < Size) and (Bytes(Dest)[N] - Bytes(Source)[N]) do
N:=N+1;
Equal:= (N = Size);
End;
{
Имеются
описания
:}
Type
Vector = Array [1..10] of Integer;
Point = Record
X,Y: Integer;
End;
Var
Vec1, Vec2: Vector;
N: Integer;
P: Pointer;
Begin
. . . . . . . . . . . . . . . . . . . . . . . . .
Equal(Vec1, Vec2, SizeOf(Vector)); {
сравниваются
все
элементы
Vec1 c Vec2}
Equal(Vec1, Vec2, SizeOf(Integer)*N); {
сравниваются
N
первых
элементов
Vec1
и
Vec2}
Equal(Vec1[1], Vec2[6], SizeOf(Integer)*5); {
сравниваются
5
первых
элементов
Vec1 c
пятью
последними
Vec2}
Equal(Vec1[1], P, 4); {
сравнивает
Vec1[1] c P.x
и
Vec2
с
P.y}
Пример2: задание различных действий в зависимости от размера переданного нетипизированногопараметра:
Procedure Size (Var X);
Var
V1: Byte absolute X;
V2: Word absolute X;
V3: LongInt absolute X;
Begin
Case SizeOf(X) of
SizeOf(Byte):
Begin
. . V1 . .
End;
SizeOf(Word):
Begin
. . V2 . .
End;
SizeOf(LongInt):
Begin
. . V3 . .
End;
End;
End;
Вычисление значения функции, завершение подпрограммы.
Смысл функции заключается в задании алгоритма вычисления некоторого значения и организации возврата этого значения в точку вызова.
В теле функции должен присутствовать оператор присваивания специального вида, в левой части которого должен быть указан идентификатор, совпадающий с именем функции, а в правой части - выражение, вычисляющее возвращаемое значение. Таких операторов может быть несколько, важно чтобы хотя бы один всегда срабатывал в процессе выполнения тела функции. Если в процессе выполнения функции не было выполнено ни одного присваивания, то результат функции считается неопределённым.
Функция может возвращать в качестве результата значения только простого, строкового или ссылочного типа.
Работа процедуры или функции по определению завершится после выполнения последнего оператора её тела. Однако Турбо-Паскаль имеет оператор Exit, возвращающий программу в точку вызова. Перед выполнением Exit в теле функции должен выполнится оператор присваивания имени функции значения.
Предварительные и внешние описания подпрограмм.
Как правило, телом процедуры является блок, но есть несколько исключений из данного правила.
1. Две подпрограммы, описанные на одном уровне, содержат взаимные вызовы друг друга.
2.
3.
Procedure A(X,Y: Real);
4.
Begin
5.
. . . . . .
6.
B (2, 2);
7.
. . . . . .
8.
End;
9.
10.
Procedure B (A,B: Integer);
11.
Begin
12.
. . . . . .
13.
A (1.5, 2.8);
14.
. . . . . .
15.
End;
Решением этой проблемы является механизм предварительных описаний. Предварительное описание содержит заголовок подпрограммы, а вместо тела записывается служебное слово Forward. В этом случае заголовок полного описания может быть записан без списка параметров и (для функций) без типа результата.
Procedure A(X,Y: Real): Forward;
Procedure B(A,B: Integer): Forward;
. . . . . . . . .
Procedure A;
Begin
. . . . . . . .
End;
Procedure B;
Begin
. . . . . . . .
End;
В случае предварительного описания подпрограммы далее в тексте должно обязательно содержаться её определяющее описание, даже если нигде в программе не встречается вызов этой подпрограммы.
16. Подпрограмма или группа подпрограмм разработана вне системы Турбо-Паскаля, на другом языке, и необходимо использовать её в данной Pascal-программе.
Внешнее описание. Объектный код подпрограммы содержится в OBJ- файле.
В Pascal-программе необходимо описать заголовок подключаемой подпрограммы, после которого должно идти слово external, и указать имя файла, содержащего подпрограмму: {$L <имя .OBJ-файла>}
17.
18.
{$L ABC.OBJ}
19.
Procedure A(C,D,E: Real); External;
20.
Procedure B(I,F,J: Integer); Extenal
Для обеспечения корректности такого подключения необходимо соблюдать определённые межъязыковые соглашения о связях принятые в Турбо-Паскале.
Специальные случаи.
1. Описание тела подпрограммы в машинных кодах - используется специальная конструкция со словом inline или серия ассемблерных инструкций с использованием слова asm.
Следующие коды эквивалентны:
2.
3.
asm
4.
mov cx,100
5.
metka mov i,cx
6.
. . . . . . . .
7.
loop metka
8.
end;
9.
10.
И:
11.
12.
for i:=100 downto 0 do
13.
. . . . . . . . . .
Однако, код, написанный между asm и end выполнится быстрее, нежели всего одна строка, заменяющая его с использованием оператора цикла паскаля.
14. В описании процедуры перед блоком может указываться специальное слово interrupt, которое служит для определения процедур прерываний.
15. Допускается указание “типа вызова” процедуры (Far, Near). Для функций не допускается interrupt, тоесть она не может быть обработчиком прерывания.
Рекурсия и побочный эффект.
В теле подпрограммы доступны все объекты, описанные в объемлющем блоке, в том числе и имя самой подпрограммы. Подпрограммы, вызывающие сами себя, называются рекурсивными.
Пример: алгоритм вычисления факториала: N! = (N-1)!*N
Fuction Fact(N: Word): LongInt;
Begin
If N = 1 then
Fact:= 1
else
Fact:= N * Fact(N-1);
End;
В Турбо-Паскале нет никаких ограничений на рекурсивные вызовы подпрограмм, необходимо только помнить, что каждый очередной рекурсивный вызов приводит к образованию новой копии локальных объектов подпрограммы и все эти копии, соответствующие цепочке активизированных и незавершенных рекурсивных вызовов существуют независимо друг от друга.
Может возникнуть ситуация, при которой значение выражения, использующего вызов такой функции, зависит от порядка следования операндов, что является источником трудноуловимых программных ошибок - побочный эффект. Например:
Program SideEffect;
Var
A,Z: Integer;
F: Text;
Function Change(X: Integer): Integer;
Begin
Z:= Z - X; { изменяем значение локальной переменной }
Change:= Sqr (X);
End;
Begin
Assign(F, ‘Prn’);
Rewrite (F);
Z:= 10;
A:= Change (Z);
WriteLn (F, A,’ ’,Z);
Z:= 10;
A:= Change (10) * Change (Z);
WriteLn (F,A,’ ‘,Z);
Z:= 10;
A:= Change (Z) * Change (10);
WriteLn (F,A,’ ‘,Z);
End;
Результаты:
100 0
10000 -10
0 0
Распределение памяти под данные.
Память под глобальные и типизированные константы выделяется при запуске программы в одном сегменте (65520 байт - сегмент данных программы).
Локальные переменные динамически размещаются в том же сегменте при активизации подпрограммы.
При рекурсии или просто вызове подпрограммы используется стек. Через стек передаются параметры подпрограмм, в стеке сохраняются переменные, вышедшие за область видимости при передаче управления в подпрограмму. Причем дописывание информации в стек происходит при каждом рекуррентном вызове , поэтому нужно следить за глубиной рекурсии, иначе слишком длинная, или бесконечная рекурсия забьет все стековое пространство и программа не сможет выполняться дальше, произойдет ошибка времени исполнения. Максимальный объём стековой памяти в Турбо-Паскале - 1 сегмент (65520 байт). Можно устанавливать размер стека и контроль за переполнением стека - $M и $S (тоесть, компилятор может включить в программу код, следящий за переполнением стека и выдающий вовремя сообщение о переполнении стека, вместо зависания программы, обычно этот ключ установлен по умолчанию). Но не следует всегда выставлять непременно максимальный обьем стека, так как ето приводит к увеличению размера памяти, занимаемой программой при исполнении.
Процедурные типы.
Турбо-Паскаль позволяет вводить переменные специального типа, значениями которых могут служить подпрограммы. Можно интерпретировать подпрограмму как значения, которые можно присваивать переменным и передавать их в качестве параметров (речь идёт о подпрограммах как о целостных объектах, а не о значениях, возникающих в процессе их выполнения).
{$F+}
Var
P: Procedure;
Значениями Р могут быть любые процедуры без параметров. В более общем случае:
Type
Func = Function(X,Y: Integer): Integer;
Var
F1,F2: Func;
Например, если есть функция:
Function Add(A,B: Integer): Integer;
Begin
Add:= A + B;
End;
то допустимо F1:= Add, в этом случае переменной F1 присваивается функция Add как таковая, но её исполнения не происходит.Теперь можно:
Write (Add (1,2)) или Write (F1 (1,2));.
Следует обратить внимание на строку {$F+} – она существенна, это ключ компилятора, и если он выключен, при присвоении переменным процедурного типа значений конкретных подпрограмм, возникнет ошибка присвоения типа.
Имена формальных параметров, указываемые в процедурных типах, играют часто иллюстративную роль и на смысл определений никакого влияния не оказывают. Необходимыми являются только идентификаторы типов параметров и результатов (для функций).
Процедурные типы допускают также присвоение вида F1:= F2;
Такие переменные можно использовать для вызова подпрограмм, которые присвоены этим переменным.
Пример:
Var
Oper: Function(X, Y: Real): Real;
Function Add(A,B: Real): Real;
Begin
Add:= A + B;
End;
Function Sub (A,B: Real): Real;
Begin
Sub:= A - B;
End;
тогда в основной программе можно использовать:
If <
условие
> then
Oper:= Add
else
Oper:= Sub;
Write (Oper(2.5, X+Y));
Процедурные типы, чтоб они были совместимыми по присваиванию, должны иметь одинаковое количество формальных параметров, а параметры на соответствующих позициях должны быть одного типа. Также, должны совпадать типы возвращаемых значений у функций.
Использование процедурных типов не ограничиваются простыми процедурными переменными. Как и любой другой тип, процедурные типы могут участвовать в построении структурированных типов:
Type
Proc = Procedure(T: Real);
Notice = Record
Next: Integer;
Time: Real;
Action: Proc;
End;
Var
New_Notices: Array[1..10] of Proc;
Notices: Notice;
Правила корректной работы с процедурными типами.
1. Подпрограмма, присваиваемая процедурной переменной должна быть странслирована в режиме “дальнего вызова”.
2. Подпрограмма, присваиваемая процедурной переменной, не должна быть стандартной процедурой или функцией. Это ограничение легко обойти:
3.
4.
Var
5.
Func: Function(R: Real): Real;
6.
. . . . . . . . . . . . . . . .
7.
Function MyExp(R: Real): Real
8.
Begin
9.
MyExp:= Exp (R);
10.
End;
11.
. . . . . . . . . . . . . . . .
12.
Begin
13.
Func:= MyExp;
14. Подпрограмма, присваиваемая процедурной переменной, не может быть вложенной в другие подпрограммы.
15. Подпрограмма, присваиваемая процедурной переменной, не может быть подпрограммой специального вида (interrupt или inline).
Процедурная переменная занимает в памяти 4 байта (2 слова). В первом хранится смещение, во втором - сегмент (т.е. указатель на код подпрограммы).
Можно описывать процедуры и функции, параметрами которых являются процедурные переменные.
Таким образом, можно организовать подпрограмму, которая будет выполнять некоторое общее действие для различных подпрограмм - периметров.
В общем случае использование процедурных переменных в операторе или выражении означает вызов присвоенной данной переменной процедуры или функции.
Необходимо помнить, что если в левой части оператора стоит процедурная переменная, правая часть его должна представлять идентификатор другой процедурной переменной или идентификатор подпрограммы.
К сожалению есть неопределённость для компилятора, например в таком случае:
Type
Func = Function: Real;
Var
F: Func;
Function FFF: Real;
Begin
FFF:= 1.25;
End;
Function FF: Real;
Begin
FF:= 2.10;
End;
. . . . . . . . . .
F:= FF;
If F = FFF then . . . .
В подобных случаях неочевидно, должен ли компилятор сравнивать значение процедуры F с FFF или нужно вызвать процедуру F и FFF и сравнить их значения.
Принято, что такое вхождение идентификатора подпрограммы означает вызов функции.
Чтобы сравнить значение переменной F со значением (адресом) подпрограммы FFF нужно использовать следующую конструкцию:
If @F = @FFF then . . . .
Чтобы получить адрес самой процедурной переменной нужно написать:
@@F
Приведение типов переменных для процедурных типов.
Определены следующие типы и переменные:
Type
Func:= Function(X: Integer): Integer;
Function MyFunc(X: Integer): Integer;
Begin
MyFunc:= X;
End;
Var
F: Func;
P: Pointer;
N: Integer;
Можно построить следующие присваивания:
F:= MyFunc {переменной F присваивается функция MyFunc}
N:= F(N) {функция MyFunc вызывается через переменную F}
P:= @F {P получает указатель на функцию MyFunc}
N:= Func (P)(N) {функция MyFunc вызывается через указатель P}
F:= Func (P) {присвоить значение подпрограммы в P переменной F}
Func(P):= F {присвоить значение подпрограммы в F указателю P}
@F:= P {присвоить значение указателя в P переменной F}
Динамическая память.
Все глобальные переменные и типизированные константы размещаются в одной непрерывной области оперативной памяти, которая называется сегментом данных. Длина сегмента определяется архитектурой процессора 8086 и составляет 64 Килобайта (65536 байт), что может вызвать определённые трудности при описании и обработке больших массивов данных.
С другой стороны объём стандартной памяти - 640 Килобайт. Выход - использовать динамическую память.
Динамическая память - это оперативная память ЭВМ, предоставляемая Турбо-Паскалевой программе при её работе, за вычетом сегмента данных (64 К), стека (обычно 16 К) и собственно тела программы.
По умолчанию размер динамической памяти определяется всей доступной памятью ЭВМ и, как правило, составляет не менее 200 - 300 Кбайт.
Динамическую память обычно используют при:
- обработке больших массивов данных;
- разработке САПР;
- временном запоминании данных при работе с графическими и звуковыми средствами ЭВМ.
Размещение статических переменных в памяти осуществляется компилятором в процессе компиляции.
Динамические переменные - размещаются в памяти непосредственно в процессе работы программы.
При динамическом размещении заранее неизвестны ни тип, ни количество размещаемых данных, к ним нельзя обращаться по именам, как к статическим переменным. Турбо-Паскаль представляет средство управления динамической памятью: указатели.
Указатель - это переменная, которая в хранит качестве своего значения адрес байта памяти.
В 8086 адреса задаются совокупностью двух шестнадцатиразрядных слов - сегмента и смещения. Сегмент - участок памяти, имеющий максимальную длину 64 К и начинающийся к физического адреса, кратного 16 (то есть 0, 16, 32, 48 и т.д.). Смещение - указывает, сколько байт от начала сегмента нужно пропустить, чтобы обратиться по нужному адресу. Фрагмент памяти в 16 байт называется параграфом. Сегмент адресует память с точностью до параграфа, а смещение - с точностью до байта.
Каждому сегменту соответствует непрерывная и отдельно адресуемая область памяти. Сегменты могут следовать в памяти один за другим, или с некоторыми интервалами, или, наконец, перекрывать друг друга.
Таким образом любой указатель по своей внутренней структуре представляет собой совокупность двух слов (типа Word), трактуемых как сегмент и смещение. Указатель адресует лишь первый байт типа данных.
Справка: процессор 8086.
8086 имеет 16 - битовые регистры, всего 14 штук, из них:
- 12 - регистры данных и адресов;
- 1 - указатель команд (регистр адреса команды);
- 1 - регистр состояния (регистр флагов).
Регистры данных и адресов делятся на три группы:
- регистр данных;
- регистр указателей и индексов;
- регистр сегментов.
Процессор 8086 всегда генерирует 20-ти бытовые адреса за счёт добавления 16-ти битового смещения к содержимому регистра, умноженному на 16:
физический адрес = смещение + 16 * регистр сегмента.
Вместо умножения на 16 содержимое регистра сегмента используется так, как если бы оно имело четыре дополнительных нулевых бита:
пусть:
смещение = 10H
регистр сегмента = 2000H
тогда:
0000 0000 0001 0000 (смещение)
0010 0000 0000 0000 (0000) (номер блока)
0010 0000 0000 0001 0000 (физический адрес)
физический адрес = 20010H
У 8086 адрес ячейки задаётся номером блока и смещением, что обусловлено тем, что команды 8086 и её данные должны располагаться в разных частях памяти (в разных сегментах).
Если требуется адресоваться к данным, то потребуется адресация блока памяти, с которого начинается сегмент данных (из регистра сегмента данных) и позиция желаемой ячейки в этом сегменте (смещение).
В дополнение к области памяти 1 Мбайт, 8086 может адресоваться к внешним устройствам через 65536 (64 К) портов ввода-вывода. Имеются специальные команды ввода-вывода, позволяющие иметь непосредственный доступ к первым 256 (от 0 до 255) портам. Другие команды позволяют получить косвенный доступ к порту с помощью занесения идентифицирующего его номера (0 - 65535) в определенный регистр данных. Подобно ячейке памяти любой порт может быть 8 или 16- битовым.
Распределение памяти IBM PC.
- 16 - старших байт - команды начальной загрузки системы.
- Первые 1024 ячейки - вектора прерываний.
Типы прерываний.
Существуют 2 вида прерываний - одни можно игнорировать, другие обязательно обслужить как можно скорее.
8086 может распознать 256 различных прерываний, каждому из них однозначно соответствует код типа (целое число от 0 до 255). Код используется в качестве указателя ячейки в области памяти с младшими адресами (область векторов прерываний).
Объявление указателей.
Как правило, в Турбо-Паскале указатель связывается с некоторым типом данных (типизированные указатели).
Type
PerPnt = ^PerRec; {указатель на переменную типа PerRec}
PerRec = Record
Name: String;
Job: String;
Next: PerPnt;
End;
Var
P1: ^Integer;
P2: ^Real;
Внимание! При объявлении типа PerPnt мы сослались на PerRec, который ещё не был объявлен, это связано с тем, что существует исключение из правил для указателей, которые содержат ссылку на ещё неописанный тип данных. Это исключение сделано не случайно, так как динамическая память позволяет использовать организацию данных в виде списков (каждый элемент имеет в своём составе ссылку на соседний элемент - обратите внимание на поле Next).
В Турбо-Паскале можно объявлять указатель и не связывать его с конкретным типом данных:
Var
PP: Pointer;
Указатели такого рода называют нетипизированными. В них удобно размещать данные, структура которых меняется по ходу работы.
В Турбо-Паскале можно передавать значения только между указателями, связанными с одним и тем же типом данных.
Var
P1,P2: ^Integer;
P3: ^Real;
PP:Pointer;
Begin
P1:= P2; {-
верно
}
P1:= P3; {- неверно}
но можно сделать так:
PP:= P3;
P1:= PP;так как ограничение не распространяются на нетипизированные указатели. Но подобные операции часто путают программиста и чреваты смысловыми ошибками.
Выделение и освобождение динамической памяти.
Вся динамическая память – пространство ячеек, называемое кучей. Физически куча располагается в старших адресах, сразу за программой. Указатель на начало кучи храниться в предопределенной переменной HeapOrg, конец - FreePtr, текущую границу незанятой динамической памяти указывает указатель HeapPtr.
Для выделения памяти под любую переменную используется процедура New. Единственным параметром является типизированный указатель:
Var
I,J: ^Integer;
R: ^Real;
Begin
New(I); {под I выделяется область памяти,}
{адрес первого байта этой области помещается в I}
End.
После выполнения этого фрагмента указатель I приобретёт значение, которое перед этим имел указатель кучи HeapPtr, а HeapPtr увеличится на два (т.к. он типа Integer); New(R) - вызовет смещение указателя на 6 байт.
После того как указатель приобрёл некоторое значение, то есть стал указывать на конкретный байт памяти, по этому адресу можно разместить значения соответствующего типа:
I^:= 2;
R^:= 2*Pi; Допустима запись: R^:= Sqr (R^) + I^ - 17;
Но недопустима запись: R:= Sqr (R^) + I^ - 17; так как указателю R нельзя присвоить значение вещественного выражения.
Возврат динамической памяти обратно в кучу осуществляется оператором Dispose:
Dispose(R);
Dispose(I); - вернут в кучу, ранее забранные 8 байт.
Dispose(Ptr) не изменяет значения указателя Ptr, а лишь возвращает в кучу память, связанную с этим указателем. Однако повторное применение процедуры к “свободному” указателю приведет к возникновению ошибки времени исполнения. Чтобы указать, что указатель свободен, нужно использовать зарезервированное слово Nil.
К указателям можно применять операции отношения, в том числе и сравнения с Nil:
Const
P:^Real = Nil;
. . . . . . . .
Begin
If P = Nil then
New (P);
. . . . . . . .
Dispose(P);
P:= Nil;
End.
Использование указателей, которым не присвоено значение процедурой New или каким-либо другим способом, никак не контролируется системой и может привести к непредсказуемым результатам. Многократное чередование New и Dispose приводит к “ячеистой” структуре кучи. Дело в том, что все операции с кучей выполняется под управлением особой программы, которая ведёт учёт всех свободных фрагментов в куче. При выполнении оператора New( ) эта программа отыскивает минимальный свободный фрагмент, в котором может разместиться требуемая переменная. Адрес начала найденного фрагмента возвращается в указателе, а сам фрагмент или его часть нужной длины, помечаются как занятая часть кучи.
Можно освободить целый фрагмент кучи следующим образом:
- Перед началом выделения динамической памяти значения указателя HeapPtr запоминается в переменной-указателе с помощью процедуры Mark.
- Выполнение программы.
- Освобождение фрагмента кучи от заполненного адреса до конца динамической памяти с использованием процедуры Release.
4.
5.
Var
6.
P, P1, P2, P3, P4, P5: ^Integer;
7.
Begin
8.
New(P1);
9.
New(P2);
10.
New(P3);
11.
New(P4);
12.
New(P5);
13.
Mark(P);
14.
. . . . . . .
15.
Release (P);
16.
End.
В этом примере процедурой Mark(P) в указатель P было помещено текущее значение HeapPtr, однако память под переменную не резервировалась.
Вызов Release уничтожает список свободных фрагментов в куче, созданных Dispose, поэтому совместное применение этих процедур не рекомендуется.
Для работы с нетипизированными указателями используются также процедуры GetMem(P, Size) и FreeMem(P, Size) - резервирование и освобождение памяти.
P - нетипизированный указатель, Size - размер.
За одно обращение к куче процедурой GetMem можно зарезервировать до 65521 байт. Освобождать нужно ровно столько памяти, сколько было зарезервировано, и именно с того адреса, с которого память была зарезервирована, иначе программа не будет работать и завершаться корректно !!!
Использование нетипизированных указателей даёт широкие возможности неявного преобразования типов:
Var
i,j: ^Integer;
r: ^Real;
Begin
New (i); { I:= HeapOrg; HeapPtr:= HeapOrg+2 }
j:= i; { J:= HeapOrg }
j^:=2;
Dispose (i); { HeapPtr:= HeapOrg }
New (r); { R:= HeapOrg; HeapPtr:= HeapOrg+6 }
r^:= Pi;
WriteLn (j^);
End.
Будет выведено: "8578" {здесь преобразование не имеет никакого смысла}
Примеры использования указателей.
Динамическая память - 200..300 Кбайт. Нужно разместить массив 100 * 200 типа Extended. Требуется 100 * 200 * 10 = 200000 байт. Пробуем:
Var
i,j: Integer;
PtrArr: Array[1..100, 1..200] of ^Extended.
Begin
. . . . . .
For i:= 1 to 100 do
For j:= 1 to 200 do
New (PtrArr [i,j]);
. . . . . .
End.
Теперь к любому элементу можно обратиться: PtrArr[i,j]^:=...; Но длина внутреннего представления указателей 4 байта, поэтому потребуется ещё 100*200*4 = 80000 байт, что превышает размер сегмента (65536 байт), доступный для статического размещения данных.
Можно было бы работать с адресной арифметикой (арифметикой над указателями), то есть не создавать массив указателей, а вычислять адрес элемента непосредственно перед обращением к нему. Однако в Турбо-Паскале над указателями не определены никакие операции, кроме присваивания и отношения. Но задачу решить можно:
Seg(x) - возвращает сегментную часть адреса.
Ofs(x) - возвращает смещение.
x - любая переменная, в том числе и та, на которую указывает указатель.
Далее с помощью Ptr(Seg, Ofs: Word): Pointer можно создать значение указателя, совместимое с указателем любого типа.
Таким образом, сначала с помощью GetMem забираем из кучи несколько фрагментов подходящей длины (не более 65521). Удобно по строкам 200 * 100 = 20000 байт.
Начало каждого фрагмента запоминается в массиве PtrStr из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать указатель.
Var
i,j: Integer;
PtrStr: Array [1..100] of Pointer;
Pr: ^Extended;
Const
SizeOfExt = 10;
Begin
For i:= 1 to 100 do
GetMem (PtrStr[i], SizeOfExt*200);
. . . . . . . . . . . . . . . .
Pr:= Ptr(Seg(PtrStr[i]^), Ofs(PtrStr[i]^) + (j - 1) * SizeOfExt); {
Обращение
к
элементу
матрицы
[i,j]}
End;
далее работаем с Pr^:=. . . и т.д.
Полезно ввести две вспомогательных функции GetExt и PutExt. Каждая из них будет обращаться к функции вычисления адреса AddrE.
Program Demo;
Const
SizeOfExt = 10;
N = 100;
M = 200;
Type
ExtPoint = ^Extended;
Var
i,j: Integer;
PtrStr: Array[1..N] of Pointer;
S: Extended;
Function AddrE(i,j: Word): ExtPoint;
Begin
AddrE:= Ptr(Seg(PtrStr[i]^), Ofs(PtrStr[i]^) + (j - 1) * SizeOfExt);
End;
Function GetExt(i,j: Integer): Extended;
Begin
GetExt:= AddrE(i,j)^;
End;
Procedure PutExt(i,j: Integer; X: Extended);
Begin
AddrE(i,j)^:= X;
End;
Begin {main}
Randomize;
For i:=1 to N do
Begin
GetMem (PtrStr[i], M*SizeOfExt);
For i:= 1 to m do
PutExt(i, j, Random(255));
End;
S:= 0;
For i:= 1 to N do
For j:= 1 to M do
S:= S + GetExt(i,j);
WriteLn(S/(N*M));
End.
Мы предполагали, что каждая строка размещается в куче с начала границы параграфа и смещение каждого указателя PtrStr ровно 0. В действительности при последовательных обращениях к GetMem начало очередного фрагмента следует за концом предыдущего и может не попасть на границу сегмента. В результате при размещении фрагментов максимальной длины (65521 байт) может возникнуть переполнение при вычислении смещения последнего байта.
Процедуры и функции для работы с динамической памятью.
- Функция Addr - возвращает результат типа Pointer, в котором содержится адрес аргумента.
- Addr(X), x - любой объект программы. Возвращаемый адрес совместим с указателем любого типа. Аналогично операции @.
- Функция CSeg - возвращает значения хранящееся в регистре CS (в начале работы программы в CS содержится сегмент начала кода программы), результат CSeg - слово типа Word.
- Процедура Dispose(x) - возвращает в кучу фрагмент динамической памяти, зарезервированный за типизированным указателем x.
- Функция DSeg - возвращает значение хранящиеся в регистре DS (в начале работы в DS - сегмент начала данных программы), результат - типа Word.
- Процедура FreeMem - возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за нетипизированным указателем. FreeMem(P, Size), P - нетипизированный указатель. Size - длина фрагмента, подлежащего освобождению.
- Процедура GetMem(P, Size) - резервирует память (за одно обращение не более 65521 байт), если нет свободной памяти - ошибка времени исполнения.
- Процедура Mark(Ptr) запоминает текущее значение указателя кучи HeapPtr. Ptr - указатель любого типа, в нём будет возвращено HeapPtr. Используется совместно с Release для освобождения части кучи.
- Функция MaxAvail - возвращает размер (в байтах) наибольшего непрерывного свободного участка кучи. Результат типа LongInt. За один вызов New или GetMem нельзя зарезервировать значение большее, чем возвращаемое этой функцией.
- Процедура New(TP) - резервирует фрагмент кучи для размещения переменной. TP - типизированный указатель (за одно обращение не более 65521байт).
- Функция MemAvail - возвращает размер (в байтах) общего свободного пространства кучи. Результат типа Longint.
- Функция Ofs(X) - возвращает значение типа Word, содержащее смещение адреса указанного объекта. X - выражение любого типа или имя процедуры.
- Функция Ptr(Seg, Ofs) - возвращает значение типа Pointer по заданному сегменту и смещению. Значение, возвращаемое функцией, совместимо с указателем любого типа.
- Процедура Release(Ptr) - освобождает участок кучи. Рtr - указатель любого типа, в котором сохранено процедурой Mark значение указателя кучи. Освобожденный участок кучи - от адреса в Ptr до конца. Одновременно уничтожается список свободных фрагментов, созданных по Dispose и FreeMem.
- Функция Seg(X) - возвращает значение типа Word, содержащее сегмент адреса указанного объекта.
- Функция SizeOf(X) - возвращает длину (в байтах) внутреннего представления указанного объекта. X - имя переменной, функции или типа.
Проблема потерянных ссылок
Работа с динамическими переменными через указатели требует большой тщательности и аккуратности при проектировании программ. В частности, следует стремиться освобождать выделенные области сразу же после того, как необходимость в них отпадает, иначе “засорение” памяти ненужными динамическими переменными может привести к быстрому ее исчерпанию.
Кроме того, необходимо учитывать еще одну проблему, связанную с противоречием между стековым принципом размещения статических переменных и произвольным характером создания и уничтожения динамических переменных. Рассмотрим следующий схематический пример программы:
Program LostReference;
Type
PPerson = ^Person;
Person = Record
. . . .
End;
Procedure GetPerson;
Var
Р
:
РР
erson;
Begin
P:= New(PPerson);
End;
Begin
WriteLn(MemAvail);
GetPerson;
Writeln(MemAvail);
End.
Вызов New в процедуре GetPerson приводит к отведению памяти для динамической переменной типа Person. Указатель на эту переменную присваивается переменной Р. Рассмотрим ситуацию, возникающую после выхода из процедуры GetPerson. По правилам блочности все локальные переменные подпрограммы перестают существовать после ее завершения. В нашем случае исчезает локальная переменная Р. Но, с другой стороны, область памяти, отведенная в процессе работы GetPerson, продолжает существовать, так как освободить ее можно только явно, посредством процедуры Dispose. Таким образом, после выхода из GetPerson отсутствует какой бы то ни было доступ к динамической переменной, так как единственная "ниточка", связывавшая ее с программой - указатель Р - оказался потерянным при завершении GetPerson. Вывод на печать общего объема свободной памяти до и после работы GetPerson подтверждает потерю определенной области.
При проектировании программ, интенсивно использующих динамическую память, следует с особой внимательностью относиться к данной проблеме, так как Turbo Pascal, как, впрочем, и многие другие языки программирования, не имеет встроенных средств борьбы с засорением памяти неиспользуемыми динамическими переменными. Во всяком случае нужно придерживаться правила, согласно которому при выходе из блока необходимо или освободить все созданные в нем динамические переменные, или сохранить каким-то образом ссылки на них (например, присвоив эти ссылки глобальным переменным).
К описанной проблеме примыкает коллизия другого рода, заключающаяся в ситуации, когда некоторая область памяти освобождена, а в программе остался указатель на эту область. Рассмотрим следующий пример:
Var
P: Integer;
Procedure X1;
Var
i: Integer;
Begin
i:= 12345;
P:= @i;
WriteLn(P^); {
напечатает
12345 }
End;
Procedure X2;
Var
j: Integer;
Begin
j:= 7777;
WriteLn(P^); { напечатает 7777, а не 12345 }
End;
Begin
X1;
X2;
End;
В этом примере глобальная ссылочная переменная Р первоначально (в процедуре X1) устанавливается на локальную переменную i. После завершения процедуры X2 переменная i исчезает, указатель Р “повисает”. Вызов процедуры Х2 приводит к тому, что на место, локальной переменной i, будет помещена локальная переменная j, и указатель Р теперь ссылается на нее, что подтверждает результат второго вызова WriteLn.
Таким образом, смысл указателя Р зависит в данном случае не от семантики решаемой задачи, a от системных особенностей ее функционирования, что может привести к неожиданным эффектам. Аналогичные ситуации возможны при повторном использовании областей динамической памяти: если на освобожденную область остался указатель, то он может быть (по ошибке) использован после повторного выделения этой памяти для другой переменной, что опять- таки не будет "замечено" системой, но может сделать поведение программы непредсказуемым.
Модули
Стандартный Паскаль не предусматривает никаких механизмов раздельной компиляции частей программы с последующей их сборкой перед выполнением. Более того, последовательное проведение в жизнь принципа обязательного описания любого объекта перед его ис пользованием делает фактически невозможным разработку разнообразных библиотек прикладных программ. Точнее, такие библиотеки в рамках стандартного Паскаля могут существовать только в виде исходных текстов и программист должен сам включать в программу подчас весьма обширные тексты различных поддерживающих процедур, таких, как, например, процедуры матричной алгебры, численного интегрирования, математической статистики и т.п.
Вполне понятно, поэтому стремление разработчиков коммерческих компиляторов Паскаля включать в язык средства, повышающие его модульность. Чаще всего таким средством является разрешение использовать внешние процедуры и функции, тела которых заменяются зарезервированным словом External.
Разработчики Турбо-Паскаля пошли в этом направлении еще дальше, включив в язык механизм так называемых модулей. Модуль - это автономно компилируемая программная единица, включающая в себя различные компоненты раздела описаний (типы, константы, пе ременные, процедуры и функции) и, возможно, некоторые исполняемые операторы инициирующей части. По своей организации и характеру использования в программе модули Турбо-Паскаля близки к модулям- пакетам (Package) языка программирования Ада: в них также явным образом выделяется некоторая "видимая" интерфейсная часть, в которой сконцентрированы описания глобальных типов, констант и переменных, а кроме того, приводятся заголовки глобальных процедур и функций. Появление объектов в интерфейсной части делает их доступными для других модулей и основной программы. Тела процедур и функций располагаются в исполняемой части модуля, которая может быть скрыта от пользователя.
Насколько сильно изменяются свойства языка Паскаль при введении механизма модулей, свидетельствует следующее замечание его автора Н.Вирта, сделанное им по поводу более позднего языка Модула-2: "Модули - самая важная черта, отличающая язык Модула-2 от его предшественника Паскаля". Кстати, из пяти основных отличий Модулы- 2 от Паскаля, сформулированных Н. Виртом, три: модули, средства программирования низшего уровня и процедурные типы - реализованы в Турбо-Паскале.
Модули представляют собой прекрасный инструмент для разработки библиотек прикладных программ и мощное средство модульного программирования. Важной особенностью модулей является то обстоятельство, что компилятор Турбо-Паскаля размещает их про граммный код в отдельном сегменте памяти. Максимальная длина сегмента не может превышать 64 Кбайт, однако количество одновременно используемых модулей ограничивается лишь доступной памятью, что дает возможность создавать весьма крупные программы.
Структура модулей.
Модуль имеет следующую структуру:
Unit < имя >;
Interface
< интерфейсная часть >
Implementation
< исполняемая часть >
[ Begin < инициирующая часть > ]
End.
Здесь:
Unit - кодовое слово (англ. модуль); начинающее заголовок модуля;
<имя> - имя модуля (правильный идентификатор);
Interface - кодовое слово, начинающее интерфейсную часть модуля;
Implementation - кодовое слово (англ. реализация); начинает исполняемую часть;
Begin - кодовое слово, начинающее инициирующую часть;
(часть модуля Begin < инициирующая часть > необязательна);
End. - признак конца модуля.
Таким образом, модуль состоит из заголовка и трех составных частей, любая из которых может быть пустой.
Заголовок модуля и связь модулей друг с другом
Заголовок модуля состоит из кодового слова Unit и следующего за ним имени модуля. Для правильной работы среды Турбо-Паскаля и возможности подключения средств, облегчающих разработку крупных программ, это имя должно совпадать, с именем дискового файла, в который помещается исходный текст модуля.
Если, например, имеем заголовок Unit Global, то исходный текст соответствующего модуля должен размещаться в дисковом файле GLOBAL.PAS. Имя модуля служит для его связи с другими модулями и основной программой. Эта связь устанавливается специальным предложением
Uses <список модулей>
Здесь:
Uses - кодовое слово (англ. использует);
<список модулей> - список модулей, с которыми устанавливается связь; элементами списка являются имена модулей, отделяемые друг от друга запятыми, например:
Uses Crt, Graph, Global;
Если предложение Uses... используется, оно должно открывать раздел описаний основной программы или следовать сразу за кодовым словом Interface в модуле.
Интерфейсная часть.
Интерфейсная часть открывается кодовым словом Interface. В этой части содержатся объявления всех глобальных объектов модуля (типов, констант, переменных и подпрограмм), которые должны стать доступными основной программе и/или другим модулям.
При объявлении глобальных блоков в интерфейсной части пишется только их заголовок, например:
Unit Cmplx;
Interface
Type
Complex = Record
Re: Real;
Im: Real;
End;
Procedure AddC (X,
У
: Complex; Var R: Complex);
Procedure MulC (X,
У
: Complex; Var Z : Complex);
Если теперь в основной программе написать предложение Uses Cmplx; то в программе станут доступными тип Complex и две процедуры - Addc и Mulc из модуля Cmplx.
Следует учесть, что все константы и переменные, объявленные в интерфейсной части модуля, равно как и глобальные объекты основной программы, помещаются компилятором Турбо-Паскаля в обилии сегмент данных (максимальная длина сегмента 65521 байт).
Порядок появления различных разделов объявлений и их количество могут быть произвольными.
Если в интерфейсной части объявляются внешние блоки или блоки в машинных кодах, их тела (т.е. зарезервированное слово External в первом случае и машинные коды вместе со словом Inline во втором) должны следовать сразу за их заголовками.
В интерфейсной части модулей нельзя использовать опережающее описание.
Исполняемая часть
Исполняемая часть начинается кодовым словом Implementation и содержит тела процедур и функций, объявленных в интерфейсной части. В этой части могут также объявляться локальные для модуля объекты: вспомогательные типы, константы, переменные и блоки, а также метки, если они используются в инициирующей части.
Ранее объявленные в интерфейсной части глобальные процедуры и функции должны описываться в той же последовательности, в какой появляются их заголовки в интерфейсной части. Описанию глобального блока в исполняемой части должен предшествовать заголовок, в котором разрешается опускать список формальных переменных (и тип результата для функции), так как они уже описаны в интерфейсной части.
Но если заголовок блока приводится в полном виде, т.е. со списком формальных параметров и объявлением результата, он должен совпадать с заголовком, объявленным в интерфейсной части, например:
Unit Cmplx;
Interface
Type
Complex = Record
Re: Real;
Im: Real;
End;
Procedure AddC (X,
У
: Complex; Var Z : Complex);
Implementation
Procedure AddC;
Begin
Z.Re:=
Х
.R
е
+ Y.Re;
Z.Im:= X.Im + Y.Im;
End;
End.
Локальные переменные и константы, а также все программные коды, порожденные при компиляции модуля, помещаются в общий сегмент памяти.
Инициирующая часть
Инициирующая часть завершает модуль. Она может отсутствовать вместе с начинающим ее словом Begin или быть пустой - тогда за Begin сразу следует признак конца модуля (кодовое слово End и следующая за ним точка). В инициирующей части помещаются ис полняемые операторы, содержащие некоторый фрагмент программы. Эти операторы исполняются до передачи управления основной программе и обычно используются для подготовки ее работы. Например, в них могут инициироваться переменные, открываться нужные файлы, устанавливаться связь с другими ПЭВМ по коммуникационным каналам и т.п.:
Unit FileText;
Interface
Procedure Print(S: String);
Implementation
Var
F: Text;
Const Name = 'output.txt’;
Procedure Print;
Begin
WriteLn(F, S);
End;
{
Начало
инициирующей
части
}
Begin
Assign(F, Name);
Rewrite(F);
{ Конец инициирующей части }
End.
Компиляция модулей
В среде Турбо-Паскаля имеются средства, управляющие способами компиляции модулей и облегчающие разработку крупных программных проектов. В частности, определены три режима компиляции: Compile, Make и Build, Режимы отличаются только способом связи компилируемого модуля или компилируемой основной программы с другими модулями, объявленными в предложении Uses.
При компиляции модуля или основной программы в режиме Compile все упоминающиеся в предложении Uses модули должны быть предварительно откомпилированы и результаты их компиляции должны быть помещены в одноименные файлы с расширением .TPU. Например, если в программе (в модуле) имеется предложение Uses Global, то на диске в каталоге, объявленном опцией Unit directories, уже должен находиться файл GLOBAL.TPU. Файл с расширением .TPU (от англ. Turbo-Pascal Unit) создается в результате компиляции модуля.
В режиме MAKE компилятор проверяет наличие TPU-файлов для каждого объявленного модуля. Если какой-либо из файлов не обнаружен, система пытается отыскать одноименный файл с расширением .PAS, т.е. файл с исходным текстом модуля, и, если PAS- файл найден, приступает к его компиляции. Кроме того, в этом случае система следит за возможными изменениями исходного текста любого используемого модуля. Если внесены какие-либо изменения в PAS-файл (исходный текст модуля), то независимо от того, есть ли уже в каталоге соответствующий TPU-файл или нет, система осуществляет его компиляцию перед компиляцией основной программы.
Более того, если изменения внесены в интерфейсную часгь модуля, то будут перекомпилированы также и все другие модули, обращающиеся к нему. Режим Make, таким образом, существенно облегчает процесс разработки крупных программ с множеством модулей: программист избавляется от необходимости следить за соответствием существующих TPU-файлов и их исходного текста, так как система делает это автоматически.
В режиме Build существующие TPU-файлы игнорируются, и система пытается отыскать (и компилировать) соответствующий PAS-файл для каждого объявленного в Uses модуля. После компиляции в режиме Build программист может быть уверен в том, что учтены все сделанные им изменения в любом из модулей.
Подключение модулей к основной программе и их возможная компиляция осуществляется в порядке их объявления в предложении Uses. При переходе к очередному модулю система предварительно отыскивает все модули, на которые он ссылается. Ссылки модулей друг на друга могут образовывать древовидную структуру любой сложности, однако запрещается явное или косвенное обращение модуля к самому себе.
Доступ к объявленным в модуле объектам
Пусть, например, создан модуль, реализующий арифметику комплексных чисел (напомню, что такая арифметика ни в стандартном Паскале, ни в Турбо-Паскале не предусмотрена). К сожалению, в Турбо-Паскале нельзя использовать функции, значения которых имели бы структурированный тип (запись, например), поэтому арифметика комплексных чисел реализуется четырьмя приведенными ниже процедурами.
Unit Cmplx;
Interface
Type
Complex = Record
Re: Real;
Im: Real;
End;
Procedure AddC (X,
У
: Complex; Var Z ; Complex);
Procedure SubC (X,
У
: Complex; Var Z: Complex);
Procedure MulC (X, Y.: Complex; Var Z: Complex);
Procedure DivC (X,
У
: Complex; Var Z: Complex);
Const
С
: Complex = (Re: 0.1; 1m: -1);
Implementation
Procedure Addc;
Begin
Z.Re:= X.Re + Y.Re;
Z.Im:= X.Im + Y.Im;
End {Addc};
Procedure Subc;
Begin
Z.Re:= X.Re - Y.Re;
Z.Im:= X.Im - Y.Im;
End {Subc};
Procedure Mulc;
Begin
Z.Re:= X.Re * Y.Re - X.Im * Y.Im;
Z.Im:= X.Re * Y.Im + X.Im * Y.Re;
End {Mulc};
Procedure DivC;
Var
ZZ: Real;
Begin
ZZ:= Sqr(Y.Re) + Sqr(Y.Im);
Z.Re:= (X.Re * Y.Re + X.Im * Y.Im) / ZZ;
Z.Im:= (X.Re * Y.Im - X.Im * Y.Re) / ZZ;
End;
End.
Текст этого модуля нужно поместить в файл CMPLX.PAS. Теперь можно написать программу, приведенную в примере:
Program TestOfComplex;
Uses Cmplx;
Var
A, B,
С
: Complex;
Begin
A.Re:= 1;
A.Im:= 1;
B.Re:= 1;
B.Im:= 2;
AddC (A, B, C);
WriteLn (‘
Сложение
: ‘, C.Re:5:1, C.Im:5:1,’i’);
SubC (A, B, C);
WriteLn (‘
Вычитание
: ‘, C.Re:5:1, C.Im:5:1,’i’);
MulC (A, B, C);
WriteLn ('
Умножение
: ‘, C.Re:5:1, C.Im:5:1,’i’);
DivC (A, B, C);
WriteLn ('
Деление
: ', C.Re:5:1, C.Im:5:1,’i’);
End.
Как видим, программе стали доступны все объекты, объявленные в интерфейсной части. При необходимости мы можем переопределить любой из этих объектов, как это произошло, например, с объявленной в модуле типизированной константой С. Переопределение объекта означает, что вновь объявленный объект "закрывает" ранее определенный в блоке одноименный объект. При необходимости мы можем получить доступ к "закрытому" объекту. Для этого нужно перед именем объекта поставить имя модуля и точку. Так, оператор
WriteLn(Cmplx.C.Re:5:1, Cmplx.C.Im:5:1,’i’);
выведет на экран содержимое "закрытой" типизированной константы из примера.
Стандартные модули
В Турбо-Паскале имеется восемь стандартных модулей, в которых содержится большое число разнообразных типов, констант, процедур и функций. Этими модулями являются: System, Dos, Crt, Printer, Graph, Overlay, Turbo3 и Graph3.
Модули Graph, Turbo3 и Graph3 содержатся в одноименных TPU-файлах, остальные входят в состав библиотечного файла TURBO.TPL. Лишь один модуль System подключается к любой Турбо-Паскалевой программе автоматически, все остальные становятся доступны только после указания их имен в списке, следующем за кодовым словом Uses.
Ниже приводится краткая характеристика стандартных модулей.
· В модуль System входят все процедуры и функции стандартного Паскаля, а также встроенные процедуры и функции Турбо-Паскаля, которые не вошли в другие стандартные модули (например, Inc, Dec, Getdir и т.п.). Как уже говорилось, модуль System подключается к любой Турбо-Паскалевой программе независимо от, того, объявлен ли он в предложении Uses или нет.
· Модуль Printer упрощает вывод текстов на матричный принтер. В нем определяется файловая переменная Lst типа Text, которая связывается с логическим устройством Prn. После подключения модуля может быть выполнена, например, такая программа:
·
· USES
· Printer;
· Begin
· WriteLn (Lst, ‘TEST’); {Выводит строку на принтер}
· End.
· В модуле Crt сосредоточены процедуры и функции, обеспечивающие управление текстовым режимом работы экрана. С помощью входящих в модуль блоков можно перемещать курсор в произвольную позицию экрана, менять цвет выводимых символов и окружающего их фона, создавать окна. Кроме того, в модуль включены также процедуры "сле пого" чтения клавиатуры и управления звуком.
· Модуль Graph содержит обширный набор типов, констант, процедур и функций для управления графическим режимом работы экрана. С помощью блоков, входящих в модуль Graph, можно создавать самые разнообразные графические изображения и выводить на экран текстовые надписи стандартными или разработанными программистом шрифтами. Программы модуля Graph после соответствующей настройки могут поддержать любой тип аппаратных графических средств (CGA, EGA, VGA).
Настройка на имеющиеся в распоряжении программиста технические средства графики осуществляется специальными программами-драйверами, которые не входят в библиотечный файл TURBO.TPL, но поставляются вместе с ним.
· В модуле Dos собраны процедуры и функции, открывающие доступ Турбо-Паскалевым программам ко всем средствам дисковой операционной системы PC DOS (MS DOS).
Блоки модуля Overlay понадобятся при разработке громоздких программ с перекрытиями (позволяет хранить в памяти только нужные части программы, а не требующиеся на данный момент удалять из памяти, сохраняя на диск, либо возвращать в память при необходимости). Как уже говорилось, Турбо-Паскаль обеспечивает создание программ, длина которых ограничивается лишь доступной памятью. Для большинства отечественных IBM-совместимых ПЭВМ доступная программе память составляет около 550 Кбайт (без инстру ментальных оболочек типа Norton Commander и без самой системы Турбо-Паскаль). Память такого размера достаточна для большинства прикладных задач, тем не менее, использование программ с перекрытиями снимает и это ограничение.
· Два библиотечных модуля Turbo3 и Graph3 введены для совместимости с ранней версией 3.0 системы Турбо-Паскаль.
Ключи и директивы компилятора.
Позволяют управлять процессами компиляции программы. Директива представляет собой комментарий со специальным синтаксисом: начинается с {$, далее пишется имя директивы (одна или несколько букв) и параметры.
Турбо-Паскаль 7.0 допускает применение директив в любом месте программы, где допускается применение комментариев.
В Турбо-Паскале 7.0 существует три основных вида директив:
1. Директивы - переключатели.
С их помощью, указывая символ (+) или (-), можно разрешать или запрещать тот или иной режим.
2. Директивы с параметрами.
Позволяют задавать компилятору параметры, такие как имена подключаемых файлов, размеры памяти, выделяемые под определённую задачу, и так далее.
3. Условные директивы.
Позволяют организовывать так называемую "условную компиляцию" частей исходного текста, благодаря чему скомпоновать программу определённым образом.
Все директивы, кроме директив-переключателей, должны иметь по крайней мере один пробел между именем директивы и параметром.
Чтобы не прописывать директивы компилятора в тексте программы можно воспользоваться пунктом "Options" меню ИПО и далее подпунктом "Compiler". Но при этом все изменения будут иметь силу при всех последующих компиляциях других программ. Однако, если указать в исходном тексте какие-либо директивы, противоречащие установленным в ИПО, то последние будут проигнорированы. Это справедливо как для командно- строчного компилятора, так и для встроенного в ИПО.
Директивы-переключатели.
Различаются по диапазону своего действия на глобальные и локальные:
- Глобальные - влияют на ход компиляции и выполнение всей программы,
- локальные - только на определённую часть программы.
Глобальные директивы могут быть объявлены в разделе объявлений и соглашений прежде, чем будет использовано хотя бы одно из следующих зарезервированных слов: Uses, Label, Const, Type, Procedure, Function, Begin, а Локальные директивы могут быть объявлены в любом месте программы или программного модуля.
При необходимости можно сгруппировать комментарии из нескольких директив, разделённых запятой: {$A+, B-}.
Примечание: двойное нажатие комбинации Ctrl+O заставит редактор поместить в самое начало файла строки, содержащие текущую настройку среды в виде директив компилятора.
Директивы:
· Выравнивание данных - (Word Align Data)
{$A+}
Тип глобальный. Делает возможным переключение между выравниванием на границу слова или байта. (Для 8086 выравнивание на границу слова игнорируется, однако для 80X86 выравнивание на границу слова означает более быстрое выполнение, поскольку адресация по всем элементам, имеющим размер в слово (чётная адресация) происходит за один цикл вместо двух. {$A+} не влияет:
- на переменные размером в байт,
- на поля структур или элементы массивов.
· Булевы вычисления (Complete Boolean Evaluation)
{$B+}
Тип локальный Может быть осуществлено два различных вида генерации кода для And и Or .При {$B-} генерируется код вычисления выражения, по короткой схеме (вычисления прекращаются сразу, как станет очевидным результат вычисления всего выражения).
Пример:
·
· Function Fun(X: Integer): Boolean;
·
Begin
· X:= X + 1;
· Fun:= X > 10;
· End;
· . . . . . . . . . . .
· Begin
· X:= 0;
· If False And Fun(X) Then
·
X:= 10;
· Информация для отладки (Debug Information)
{$D+}
Тип глобальный. Составляется таблица для отладчика, состоящая из N-строк для каждой процедуры, устанавливающая соответствующие адреса объектных кодов номерам строк исходного текста.
Отладочная информация приводит к увеличению размера файла .TPU и требует дополнительного пространства при компиляции использующей модуль программы, но на размер и скорость не влияет.
$D не будет работать в Турбо-Паскале 7.0 до тех пор пока не установить в меню или не задать параметр /V при запуске TPC.EXE .
· Эмуляция сопроцессора. (Emulation).
{$E+}
Тип глобальный. В процессе генерации кода нет библиотеки, реализующей функции сопроцессора программы.
· Дальний тип вызова (Force Far Calls).{$F-}
Тип локальный, (Far) или (Near).
· Генерация кода для 80286 (286 Instruction).
{$G-}
Тип локальный. При {$G-} генерируются инструкции 8086, такие программы могут работать на любой машине. Если {$G+}, то будут использоваться дополнительные инструкции 80286, которые позволяют генерировать более эффективный код, но не работают на 8086 и 8087. (Например команды Shr и Shl - расширенные).
· Проверка ошибок ввода-вывода (I/O checking).
{$I+}
Тип локальный. Если {$I-}, то результат ввода-вывода может быть проанализирован с помощью функции IOResult.
· Информация о локальных идентификаторах. (Local Symbols)
{$L+}
Тип глобальный. Используется в основном для модулей, дает возможность проверить и модифицировать локальные переменные модуля. Увеличивает размер .TPU, на размер и скорость работы выполняемой программы не влияет. В Турбо-Паскале 7.0 директива не будет работать до тех пор, пока в меню не будет Debuginfo или для TPC.EXE ключ /V.
· Использование математического сопроцессора (8087/80287).
{$N-}
Тип глобальный. Позволяет осуществлять выбор генерируемых кодов для вычислений с плавающей запятой при “-“ реализуется программно, при ”+” - код для сопроцессора.
Примечание: если отсутствует сопроцессор и возникла необходимость в использовании специальных вещественных типов, нужно использовать {$E+}.
· Использование оверлейных структур. (Overlays allowed).
{$O-}
Тип глобальный. Разрешает или запрещает генерацию оверлейного кода, то есть Турбо-Паскаль 7.0 может использовать модуль в качестве оверлейного, если он был скомпилирован с {$O+}. Задание данного режима компиляции не обязывает использовать данный модуль в качестве оверлейного. Директиву {$O+} почти всегда используют с {$F+}.
· Использование в качестве параметров массивов открытого типа (Open parameters).
{$P-}
Тип глобальный.
· Проверка переполнения при математических операциях (Overflow checking).
{$Q-}
Тип глобальный.
· Проверка границ. (Range-Checking).
{$R-}
Тип локальный. Рекомендуется использовать R+ при отладке, а затем отключать так как “+” увеличивает размер и замедляет работу программ. R+ осуществляет так же проверку виртуальных методов на состояние инициализации для экземпляра объекта, выполняющего вызов.
· Проверка переполнения стека (Stack Checking)
{$S+}
Тип локальный. При “+” компилятор генерирует вначале каждой процедуры или функции код, который проверяет, достаточно ли в стеке выделено места для локальных переменных.
· Использование типизированного адресного оператора @ (Typed @ operator).
{$T-}
Тип глобальный. Позволяет или запрещает использовать в тексте оператор @.
· Проверка параметров переменных строкового типа (Strict Var String).
{$V+}
Тип локальный. Управляет проверкой типа при передачи строк в качестве параметров-переменных. В состоянии “+” выполняется строгая проверка типа. Требуется, чтобы формальный и фактический параметр имели идентичные строковые типы; при “-” длины могут не совпадать.
· Расширенный синтаксис. (Extended Syntax).
{$X-}
Тип глобальный. При X+ вызов функции можно использовать как оператор, то есть результат функции может быть отброшен (функция может быть интерпретирована как процедура).
Директивы с параметрами.
· Включение файла для компиляции (Include Directories).
{$I < имя файла >}
Тип локальный. Сообщает компилятору о необходимости включить в компиляцию названный файл. Текст файла вставляется сразу за директивой. Расширение по умолчанию .PAS. Турбо-Паскаль допускает одновременно не более 15 входных файлов. Значит допускается вложенность до 15-ти уровней. Так же включаемый файл не может указываться в середине раздела операторов. Это означает, что все операторы между Begin и End раздела операторов должны быть в одном файле.
· Компоновка объектного файла. (Object Directories).
{$L < Имя-файла >}
Тип локальный. Предписывает компилятору использовать указанный файл (.Obj) при компоновке компилируемой программы или модуля.
$nbsp Директива {$L} используется для компоновки кода Ассемблера для подпрограмм, описанных External.
· Размеры выделяемой памяти (Memory Sizes).
{$M < размер стека >, < размер динамической области >}.
Тип глобальный. Размер-стека - целое число от 1024 до 65520. Для модуля размер- стека игнорируется (модуль использует стек основной программы). Размер динамической области - целое число от 0 до 65360. Директива {$M} при использовании в Unit не оказывает влияния на компиляцию.
· Компоновка оверлейного модуля.
{$O < Имя модуля >}
Тип локальный. Можно преобразовать обычный модуль в оверлейный. Игнорируется при попытке объявить её в теле какого-либо модуля. Директива должна указываться в тексте программы после слова Uses. Любой модуль, который указывается в качестве параметра, должен быть откомпилирован с директивами {$O+} и {$F+}.
Условные директивы.
Установить условие (Conditional Defines).
{$ Define < условие >}
Определяет условия, которые можно использовать в операторах условной компиляции.
Установить условие - значит с помощью данной директивы ввести некоторое слово, которое затем будет управлять компиляцией какого- либо фрагмента программы. За условием не должно следовать ничего кроме }.
Пример:
{$Define Debug}
. . . . . . . . . . . . . .
{$Ifdef Debug}
Writeln(‘Отладка’);
{$Endif}
Можно ввести несколько условий одновременно, причём условия должны отделяться друг от друга точкой с запятой (в меню).
Допускается использование развёрнутой формы операторов условной компиляции.
Пример:
{$Ifdef Demo}
Code:= 1;
{$Else}
Code:= 2;
A:= ”Yes”;
{$EndIf}.
Операторы условной компиляции могут быть вложенными, что позволяет контролировать несколько условий одновременно.
Пример:
{$Ifdef Var1}
{$Ifdef Var2}
A: = N;
{$Else}
A: = 0;
{$Endif}
{$Endif}.
ФАЙЛЫ
Основные определения.
Файл - именованная область внешней памяти ПЭВМ (жесткого диска, гибкой дискеты, электронного "виртуального" диска), либо логическое устройство - потенциальный источник или приемник информации.
Любой файл имеет три характерных особенности:
· Во-первых, у него есть имя, что дает возможность программе работать одновременно с несколькими файлами.
· Во-вторых, он содержит компоненты одного типа. Типом компонентов может быть любой тип Турбо-Паскаля, кроме файлов. Иными словами, нельзя создать «файл файлов».
· В-третьих, длина вновь создаваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти.
Файловый тип или переменную файлового типа можно задать одним из трех способов:
<
имя
> = FILE OF <
тип
>;
<
имя
> =
ТЕХТ
;
< имя > = FILE; .
Здесь < имя > - имя файлового типа или файловой переменной,
FILE, OF, TЕХТ - кодовые слова (англ.: файл, из, текст),
< тип > - любой тип Турбо-Паскаля, кроме файлов. Пример:
Type
Man=record
Name: string;
LastName: string;
End;
Men=file of Man;
Var
Staff: Men;
Numbers: file of real;
Book: Text;
A_File: File;
В зависимости от способа объявления можно выделить три вида файлов:
- типизированные (задаются предложением FILE OF ...),
- текстовые (задаются предложением ТЕХТ),
- нетипизированные (задаются предложением FILE).
Вид файла, вообще говоря, определяет способ хранения информации в файле. Однако в Паскале нет средств контроля вида ранее созданных файлов. При объявлении уже существующих файлов программист должен сам следить за соответствием вида объявления характеру файла.
Доступ к файлам.
Любой Турбо-Паскалевой программе доступны два предварительно объявленных файла со стандартными файловыми переменными: INPUT - для чтения данных с клавиатуры и OUTPUT - для вывода на экран. Стандартный Паскаль требует обязательного упоминания этих файлов в заголовке программы. В Турбо-Паскале это необязательно, вот почему заголовок программы можно опускать.
Любые другие файлы, а также логические устройства становятся доступны программе только после выполнения особой процедуры открытия файла (логического устройства). Эта процедура заключается в связывании ранее объявленной файловой переменной с именем существующего или вновь создаваемого файла, а также в указании направления обмена информации: чтение из файла или запись в него.
Связывание файловой переменной с именем файла осуществляется обращением к встроенной процедуре ASSIGN:
ASSIGN(< ф.п. >, < имя файла или л.у. >);
Здесь < ф.п. > - файловая переменная (правильный идентификатор, объявленный в программе как переменная файлового типа);
< имя файла или л.у. > - текстовое выражение, содержащее имя файла или логическое устройство. Пример:
Assign(Book,’PascalLecture.txt’);
Если имя файла задается в виде пустой строки, например, ASSIGN(f, ‘’), то файловая переменная связывается со стандартным файлом INPUT или ОUТРUТ.
Имена файлов
Имя файла - это любое выражение строкового типа, которое строится по правилам определения имен в дисковой операционной ДОС) ПЭВМ:
· имя содержит до восьми разрещенных символов (разрешенные символы - это произвольные символы с кодами от 33 до 255, кроме символов пробел, точка, запятая, двоеточие, звездочка, знак вопроса, обратная косая черта, а также символ Забой - код 127 по стандарту АSCII);
· имя начинается с любого разрешенного символа;
· за именем может следовать расширение - последовательность до трех разрешенных символов; расширение, если оно есть, отделяется от имени точкой.
Перед именем может ставиться так называемый путь к файлу - имя диска и/или имя текущего каталога и имена каталогов вышестоящих уровней.
Имя диска содержит одну из латинских букв A..Z, после которой ставится двоеточие. Имена А: и В: относятся к дисковым накопителям на гибких дискетах, имена С:, D: и т.д.-к жестким дискам. Эти имена могут относиться также к одному или нескольким виртуальным дискам, созданным в операционной памяти ПЭВМ специальной командой RAMDRIVE в ходе выполнения файла автоустановки параметров ДОС СОNFIG.SYS.
Если имя диска не указано, подразумевается устройство по умолчанию - то, которое было установлено в операционной системе перед началом работы программы.
3а именем диска может указываться имя каталога, содержащего файл. Если имени каталога предшествует обратная косая черта, то путь к файлу начинается из корневого каталога, если черты нет - из текущего каталога, установленного в системе по умолчанию. За именем каталога может следовать одно или несколько имен каталогов нижнего уровня. Каждому из них должна предшествовать обратная косая черта. Весь путь к файлу отделяется от имени файла обратной косой чертой. Максимальная длина имени вместе с путем - 79 символов.
Пример: программа создает на диске C файл, содержащий возрастающие числа, закрывает файл, открывает его и полностью выводит его содержимое (в виде чисел).
Var
Num: file of Byte;
i:byte;
Begin
Assign(Num,'C:\Numbers.num');
Rewrite(Num);
For i:=0 to 255 do
Write(Num,i);
Close(Num);
Assign(Num,'C:\Numbers.num');
Reset(Num);
Repeat
Read(Num,i);
Writeln(i);
Until Eof(Num);
End.
Логические устройства
Стандартные аппаратные средства ПЭВМ, такие как клавиатура, экран терминала, печатающее устройство (принтер) и коммуникационные каналы ввода-вывода, определяются в Турбо-Паскале специальными именами, которые называются логическими устройствами. Все они в Турбо-Паскале рассматриваются как потенциальные источники или приемники текстовой информации.
СОN означает консоль - клавиатуру или экран терминала. Турбо- Паскаль устанавливает различие между этими физическими устройствами по направлению передачи данных: чтение данных возможно только с клавиатуры, а запись данных - только на экран. Таким образом, с помощью логического устройства СОN нельзя, например, прочитать данные с экрана ПЭВМ, хотя такая аппаратная возможность существует.
Ввод с клавиатуры буферируется: символы по мере нажатия на клавиши помещаются в специальный строковый буфер, который передается программе только после нажатия на клавишу "Ввод". Сам символ "Ввод" (код 13) в буфер не помещается и программе не передается. Буферизация ввода обеспечивает возможность редактирования вводимой строки стандартными средствами ДОС. При вводе символов осуществляется их эхо-повтор на экране ПЭВМ. В Турбо-Паскале можно прочитать любой символ клавиатуры, в том числе и "Ввод", сразу после нажатия на соответствующую клавишу без эхо- повтора.
PRN - логическое имя принтера. Если к ПЭВМ подключено несколько принтеров, доступ к ним осуществляется по логическим именам LРТ1, LРТ2 и LРТЗ. Имена РRN и LРТ1 первоначально синонимы. Средствами ДОС можно переназначить имя РRN на любое другое логическое устройство, способное принимать информацию.
Стандартный библиотечный модуль PRINTER, входящий в библиотеку ТURВО.ТРL, объявляет имя файловой переменной LST и связывает его с логическим устройством LРТ1. Это дает возможность использовать простое обращение к принтеру.
АUХ - логическое имя коммуникационного канала, который обычно используется для связи ПЭВМ с другими машинами. Коммуникационный канал может осуществлять и прием, и передачу данных, однако в Турбо-Паскалевой программе в каждый момент времени ему можно назначить только одну из этих функций. Как правило, в составе ПЭВМ имеются два коммуникационных канала, которым даются имена логических устройств СОМ1 и СОМ2. Первоначально имена АUХ и СОМ1 синонимы.
NUL - логическое имя "пустого" устройства. Это устройство чаще всего используется в отладочном режиме и трактуется как устройство неограниченной емкости - приемник информации. При обращении к NUL как к источнику информации выдается признак конца файла ЕОF.
Связывание логического устройства с файловой переменной осуществляется процедурой
ASSIGN(< файловая переменая >, '< имя файла >').
Турбо-Паскаль никогда не связывает имена логических устройств с дисковыми файлами, и в этом смысле можно считать эти имена зарезервированными. Иными словами, нельзя, например, обратиться к дисковому файлу с именем РRN - Турбо-Паскаль всегда интерпретирует такой запрос как обращение к принтеру.
Инициализация файла
Инициировать файл означает указать для этого файла направление передачи данных. В Турбо-Паскале можно открыть файл только для чтения, только для записи, а также для чтения и записи информации одновременно.
Для чтения файл инициируется с помощью процедуры:
RESET (< ф.п. >);
Здесь < ф.п. > - файловая переменная, связанная ранее процедурой ASSIGN() с уже существующим файлом или логическим устройством - источником информации.
При выполнении этой процедуры дисковый файл или логическое устройство подготавливается к чтению информации. Внутренняя Турбо- Паскалевая переменная-указатель, связанная с этим файлом, указывает на начало файла, т.е. на компонент с порядковым номером 0.
Если делается попытка инициировать чтение из несуществующего файла или логического устройства РRN, возникает ошибка периода исполнения, которая может быть сообщена программе с помощью встроенной функции IORESULT типа WORD, которая в этом случае имеет ненулевое значение. (Можно выяснить, существует ли требуемый файл на диске).
Турбо-Паскаль допускает к типизированным файлам, открытым с помощью процедуры RESET (т.е. для чтения информации), обращаться с помощью процедуры WRITE (т.е. для записи информации). Такая возможность позволяет легко обновлять ранее созданные типизированные файлы и при необходимости расширять их.
Встроенная процедура REWRITE(< ф.п. >) инициирует запись информации в файл или логическое устройство, связанное ранее с файловой переменной < ф.п. >. Процедурой REWRITE нельзя инициировать запись информации в ранее существовавший дисковый файл: при выполнении этой процедуры старый файл уничтожается и никаких сообщений об этом в программу не передается. Новый файл подготавливается к приему информации и его указатель устанавливается в нуль.
Встроенная процедура АРРЕND(< ф.п. >) инициирует запись в ранее существовавший текстовый файл для его расширения - указатель файла устанавливается в его конец. Не следует забывать, что процедура АРРЕND применима только к текстовым файлам. Если текстовый файл ранее уже был открыт с помощью RESET или REWRITE, использование процедуры APPEND приведет к закрытию этого файла и открытию ею вновь, но уже для добавления записей.
Процедуры и функции для работы с файлами.
Ниже описываются процедуры и функции, которые можно использовать с файлами любого вида. Специфику работы с типизированными, текстовыми и нетипизированными файлами рассмотрим позднее.
· CLOSE(< ф.п. >)
Закрывает файл, однако связь файловой переменной с именем файла, установленная ранее процедурой АSSIGN(), сохраняется.
Поскольку связь файла с файловой переменной сохраняется, файл можно повторно открыть без дополнительного использования процедуры ASSIGN.
· RENAME (< ф.п. >, < новое имя >)
Переименовывает файл < ф.п. >.
Перед выполнением процедуры необходимо закрыть файл, если он ранее был открыт.
· ERASE(< ф.п. >).
Уничтожает файл < ф.п. >
· FLUSH(< ф.п. >).
Очищает внутренний буфер файла и, таким образом, гарантирует сохранение всех последних изменений файла на диске.
Любое обращение к файлу в Турбо-Паскале осуществляется через некоторый внутренний буфер, что необходимо для согласования bmsrpemmecn представления файлового компонента - записи с принятым в ДОС форматом хранения данных на диске. В ходе выполнения процедуры FLUSH все новые записи будут действительно записаны на диск. Процедура игнорируется, если файл был инициирован для чтения процедурой RESET.
· Функция ЕОF(< ф.п. >): BOOLEAN.
Логическая функция, тестирующая конец файла. Возвращает ТRUE, если файловый указатель стоит в конце файа. При записи это означает, что очередной компонент будет добав лен в конец файла, при чтении - что файл исчерпан.
· MKDIR(< имя каталога >).
Создает новый каталог на указанном диске.
Имя уже существующего каталога не может быть последним именем в пути, т.е. именем вновь создаваемого каталога.
ДРУГИЕ ВОЗМОЖНОСТИ ТУРБО-ПАСКАЛЯ
Внешние процедуры (функции)
С помощью внешних процедур (функций) можно вызывать из Турбо- Паскалевой программы процедуры или функции, написанные на языке ассемблера и других языках. Машинно-ориентированный язык ассемблера предоставляет квалифицированному программисту богатейшие возможности использования всех особенностей архитектуры ПЭВМ. Ассемблерные программы выполняются значительно быстрее и занимают меньший объем памяти, чем Турбо-Паскалевые программы, однако низкий уровень языка существенно снижает производительность труда программиста и резко усложняет отладку программ, Как правило, на языке ассемблера пишутся сравнительно небольшие фрагменты программ, учитывающие особенности архитектуры ПЭВМ, которые невозможно использовать в Турбо- Паскалевой программе. Внешняя процедура (функция) в Турбо-Паскалевой программе объявляется заголовком, за которым следует зарезервированное слово ЕХТЕRNАL, например:
Function LoCase(ch: char): char; external;
Procedure Swapping(Var a, b; N: word); externa1;
Как видим, тело внешней процедуры (функции) отсутствует его заменяет кодовое слово ЕХТЕRNАL. Для подключения ассемблерной программы необходимо предварительно ее откомпилировать и получить OBJ - файл с перемещаемым кодом программы. Непосредственно перед описанием внешней процедуры (функции) в основной программе вставляется директива компилятора {$L < имя файла >}, где < имя файла > - имя OBJ - файла. Диск и каталог, в котором следует искать этот файл, если он не обнаружен в текущем каталоге, указывается опцией "OPTIONS/DIRECTORIES/OBJECT DIRECTORIES" ИПО.
Разумеется, в рамках этой книги совершенно невозможно рассмотреть методику разработки программ на языке ассемблера - это предмет самостоятельной книги. Тем не менее, следует дать один совет. Прежде чем браться за разработку ассемблерной процедуры, тщательно взвесьте специфические потребности разрабатываемой программы и возможности Турбо-Паскаля: может оказаться, что, оставаясь только в рамках Турбо-Паскаля, Вы решите задачу намного проще и быстрее, хотя, разумеется, Ваша программа и не будет оптимальной по скорости вычислений и расходуемой памяти. Замечено, что производительность труда программиста почти не зависит от языка и составляет 10...20 операторов отлаженной программы в день. Один оператор Турбо- Паскаля реализуется десятками машинных команд, вот почему использование языков высокого уровня, к которым относится Турбо- Паскаль, значительно ускоряет разработку программ.
Перед передачей управления внешней процедуре (функции) Турбо- Паскалевая программа заталкивает параметры обращения в программный стек в том порядке, как они перечислены в заголовке процедуры (функции). Ассемблерная процедура должна сохранить регистры BP, SP, SS и DS центрального процессора в самом начале своей работы и восстановить содержимое этих регистров перед возвратом управления в Турбо-Паскалевую программу. Остальные регистры можно не сохранять и соответственно не восстанавливать.
Параметры могут передаваться по ссылке или по значению. Если параметр передается по ссылке, в стек заталкивается указатель, содержащий абсолютный адрес параметра; если по значению - в стек заталкивается сам параметр, точнее - его значение. Параметры, объявленные в заголовке с предшествующим словом VAR, всегда передаются по ссылке. Параметры- значения могут передаваться по ссылке или по значению, в зависимости от длины внутреннего представления соответствующею параметра. В общем случае используется следующее правило: если длина внутреннего представления параметра-значения составляет 1, 2 или 4 байта, само значение параметра заталкивается в стек. Точно так же через стек передаются и все вещественные данные длиной в 4, 6, 8 и 10 байт (в версии 4.0 эти данные передаются через стек сопроцессора 8087/80287, в версии 5.0 - через стек центрального процессора 8086/80286). Во всех остальных случаях, если длина внутреннего представления больше 4 байт, соответствующий параметр передается по ссылке.
Ассемблерные функции должны возвращать результат с помощью регистров центрального процессора или сопроцессора в зависимости от длины внутреннего представления результата по следующим правилам:
- длиной в 1 байт - в регистре АL;
- длиной в 2 байта - в регистре АХ;
- длиной в 4 байта - в регистрах DХ:АХ (старшее слово в DХ);
- тип REAL (б байт) - в регистрах DХ:ВХ:АХ;
- типы SINGLE, DOUBLE, EXTENDED и COMP - через стек сопроцессора 8087/80287;
- указатели - в регистрах DХ:АХ (сегмент в DХ);
- строки возвращаются по ссылке: адрес начала строки помещается в DХ:АХ (сегмент в DХ).
Все ассемблерные процедуры должны размещаться в сегменте с именем СОDЕ, а имена процедур и функций должны быть объявлены директивой PUBLIC. Локальные переменные необходимо размещать в сегменте с именем DАТА. Все имена, объявленные в интерфейсной части модулей Турбо-Паскалевой программы, становятся доступны ассемблерной процедуре (функции) после их объявления директивой ЕХТRN.
Использование встроенных машинных кодов.
В Турбо-Паскале имеется возможность включения в программу фрагментов, написанных непосредственно в машинных кодах. Для этого используется зарезервированное слово INLINE, за которым в круглых скобках следует один или несколько элементов машинного кода, разделяемых косыми чертами. Элемент кода, в свою очередь, строится из одного или более элементов данных, разделенных знаками "+" или "-".
В качестве элемента данных может использоваться целая константа, идентификатор (переменной, константы или функции) или ссылка на счетчик адреса "*". Каждый элемент данных вызывает генерацию 1 или 2 байт кода программы. Значение этого кода получается сложением или вычитанием элементов данных в соответствии с разделяющим их знаком. Значением идентификатора переменной, константы, функции является адрес соответствующего объекта, значением ссылки на счетчик адреса является тот адрес, по которому будет размещаться следующий байт кода.
Элемент кода будет генерировать 1 байт кода, если этот элемент состоит только из целых констант и значение результата не превышает 1 байта, т.е. находится в диапазоне от 0 до 255. Если значение превышает 255 или элемент кода содержит ссылку на счетчик адреса, генерируются 2 байта. Знаки ">" и "<" могут использоваться для отмены автоматического выбора размера генерируемого кода. Если элемент кода начинается со знака "<", в код заносится только один младший байт, даже если само значение занимает 2 байта. Наоборот, если элемент начинается со знака ">", в код заносится 2 байта (старший байт может оказаться нулевым).
Значением идентификатора, как уже говорилось, является смещение соответствующего объекта. Если переменная. глобальная, смещение задается относительно сегмента данных, хранящегося в регистре DS, если переменная локальная, - относительно сегмента стека (регистр SР). Базовым сегментом типизированной константы является сегмент кода (регистр СS).
В следующем примере приводятся две короткие процедуры, с помощью которых можно вывести данные через любой порт ПЭВМ:
Function InPort(Port: Word): Word;
Var
pp: Word;
cc: Char;
Begin
pp:= port;
in1ine( {assembler code: }
$8b/$96/pp/ { MOV DX,pp[bp] }
$EC/ { IN AX,DX }
$88/$86/cc { MOV cc[bp],AX }
);
InPort:= ord(cc);
End;
Procedure OutPort(Port, Bt: Word);
Var
pp: Word;
cc: Char;
Begin
pp:=port;
cc:=chr(Bt);
in1ine(
$8a/$86/cc/ { MOV AX,cc[bp] }
$8b/$96/pp/ { MOV DX,pp[bp] }
$EE { OUT DX,AX }
);
END;
Операторы INLINE могут произвольным образом смешиваться с другими операторами Турбо-Паскаля, однако при выходе из процедуры (функции) содержимое регистров BP, SP, DS и SS должно быть таким же, как и при входе в нее.
Обращение к функциям операционной системы
Турбо - Паскаль предоставляет программисту практически неограниченные возможности использования любых функций стандартной операционной системы PC DOS (MS DOS). При внимательном анализе материала этой книги Вы, очевидно, заметите, что большую часть составляет описание многочисленных библиотечных процедур и функций. Собственно язык Паскаль весьма прост и лаконичен, что, по мнению многих специалистов, и послужило одной из причин его широкого распространения. Значительная же часть библиотечных процедур и функций является по существу своеобразным интерфейсом между языковыми средствами Турбо-Паскаля и функциями операционной системы. Разумеется, можно только приветствовать усилия разработчиков Турбо-Паскаля по созданию мощных библиотек TURBO.TPL и GRAPH.TPU, однако ясно что таким способом невозможно запрограммировать все допустимые обращения к средствам ДОС. Вот почему в Турбо-Паскаль включены две процедуры, с помощью которых программист может сам сформировать вызов той или иной функции ДОС.
Следует учесть, что единственным механизмом обращения к функциям операционной системы является инициация программного прерывания. Прерывание - это особое состояние вычислительного процесса. В момент прерывания нарушается нормальный порядок выполнения команд программы, и управление передается специальной процедуре, которая входит в состав ДОС и называется процедурой обработки прерывания. Каждое прерывание характеризуется в рамках ДОС порядковым номером и связано со своей процедурой обработки. В архитектуре центрального процессора ПЭВМ предусмотрены прерывания двух типов: аппаратные и программные. Аппаратные прерывания создаются схемами контроля и управления ПЭВМ и сигнализируют операционной системе о переходе какого-либо устройства в новое состояние или о возникновении неисправности. Программные прерывания инициируются при выполнении одной из двух специальных команд (INТ или INТО) и служат для обращения к средствам ДОС.
Всего в ДОС имеется около 40 программных прерываний, каждое из которых может активизировать одну или несколько функций ДОС. Одно из прерываний - с номером 33 ($21 - шестнадцатиричное число) обеспечивает доступ к 85 функциям, а всего в ДОС имеется более 200 разнообразных функций.
Описываемые ниже процедуры входят в состав библиотечного модуля DOS и становятся доступными после объявления USES DOS. При возникновении программного прерывания в большинстве случаев необходимо передать процедуре обработки прерывания некоторые параметры, в которых конкретизируется запрос нужной функции. Эти параметры, а также выходная информация передаются от программы к процедуре и обратно через регистры центрального процессора. В модуле DOS для этих целей определен специальный тип:
Type
Registers = record
Case integer of
0:(AX, BX, CX, 0X, BP, SI, DI, DS, ES, Flags: word);
1:(
end;
Как видим, этот тип имитирует регистры центрального процессора и дает возможность обращаться к ним как к 16-битным или 8-битным регистрам.
С помощью процедуры INTR инициируется программное прерывание с требуемым номером;
формат обращения: INTR(< N >, < регистры >)
Здесь < N > - выражение типа ВYТЕ, означающее номер прерывания;
< регистры > - переменная типа REGISTERS, в которой процедуре обработки прерывания передается содержимое регистров и возвращается выходная информация.
Так, прерывание с номером 18 ($12) возвращает в регистре AX объем оперативной памяти ПЭВМ. Пример программы выводящей на экран сообщение об этом объеме:
Program IntrDem;
Uses DOS;
Var
r: registers;
Begin
Intr($12, r);
writeln('
Объем
памяти
= ', r.
АХ
, '
Кбайт
')
END.
Процедура MSDOS.
Инициирует прерывание с номером 33 ($21);
формат обращения: MSDOS(< регистры >);
Здесь < регистры > - переменная типа REGISTERS, содержащая значения регистров на входе и выходе процедуры обработки прерывания.
Программное прерывание с номером 33 ($21) стоит особняком: как уже говорилось, оно дает доступ к большому количеству функций ДОС (этим прерыванием вызывается 85 функций). Рассматриваемая процедура полностью эквивалентна вызову процедуры INTR c номером прерывания 33. Программа примера выведет на экран версию операционной системы:
Program MsDosDemo;
Uses DOS;
Var
R: registers;
Begin
r.AH: = $30;
MsDos(r);
write1n ('Версия операционной системы: г.АL, '.', г.АН)
END.
Поддержка процедур обработки прерываний.
При написании процедур обработки прерываний существенными являются два обстоятельства. Во-первых, процедура обработки прерывания не должна искажать работу прерванной программы. Для этого необходимо сначала сохранить регистры центрального процессора, а перед выходом из процедуры - восстановить их. Во- вторых, процедура должна строиться по принципу реентерабельности (повторной входимости): ее работа может быть прервана в любой момент другими прерываниями и ДОС может обратиться к соответствующей функции до завершения обработки предыдущего прерывания.
Турбо - Паскаль предоставляет программисту возможность написать процедуру обработки прерывания на языке высокого уровня, хотя обычно такие процедуры пишутся на языке ассемблера.
Турбо-Паскалевая процедура обработки прерывания должна начинаться зарезервированным словом INTERRUPT (англ. прерывание), например:
Procedure IntProc(Flags, CS, IP, AX, BX, CX, DX, SI, DF, 0S, ES, BP: word); inerrupt;
begin
.......
end;
Список формальных параметров должен быть именно таким, как в примере: через эти параметры вызова все регистры прерванной программы становятся доступны процедуре обработки прерывания. Кодовое слово INTERRUPT приводит к генерации специальных машинных кодов, обеспечивающих заталкивание регистров в стек при входе в процедуру и извлечение их из стека перед выходом из нее.
При входе в процедуру:
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
push bp
mov bp,si
sub sp,LocalSize
mov ax,SEG DATA
mov ds,ax
При выходе из процедуры:
mov sp,bp
pop bp
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
irep
В самой процедуре обработки прерывания не рекомендуется обращение к другим функциям ДОС, так как некоторые из них, в том числе все функции ввода-вывода, нереентерабельны.
Для связи с любыми процедурами прерываний, а следовательно и с процедурами, написанными программистом, используются векторы прерываний - четырехбайтные абсолютные адреса точек входа в эти процедуры. Векторы прерываний располагаются в младших адресах оперативной памяти, начиная с нулевого адреса: прерывание номер 0 - по адресу 0, номер 1 - по адресу 1 * 4 = 4, номер N - по адресу N*4.
С помощью следующих двух процедур программист может прочитать содержимое любого вектора или установить его новое значение.
Процедура GETINTVECT. Возвращает вектор прерывания с указанным номером;
формат обращения GETINTVECT(< N >, < вектор >> )
Здесь < N > - выражение типа ВYТЕ, содержащее номер прерываиия;
< вектор > -переменная типа РOINTER, в которой возвращается адрес точки входа в процедуру обработки прерывания.
Пример: программа выводит на экран содержимое всех ненулевых векторов прерываний.
Uses DOS;
var
i: byte;
p: pointer;
Begin
for i:= 0 to 255 do
Begin
GetIntVec(i, p);
if (Seg(p^) <> 0) or (Ofs(p^) <> 0) then
write1n('N = ', i:3, 'Seg = ', Seg(p^):5, 'Ofs =', Ofs(p^):5);
End;
End.
Процедура SETINTVEC. Устанавливает новое значение вектора прерывания;
формат обращения SETINTVEC(< N >>, < адрес >);
Здесь < адрес > - выражение типа РOINTER , в котором задается адрес точки входа в процедуру обработки прерывания.
При нормальном завершении Турбо-Паскалевой программы она выгружается из памяти, что делает невозможным разработку резидентных в памяти процедур обработки прерываний. Вы можете прекратить работу программы и оставить ее в памяти, если воспользуетесь процедурой КЕЕР.
Процедура КЕЕР. Завершает работу программы и оставляет ее в памяти;
формат обращения КЕЕР(< код >)
< код > - выражение типа WORD, в котором содержится код завершения программы.
Код завершения представляет собой фактически единственный механизм передачи сообщений от запущенной программы к программе, которая ее запустила. Он может быть проанализирован в вызывающей программе с помощью функции DOSEXITCODE.
Функция DOSEXITCODЕ. Возвращает значение типа WORD - код завершения подчиненной программы.
Запуск внешних программ.
Из Турбо-Паскалевой программы можно запустить любую другую готовую к работе программу. Для этого используется процедура ЕХЕС из библиотечного модуля DOS;
формат обращения ЕХЕС(< имя >, < параметры >);
Здесь < имя > - выражение типа STRING, в котором задается имя файла с вызываемой программой;
< параметры > - выражение типа STRING, в котором передаются параметры вызова.
Имени запускаемой программы должен предшествовать путь к файлу. Параметры передаются запускаемой программе в виде текстовой строки и могут быть проанализированы ею с помощью двух следующих функций.
Функция PARAMCOUNT. Возвращает общее количество параметров вызова программы (значение типа WORD).
Обращение PARAMCOUNT
Параметры вызова обычно следуют в командной строке ДОС сразу за именем вызываемой программы и отделяются от этого имени и друг от друга пробелами, например:
С:\ТURBO MyProg.Рas
C:\SIAM A:\Sistem.Sia
Здесь MyProg.Pas и A:\Sistem.Sia - параметры, передаваемые программам TURBO и SIAM.
При вызове программы непосредственно из среды Турбо-Паскаля ей можно передать параметры с помощью опции OPTIONS/PARAMETERS
Функция PARAMSTR Возвращает значение типа STRING, соответствующее нужному параметру вызова,
формат обращения PARAMSTR(< N >)
Здесь < N > - выражение типа WORD, задающее порядковый номер параметра.
Использование процедуры ЕХЕС имеет ряд специфических особенностей. Прежде всего необходимо отметить, что сама Турбо- Паскалевая вызывающая программа остается резидентной в памяти, поэтому она не должна занимать всю оперативную память. Объем выделяемой программе памяти регулируется опцией OPTIONS/COMPILER/MEMORY SIZE или директивой компилятора {$M . . }. По умолчанию параметры этой опции таковы (0 и 655360 байт соответственно), что Турбо-Паскалевая программа занимает весь доступный объем памяти и вызываемая программа не будет загружена. Полезно включить в текст вызывающей программы директиву компилятора, в которой изменяются принятые по умолчанию размеры памяти. Это можно сделать, например, так:
{$М 2048, 0, 0}
Такая директива ограничивает используемую программой область стека объемом 2 Кбайт и исключает возможность использования в ней динамической памяти. Разумеется, Вы можете установить и другие значения параметров в этой директиве.
Далее, специфические особенности исполнения Турбо-Паскалевых программ требуют изменения стандартных значений некоторых векторов прерываний. К ним относятся векторы со следующими шестнадцатеричными номерами: $00, $02, $18, $23, $24, $34, $35, $36, $37, $38, $39, $3А, $3В, $3С, $3D, $3Е, $3F, $75.
Начальные значения этих векторов сохраняются в восемнадцати переменных с именами SAVEINTXX из библиотечного модуля SYSTEM, где ХХ -шестнадцатеричный номер прерывания. Поэтому непосредственно перед запуском внешней программы и сразу после возврата из нее рекомендуется вызвать библиотечную процедуру без параметров SWAPVECTORS, которая обменивает содержимое векторов прерывания и перечисленных переменных.
Пример: программа воспринимает с клавиатуры любую команду ДОС, затем вызывает командный процессор СОММАND.СОМ операционной системы и передает ему эту команду.
Program ExecDemo;
{$M 1024, 0, 0}
Uses DOS;
Var
st: string [79];
Begin
Write(‘
Введите
команду
DOS:’);
Readln(st);
if st <> '' then
Begin
st:= 'C:\'+st,;
SwapVectors;
Exec(GetEnv('COMSPEC'), st);
SwapVectors;
End;
End.
Обратите внимание: для указания файла СОММАND.СОМ и пути к нему использовано обращение к библиотечной функции GETENV, с помощью которой можно получить параметры настройки операционной системы. В частности, параметр СОМSPЕС определяет спецификацию файла, содержащего командный процессор.
С помощью следующей несложной программы можно вывести на экран ПЭВМ список всех параметров настройки ДОС.
Program EnvParDemo;
Uses DOS;
Var
i: integer;
Begin
For i:= 0 To EnvCount Do
Writeln(EnvStr(i));
End.
Функция ENVCOUNT. Возвращает значение типа INTEGER, в котором содержится общее количество установленных в ДОС параметров.
Обращение ENVCOUNT.
Функция ENVSTR. Возвращает значение типа STRING, содержащее имя и значение нужного параметра настройки операционной системы;
формат обращения ENVSTR(< N >) Здесь < N > - выражение типа INTEGER, в котором указывается номер параметра.
Эта функция возвращает строку вида NАМЕ = VАLUЕ, где NАМЕ - имя, а VALUE - значение соответствующего параметра настройки.
Функция GETENV возвращает значение типа STRING, в котором содержится параметр настройки ДОС;
формат обращения GETENV (< имя >) Здесь < имя > - выражение типа STRING, определяющее имя параметра.
Эта функция имеет параметр обращения NАМЕ и возвращает значение VALUE (см. функцию ENVSTR).
Оверлей
Как уже говорилось в, максимальный размер модуля не может превышать 64 Кбайт, однако количество модулей не ограничено, что дает возможность разрабатывать весьма крупные программы, занимающие, например, всю доступную оперативную память ПЭВМ (приблизительно 580 Кбайт). Однако в некоторых случаях и этот объем может оказаться недостаточным. Турбо-Паскаль предоставляет в распоряжение программиста простой и достаточно эффективный механизм оверлея, с помощью которого Вы сможете создавать программы практически неограниченной длины (следует оговориться, что речь идет только о длине кода программ; два важных размера - длина стека данных и программного стека - в Турбо-Паскале не может превышать 64 Кбайт независимо от структуры программы).
Оверлей - это такой способ использования оперативной памяти, при котором в один и тот же участок памяти, называемый оверлейным буфером, попеременно по мере надобности загружаются различные оверлейные (перекрывающиеся) модули. При этом все оверлейные модули в готовом к работе виде хранятся на диске, а в оперативной памяти в каждый момент находится лишь один активный модуль и, возможно, небольшое число других неактивных модулей.
Пусть, например, программа состоит из главной части МАIN и двух модулей А и В. Пусть также LМ, LА и LВ - длина соответственно главной части и обоих модулей, причем LA > LВ. Тогда неоверлейная программа займет в памяти LМ + LA + LВ байт, в то время как оверлейная программа лишь LМ + LА.
При исполнении оверлейной программы в память первоначально загружается главная часть и один из модулей, например А. Если в процессе исполнения программы встретится обращение к модулю В, программа приостановит свою работу, с диска в оверлейный буфер будет загружен модуль В (модуль А при этом будет частично уничтожен), после чего программа продолжит свою работу. Если в дальнейшем встретится обращение к А, точно таким же образом будет загружен модуль А, причем загрузка нужных модулей в оверлейный буфер осуществляется автоматически и программисту не нужно об этом заботиться.
Описанный механизм выявляет главное преимущество оверлейной структуры: объем памяти, занимаемой оверлейной программой, определяется длиной главной части н наибольшего из перекрывающихся модулей, в то время как при неоверлейной структуре в этот объем входит суммарная длина всех модулей. Чем больше в программе оверлейных модулей и чем меньше длина наибольшего из них, тем больший выигрыш в памяти дает оверлейная структура. Однако совершенно очевиден и главный недостаток таких структур: на каждую загрузку оверлейного модуля с диска в оверлейный буфер требуется дополнительное время, поэтому оверлейная программа будет в общем случае исполняться с меньшей скоростью.
Работа оверлейных программ обеспечивается с помощью процедур, и функций библиотечного модуля OVERLAY, входящего в библиотечный файл TURBO.TPL.
Оверлейные программы нужно создавать в такой последовательности:
1. Вначале необходимо выделить главную часть программы и разбить оставшуюся часть на несколько модулей. Отметим, что никаких дополнительных ограничений на модули по сравнению с описанными в, за одним исключением: в оверлейных модулях нельзя использовать процедуры обработки прерываний. Желательно продумать состав модулей таким образом, чтобы минимизировать количество их перезагрузок в буфер в процессе исполнения программы.
2. В главной части программы необходимо указать с помощью директив компилятора вида {$0 < имя >} те модули, которые будут оверлейными, например:
3.
4.
Program Main;
5.
Uses CRT, DOS, Graph, Overlay, UnitA, UnitB;
6.
{$O DOS}
7.
{$O UNITA}
8.
{$O UNITB}
Учтите, что из всех стандартных библиотечных модулей только модуль DOS может быть оверлейным, остальные же модули (CRT, Graph, Printer и т.д.) не могут объявляться оверлейными.
9. Необходимо предусмотреть перед первым по логике работы программы обращением к какому-либо оверлейному модулю вызов процедуры инициализации оверлея OVERINIT. Здесь же, если это необходимо, следует установить размер оверлейного буфера и указать возможность использования расширенной памяти (см. ниже).
10. Наконец, в начале главной программы и каждого оверлейного модуля необходимо поместить директивы компилятора {$O+} и {$F+} или установить опции OPTIONS/COMPILE/FORCE FAR CALLS и OPTIONS/COMPILE/OVERLAYS ALLOWED в состояние ОN, после чего следует откомпилировать программу на диск. Программа готова к работе.
11. Необходимо использовать дальнюю модель вызова - это обязательное условие.
Директива {$O+}, строго говоря, не является обязательной. Если она указана, то при вызове любой процедуры или функции программа будет помещать все фактические параметры обращения в резидентную (неоверлейную) часть памяти, что позволяет из одного оверлейного модуля вызывать процедуры и функции любого другого оверлейного же модуля.
Процедура OVRINIT. Инициализирует оверлейный файл;
формат обращения OVRINIT(< имя >)
Здесь < имя > - выражение типа STRING, означающее имя файла с оверлейной частью программы.
При компиляции оверлейной программы создается специальный файл с именем, совпадающим с именем главной программы, и расширением .OVR. В этот файл компилятор помещает все оверлейные модули, из него же эти модули будут загружаться в оверлейный буфер в процессе исполнения программы. Файл с оверлейной частью программы должен размещаться в том же каталоге, что и файл с главной частью (с расширением .ЕХЕ).
Пример: пусть файл с главной частью программы называется MAIN.РАS, в программе используются два оверлейных модуля, помещаемые в файлы UNITA.PAS и UNITB.PAS.
Program OverlayDemo; {Текст главной программы нужно поместить в файл МAIN.РАS.}
{$F+,0+}
Uses Over1ay, UnitA, UnitB;
{$0 UnitA}
{$0 UnitB}
Begin
OvrInit('MAIN.OVR');
SubA
End.
{---------------------------------------------------------------}
UNIT UnitA; {
Текст
модуля
нужно
поместить
в
файл
UNITA.PAS}
{$F+,0+}
Interface
Uses UnitB;
Procedure SubA;
Implementation
Procedure SubA;
const
st = '
Работает
модуль
’;
Begin
Writeln(st, 'A');
SubB(st);
End;
End.
{---------------------------------------------------------------}
Unit UnitB; {
Текст
модуля
нумно
поместить
в
файл
UNITB.PAS.}
{$F+,0+}
Interface
Procedure SubB(s: string);
Implementation
Procedure SubB;
Begin
Writeln(s, 'B');
End;
End.
0бычно размер оверлейного буфера автоматически определяется таким, что в нем может разместиться самый крупный из всех оверлейных модулей. Программист может увеличить размер буфера. Тогда при загрузке в буфер очередного модуля программа проверит, достаточно ли в буфере свободного места, и, если места достаточно, загрузит новый модуль сразу за старым, который таким образом не будет уничтожен. Такой механизм способствует минимизации потерь времени на перезагрузку модулей. Если установлен очень большой размер буфера, то в нем, возможно, смогут разместиться все оверлейные модули и потери времени будут сведены к нулю, однако в этом случае оверлейная структура становится просто ненужной.
Процедура OVRSETBUF. Устанавливает больший, чем по умолчанию, размер оверлейного буфера;
формат обращения OVRSETBUF(< длина >)
Здесь < длина > - выражение типа LONGINT, определяющее новую длину буфера.
Новая длина задается в байтах и не может быть меньше той, что устанавливает сама система автоматически. Расширение буфера идет за счет соответствующего уменьшения доступной динамической памяти, поэтому к моменту вызова этой процедуры куча должна быть пустой.
Функция OVRGETBUF. Возвращает значение типа LONGINT, содержащее текущий размер кучи.
Обращение OVRGETBUF.
Если Ваша ЭВМ имеет расширенную память (общий объем памяти свыше 1024 Кбайт), Вы можете использовать эту память для размещения в ней оверлейного файла .ОVR. Поскольку время доступа к расширенной памяти значительно меньше времени чтения с диска, такое размещение увеличивает скорость исполнения оверлейной программы.
Процедура OVRINITEMS обеспечивает использование расширенной памяти.
Обращение OVRINITEMS
При обращении к этой процедуре программа прежде всего проверит, подключена ли к Вашей ЭВМ ЕМS-память (от англ. Expanded Memory Specification - расширенная память,) нужного для размещения оверлейной части объема. Если это так, то оверлейный файл будет считан в расширенную память, сам файл будет закрыт и программа будет считывать оверлейные модули из этой памяти. Если же расширенная память отсутствует или ее объем недостаточен для размещения оверлейного файла, обращение к процедуре игнорируется и программа будет считывать оверлейные модули с диска.
Прямое обращение к памяти и портам ввода-вывода
В Турбо-Паскале имеется пять предварительно объявленных массивов: МЕМ, МЕМW, МЕМL, РОRТ и РОRТW. Первые три обеспечивают доступ к любому участку оперативной памяти по абсолютному адресу, два другие - доступ к портам ввода-вывода.
Компонентами массива МЕМ являются данные типа ВYТЕ, массива МЕМW - типа WORD, массива МЕМL - типа LONGINT. Обращение к элементам этих массивов, т.е. их индексация, имеет специальный вид: каждый индекс представляет собой абсолютный адрес и состоит из двух выражений типа WORD; первое дает сегментную часть адреса, второе - смещение; выражения разделяются двоеточием:
Mem[$0000:$1000]:= 0;
DataMem:= MemW[Seg(p):Ofs(p)];
MemLong:= MemL[64:i*SizeOf(rea1)];
Как следует из технического описания операционной системы МS DOS, в ПЗУ BIOS по адресу $F000:$FFFE зашит байт-идентификатор типа компьютера. Таким образом можно определить тип компьютера, на котором запускается программа:
Program DMA_Demo;
Begin
Write('
Тип
компьютера
: ');
Case Mem[$FOOO:$FFFE] of
$FF: writeln('PC');
$FE: writeln('XT');
$FD: writeln('PCjr');
$FC: write1n('AT');
$F9: writeIn('
совместимый
c PC');
End
End.
Компонентами массива РОRТ являются байты, а массива РОRТW - слова. Индексами этих массивов должно быть выражение типа ВYТЕ, указывающее номер нужного порта. Присвоение значения элементу массива РОRТ или РОRТW приведет к записи в порт, упоминание элемента в выражении - к чтению из порта. Компоненты массивов РОRТ и РОRТW нельзя передавать в качестве параметров процедурам или функциям. Эти идентификаторы нельзя употреблять без индексных выражений.
ТурбоПаскаль 6.0 и структурное программирование.
Мы уже знаем, что на этапе проектирования архитектуры Турбо- Паскаль поддерживает модульное проектирование. Но наиболее эффективен Турбо-Паскаль на этапе детального проектирования.
Процесс проектирования программ - это процесс решения проблем человеком, подобно другим процессам решения проблем в науке и технике. Из-за ограниченных возможностей человеческом мозга, не способного одновременно охватить все мельчайшие детали, необходимо определить некоторый способ представления проекта. Это представление затем будет использоваться как средство общения. Результирующий код должен выводиться из этого представления просто и однозначно. Приемы такого представления могут быть классифицированы как графические и языковые.
К графическим приемам относят блок-схемы и диаграммы Нэсси- Шнейдерман.
Блок-схема (схема) - наиболее распространенная и наиболее понимаемая форма графического представления. Она была предложена фон Нейманом как средство документации программ. Для каждой программной структуры существует соответствующая графическая схема. Основное преимущество этого метода - простота и наглядность. Однако, у схем есть и недостатки:
- их нотации несовместимы с нотациями, используемыми в программных спецификациях и в реализациях;
- невозможен прямой ввод в ЭВМ и вывод из нее, практически мало средств автоматизированной поддержки;
- нет эффективном способа управления уровнем детализации в рамках каждой схемы;
- нотации недостаточны для проектирования крупномасштабных систем ПО.
Диаграммы Нэсси-Шнейдерман призваны поддерживать структурное программирование. Предложены специальные графические прямоугольные изображения для базовых структур. Программа описывается с использованием этих изображений. Основные характеристики:
- функциональная область хорошо определена;
- не разрешены произвольные передачи управления;
- легко определяются границы локальных и глобальных данных;
- легко представляются рекурсивные свойства.
Среди приемов языкового представления наиболее известным является использование псевдокодов или языков проектирования программ.
Язык проектирования (РDL) - это неграфическая форма представления проекта программы, характерной особенностью которой является возможность оформления шагов обработки на естественном языке с использованием специальных предопределенных слов (кодовых вставок) для описания операторов ветвлений, циклов и описания данных. Внесение изменений в алгоритм программы может быть осуществлено при помощи редактора текстов, имеющемся в составе ПО всех современных ЭВМ.
Разработка исполнительной программы заключается в преобразовании описания шагов обработки и описания данных, выполненных на естественном языке, в операторы языка программирования. Наиболее приспособленным для поддержки методологии структурного программирования, является РАSCАL. На его базе элементарно строится язык проектирования PDL-PASCAL.
Действия разработчика на каждом шаге Детализации заключаются в подстановке одной из структур, изображенных на рисунке вместо прямоугольников на схеме, полученной на предыдущем шаге. Доказано, что такая процедура детализации позволяет построить логику любом алгоритма. Выбор необходимой структуры для подстановки и составляет
суть проектирования на этом этапе.
ТурбоПаскаль и объектно-ориентированное программирование.
Основные определения типа <объект>.
Начиная с версии 5.5, Турбо-Паскаль охватывает еще один современный метод проектирования программ, описанный выше как обьектно-ориентированное проектирование.
Объект - это структура данных, содержащая поля данных (подобно записи) различных типов и заголовки методов.
Синтаксис объявления объекта:
< ИмяПотомка > = Object(< ИмяПредка >)
поле;
............
поле;
метод;
.............
метод;
End;
Метод - это процедура или функция, объявленные внутри объявления элемента типа объект и предназначенная в обычно для работы с полями этого объекта.
Формат обьявления
Procedure <ИмяМетода>(<Параметры, как у процедуры>);
Метод имеет доступ к полям данных объекта, не требуя передачи их ему в виде параметров.
Объявление метода внутри объявления объектного типа содержит только заголовок. Тело метода определяется вне объявления объекта. Его заголовок должен содержать имя объекта, которому принадлежит метод. Например:
Procedure Объект.Метод;
(< параметры >);
Begin
.......
.......
End;
Методы подразделяются на статические и виртуальные. Виртуальный метод отличается от статического тем, что реализующий его код подсоединяется к вызову не в процессе компиляции, а в процессе выполнения, что достигается так называемым поздним связыванием. Это дает возможность строить иерархию объектов с одинаковыми названиями методов, реализуемыми, однако, различными кодами.
Синаксис объявления виртуального метода:
Procedure Метод(< параметры >); virtual;
Кроме обычных процедур и функций Турбо-Паскаль 6.0 реализует два специальных типа методов: конструктор и деструктор.
Конструктор - это специальный метод, инициализирующий объект, содержащий виртуальные методы, он объявляется специально зарезервированным словом constructor.
Синтаксис:
constructor Init(< параметры >);
Конструктор инициализирует объект установлением связи между объектом и специальной таблицей виртуальных методов, содержащей адреса кодов, реализующих виртуальные методы. Конструктор может также использоваться для инициализации полей данных объекта.
Деструктор - это специальный метод, освобождающий память кучи от динамических объектов. Он объявляется с использованием специально зарезервированного слова destructor.
Синтаксис:
destructor Done;
Основные свойства объектов.
Основными отличительными свойствами объекта являются:
- инкапсуляция - объединение записей с процедурами и функциями, работающими с этими записями;
- наследование - задание объекта, затем использование его для построения иерархии порожденных объектов с наследованием доступа каждом из порожденных объектов к коду и данным предка;
- полиморфизм - задание одного имени действию, которое передается вверх и вниз по иерархии объектов, с реализацией этом действия способом, соответствующим каждому объекту в иерархии.
Рассмотрим смысл каждого из приведенных свойств.
Инкапсуляция
Допустим, наши практические интересы лежат в области построения изображений тел звездном неба в двумерной проекции. Очевидно, что основой всякого изображения является положение (позиция) отдельного элемента на экране, описываемая координатами Х и У. Для задания двумерной позиции подходит тип запись, имеющйся в Турбо- Паскале.
Position = Record
Х
: Integer;
Y: Integer;
End;
Что можно делать с парой координат (Х,У) ?
Во-первых, может потребоваться задать значения координат (в программировании такая процедура носит название инициализации). Создадим соответствующую процедуру:
Procedure Init(СоordХ, СооrdУ: Integer);
Begin
Х:= СооrdX;
Y:= СооrdY.,
End;
Во-вторых, нам может потребоваться знание фактических значений координат, для этом вводим две функции:
Function GetX: integer;
Begin
GetX:= X;
End;
{---------------------------}
Function GetY: integer;
Begin
GetY:= Y;
End;
По нашему замыслу процедура Init и функции GetХ и GetY должны работать только с полями записи Pozition. Введение объектов в Паскаль позволяет зафиксировать зто положение, объявив и поля и действия в одном месте:
Pozition = Object
X: Integer;
Y: Integer,
Procedure Init(CoordX, CoordY: Integer);
Function GetX: Integer;
Function GetY: Integer;
End;
Теперь для инициализации экземпляра типа Pozition достаточно вызвать его метод, как если бы он был полем записи:
Var
FirstPozition: Pozition;
...................
Begin
FirstPozition.Init(10,15);
...................
Метод задается так же, как и процедура в модуле: внутри объекта записывается заголовок (как в секции Interface модуля), при этом все поля, используемые методом, должны предшествовать ем объявлению. Определение метода (расшифровка действий) происходит вне объявления объекта. Имя метода должно предваряться названием типа объекта, которому метод принадлежит, сопровождаемым точкой.
Procedure Pozition.Init(CoordX, CoordY: Integer);
Begin
X:= CoordX;
Y:= CoordY;
End;
Заметим, что имена формальных параметров метода не могут совпадать с именами полей данных объекта.
Также как модуль защищает детали реализации процедур от пользователя, объект может защищать свои поля и методы. Для этом используется ключевое слово private (личный), Личные поля и методы доступны только внутри метода. Объявление выглядит следующим образом:
Type
ObjectNam = Object
поле;
..........
поле;
метод;
..........
метод;
private
ЧастноеПоле;
..........
ЧастноеПоле;
ЧастныйМетод;
..........
ЧастныйМеетод;
End;
Наследование.
Рассмотрим звезду с координатами Х и У. Ее можно сделать видимой или невидимой, ей можно задать цвет, ее можно переместить.
Создадим объект с такими возможностями:
Star = Object
X: Integer;
Y: Integer;
Procedure Init(CoordX, CoordY: Integer);
Function GetX: Integer;
Function GetY: Integer;
Visible: Boolean;
Color: Word;
Procedure Init(CoordX, CoordY: Integer; InitColor: Word);
Function IsVisible: Boolean;
Procedure Show; {
зажигает
звезду
}
Procedure Blind; {
гасит
звезду
}
Procedure Jump(NextX, NextY: Integer); {
перемещает
звезду
}
End;
Заметим, однако, что поля Х,У и методы GetХ, GetУ практически совпадают с соответствующими полями и методами обьекта Pozition.
&nnsp Турбо-Паскаль предоставляет возможность учесть эту ситуацию. Следует считать тип объекта Star порожденным типом Pozition, записав это следующим образом (наследование):
Star = Object(Pozition)
Visible: Boolean;
Color: Word;
Procedure Init(CoordX, CoordY: Integer; InitColor: Word);
Function IsVisible: Boolean;
Procedure Show;
Procedure Blind;
Procedure Jump(NextX, NextY: Integer);
End;
Объект Star теперь наследует свойства объекта Pozition. Поля Х,У явно не заданы в Star, но Star ими обладает благодаря наследованию, т.е. можно написать:
Star.X:=17;
Смысл обьектно-ориентированного программирования заключается именно в работе с полями объекта через его методы.
Полиморфизм.
Давайте создадим объект "планета". Очевидно, что новый объект должен иметь предком объект Star, обладая всеми его свойствами, кроме того, быть "больше" по размеру (точнее - видимому размеру). Однако, даже начинающему программисту ясно, что нарисоавть на экране точку и закрашенную окружность не удастся одними и теми же командами.
Турбо-Паскаль разрешает сохранить потомку имя родительского метода, "перекрывая" его. Чтобы перекрыть родительский метод, нужно просто задать его с тем же именем, но с другим телом (кодом) и, если необходимо, с другим набором параметров. Такой метод делается виртуальным и к его объявлению добавляется слово virtual. Применение виртуальных методов налагает ограничения на процедуры инициализации, которые должны записываться с зарезервированным словом constructor и иметь общее имя Init.
Каждый отдельный экземпляр объекта должен инициализироваться с помощью отдельного вызова конструктора.
Для очистки и убирания динамически распределенных объектов существует специальная процедура - destructor Done.
Деструктор комбинирует шаг освобождения памяти в "куче" с некоторыми другими задачами. Метод деструктора может быть пустым, поскольку работу выполняет не только код тела, но и код, генерируемый Турбо-Паскалем в ответ на зарезервированное слово destructor.
Объектно-ориентированная библиотека Turbo Vision
Основные определения
Turbo Vision - это оболочка оконной программы, управляемой событиями, включающая:
- многократные перекрывающиеся окна с изменяемыми размерами,
- выпадающие меню,
- поддержку мышки,
- диалоговые окна,
- встроенную установку цвета,
- кнопки, полосы скроллинга, окна ввода, зависимые и независимые кнопки,
- стандартную обработку клавиш и нажатий мышки.
Turbo Vision использует понятия видимых элементов, событий и невидимых объектов.
Видимый элемент - это любой элемент программы прямоугольной формы, который виден на экране: поля, рамки окон, полосы скроллинга, полосы меню и диалоговые окна. Только видимые элементы могут взаимодействовать с дисплеем. Видимые элементы могут объединяться для формирования более сложных элементов таких, как окна и диалоговые окна. Эти наборы видимых элементов называются группами и они работают вместе так, как если бы это был один элемент.
Невидимые объекты - это любые другие объекты программы, которые ничего не выводят на экран.
Событие - это внешний фактор, заставляющий реагировать Вашу программу: нажатие клавиши, нажатие кнопки мышки, какие либо другие события в системе компьютера.
Встроенная справочная система
Если Вы забыли, как точно пишется имя процедуры, какие аргументы ей требуются, то ИПО может помочь, предоставив в Ваше распоряжение свою справочную систему. Ее можно вызвать, нажав клавишу [F1]. Справка, которую выдает система, является контекстно-зависимой. Это означает, что Вы получите сообщение об объекте, указываемом курсором в тексте программы или в меню, либо сообщение о текущей ситуации в системе. Передвигая курсор по тексту справки, выбирая те или иные отмеченные элементы и нажимая затем клавишу [Enter], можно получить более подробную информацию. Целесообразно пользоваться справочной системой и при появлении сообщений об ошибках. Это избавляет от необходимости выяснять их причины в руководствах и справочниках.
Во время работы с редактором текста в ТП 7.0 Вы можете с помощью комбинации [Ctrl+Fl] или правой кнопки мыши (для получения справки необходимо активизировать правой кнопкой мыши локальное меню и вызвать команду Topic\search) получить справочную информацию об операторе программы, на который указывает курсор. Установите курсор на строку, содержащую оператор WriteLn (непосредственно на само имя), и, используя комбинацию [Ctrl+Fl], активизируйте справочную систему ТП 7.0. Вы получите справку об операторе WriteLn.
Если курсор не находится на зарезервированном слове и транслятор ошибок не обнаружил, то нажатие комбинации клавиш [Ctrl+Fl] приводит к выдаче индексной страницы (предметного указателя справок), из которой можно вызвать справочную информацию о любой процедуре или функции, даже если ее имя неточно указано в тексте программы (или вообще там отсутствует).
Если Вы уже вызвали справку, а затем еще раз нажали клавишу [F1], Вы попадете в меню доступных справок. Это меню построено по иерархическому принципу и организовано как дерево, передвижение по которому в направлении корня осуществляется с помощью клавиш [Alt+Fl], а в противоположную сторону - выбором в справках конкретного термина, подлежащего более подробному истолкованию.