Download - Комплексное пособие
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
ПРИОРИТЕТНЫЙ НАЦИОНАЛЬНЫЙ ПРОЕКТ «ОБРАЗОВАНИЕ»
КАЗАНСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
ИМ. А.Н.ТУПОЛЕВА
И.Ю. ЕЛИСЕЕВА, К.В. ШЕРШУКОВ
ОСНОВЫ ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ PASCAL
(Программирование на языке высокого уровня, часть 1)
Комплексное учебное пособие
Казань – 2009
УДК 681.3.06
Елисеева И.Ю., Шершуков К.В.
Основы программирования на языке Pascal: Учебное пособие. – Казань: Изд-
во Казан. техн. ун-та, 2009. – 178 с.
Излагаются основы алгоритмизации и программирования на языке
высокого уровня на примере языка Pascal.
Учебное пособие содержит краткое описание основных средств языка
Pascal, примеры различных программ. Даются указания по самостоятельной
работе студентов и примеры заданий на лабораторные работы. Изложение
рассчитано на начинающих изучение программирования.
Для студентов направления подготовки бакалавров «Информатика и
вычислительная техника» и других лиц, изучающих программирование.
Табл. - , Ил. - , Библиогр. - назв.
© Елисеева Инна Юрьевна, 2009
© Шершуков Кирилл Вячеславович, 2009
2
Содержание
Предисловие.............................................................................................................4
Программирование на языке Pascal.......................................................................5
Эволюция языков программирования................................................................5
Алгоритмы...........................................................................................................15
Язык Turbo Pascal...............................................................................................16
Основные типы данных. Ввод-вывод...............................................................21
Операторы языка Паскаль.................................................................................39
Структурированные типы. Массивы................................................................51
Тип данных String...............................................................................................60
Структурированный тип данных - множество................................................72
Процедуры и функции.......................................................................................78
Работа с файлами................................................................................................89
Указатели и динамическая память..................................................................109
Модули Turbo Pascal........................................................................................127
Общая характеристика базовых графических средств языка Pascal...........142
Задания на лабораторные работы......................................................................152
Лабораторная работа №1. Основные приемы работы в среде Turbo Pascal
............................................................................................................................152
Лабораторная работа №2. Типы данных, выражения, ввод/вывод.............166
Лабораторная работа №3. Циклы...................................................................168
Лабораторная работа №4. Одномерные массивы.........................................171
Лабораторная работа №5. Матрицы..............................................................173
Лабораторная работа №6. Процедуры и функции.......................................176
Лабораторная работа №7. Множества, строки и записи.............................182
Лабораторная работа №8. Файлы..................................................................184
Контрольные вопросы.........................................................................................186
Литература...........................................................................................................191
3
Предисловие
Комплексное учебное пособие призвано обеспечить подготовку по
дисциплине «Программирование на языке высокого уровня» для студентов
вузов, обучающихся по направлению подготовки бакалавров «Информатика
и вычислительная техника».
Основу учебного пособия составляет расширенный конспект лекций по
программированию, много лет под разными названиями читаемых авторами
для студентов первого курса на кафедре прикладной математики и
информатики Казанского государственного технического университета
(КГТУ – КАИ).
Излагается теоретический материал лекционного курса, примеры
алгоритмов и программ, методические указания по самостоятельной работе
студентов примеры заданий на лабораторные работы. В качестве базового
языка используется язык Turbo Pascal.
Пособие используется совместно с пособием «Основы
программирования на языке С» (авторы А.Р. Бикмурзина, К.В. Шершуков). В
первом семестре изучается язык Pascal, в следующем семестре - язык C.
4
Программирование на языке Pascal
Эволюция языков программирования
История языков развития программирования тесным образом связана с
историей вычислительных машин. Даже простые арифметические операции с
большими числами затруднительны для человеческого мозга. Поэтому уже в
древности появилось простейшее счетное устройство - абак. В семнадцатом
веке была изобретена логарифмическая линейка. В 1642 году Блез Паскаль
сконструировал восьмиразрядный суммирующий механизм. В 1820 году
француз Шарль де Кольмар создал арифмометр, способный производить
умножение и деление.
Все основные идеи, которые лежат в основе работы компьютеров, были
изложены еще в 1833 году английским математиком Чарльзом Бэббиджем
(1791-1871). Он разработал проект машины для выполнения научных и
технических расчетов, где предугадал основные устройства современного
компьютера, а также его задачи. Управление такой машиной должно было
осуществляться программным путем. Для ввода и вывода данных Бэббидж
предлагал использовать перфокарты. В то время перфокарты уже
использовались в текстильной промышленности.
Первый язык программирования разработала Лавлейс Огаста Ада Кинг
(1815 – 1852) для машины Бэбиджа.
В комментариях к описанию машины, изданном в 1843 году, введены
понятия «операция», «переменная», «условный переход», «цикл». Приведена
программа для решения системы двух линейных уравнений с двумя
неизвестными
5
В 1888 году американский инженер Герман Холлерит сконструировал
первую электромеханическую счетную машину. Эта машина, названная
табулятором, могла считывать и сортировать статистические записи,
закодированные на перфокартах. В 1890 году изобретение Холлерита было
впервые использовано в 11-й американской переписи населения. Работа,
которую пятьсот сотрудников выполняли в течение семи лет, Холлерит
сделал с 43 помощниками на 43 табуляторах за один месяц.
В 1896 году Герман Холлерит основал фирму Computing Tabulating
Recording Company, которая стала основой для будущей International Business
Machines Corporation (IBM).
В 1946 в США была создана первая электронная вычислительная
машина (ЭВМ) - ENIAC (Electronic Numerical integrator and Computer -
Электронный числовой интегратор и компьютер). Разработчики: Джон Мочи
(John Маuchу) и Дж. Преспер Эккерт (J. Prosper Eckert).
Он был произведен на свет в Школе электрической техники Moore (при
университете в Пенсильвании).
Время сложения - 200 мкс, умножения - 2800 мкс и деления - 24000 мкс.
Компьютер содержал 17468 вакуумных ламп шестнадцати типов, 7200
кристаллических диодов и 4100 магнитных элементов. Общая стоимость
базовой машины - 750000 долларов.
В Советском Союзе первая электронная цифровая вычислительная
машина была разработана в 1950 году под руководством академика
С.А. Лебедева в Академии наук Украинской ССР. Она называлась «МЭСМ»
(малая электронная счётная машина).
Основоположниками компьютерной науки по праву считаются Клод
Шеннон - создатель теории информации, Алан Тьюринг - математик,
разработавший теорию программ и алгоритмов, и Джон фон Нейман - автор
6
конструкции вычислительных устройств, которая до сих пор лежит в основе
большинства компьютеров. В те же годы возникла еще одна новая наука,
связанная с информатикой, - кибернетика, наука об управлении как одном из
основных информационных процессов. Основателем кибернетики является
американский математик Норберт Винер.
Развитие ЭВМ делится на несколько периодов. Поколения ЭВМ каждого
периода отличаются друг от друга элементной базой и математическим
обеспечением. Первое поколение (1945-1954) - ЭВМ на электронных лампах.
Их быстродействие не превышало 2—3 тыс. операций в секунду,
оперативная память не превышала 2 Кб.
Машинно-зависимые языки программирования низкого уровня –
двоичные машинные коды, Автокод, языки Ассемблера.
Таблица 1.
Год Язык (идея) Создатель ЭВМ Заметки о языке
46 Плюнкалкюль Цузе Z4 Немецкий инженер Цузе разработал этот язык
для ЭВМ Z4, но ни компилятора, ни
интерпретатора для него написано не было
(поскольку и язык, и ЭВМ он разрабатывал в
одиночку).
49 "Краткий код" Мочли Юнивак По своей сути, это была система обозначений
для быстрого ввода в ЭВМ алгебраических
выражений."Краткий код" работал как
примитивный интерпретатор.
49 Идея
подпрограммы
Грейс
Хоппер
Марк-1 Программисты под руководством Хоппер
впервые во всем мире реализовали идею
подпрограмм как многократно используемую
последовательность команд. Тогда они
просто переписывали куски машинного кода
друг у друга из блокнотов.
7
50 Ассемблер Грейс
Хоппер
Эдсак Первый ассемблер представлял из себя
систему мнемонических обозначений,
объединённую с библиотекой подпрограмм
52 АВТОКОД Гленн Марк-1 Автокод был первым полноценным
транслятором (компилятором), т.е. он
транслировал мнемоническую схему в
машинные коды.
52 А-0 Грейс
Хоппер
Юнивак А-0 был первым полноценным ассемблером,
получившим широкую известность. Он
предоставлял услуги как компилятора, так и
компоновщика.По сути, А-0 представлял
первое промышленное средство для создания
других программ.
56 B-0 Грейс
Хоппер
Юнивак Хоппер создала первый ассемблер, который
понимал мнемонические команды.
ЭВМ 2-го поколения были разработаны в 1950—60 гг. В качестве
основного элемента были использованы уже не электронные лампы, а
полупроводниковые диоды и транзисторы, а в качестве устройств памяти
стали применяться магнитные сердечники и магнитные барабаны - далекие
предки современных жестких дисков. Второе отличие этих машин — это то,
что появилась возможность программирования на алгоритмических языках.
Были разработаны первые языки высокого уровня - Фортран, Алгол, Кобол.
Таблица 2.
Год Язык (идея) Создатель ЭВМ Заметки о языке
54 FORTRAN Бэкус IBM-704 Был первым языком программирования
высокого уровня. Разрабатывался компанией
IBM для её новой ЭВМ.Коллектив под
руководством Бэкуса разработал не только
спецификацию языка, но и создал компилятор
8
для него. Это обеспечило достаточно быструю
и широкую распространенность языка.Кроме
того, Фортран был первым языком, для
которого были разработаны компиляторы для
разных ЭВМ (т.е. программа могла
выполняться на различных ЭВМ).Лучше всего
Фортран подходит для решения инженерных и
расчетных задач.
58 ALGOL – 58 Первая спецификация Алгола была рождена
европейским международным комитетом.
59 “CSC” – первая
компания по
разработке ПО
В 59 году в США появилась первая компания,
занимающаяся созданием промышленных
программ.
59 БНФ Бэкус, Наур Для описания спецификации Алгола Бэкус
создал специальную систему – “нормальную
форму”. Чуть позже эта система была
уточнена Науром.
59 CODASYL Министерство обороны США создало
организацию CODASYL, главным
назначением которой было создание нового
языка, ориентированного на бизнес –
приложения.
60 ALGOL – 60 Бэкус Новая спецификация Алгола, которую
отличали следующие идеи:
Блочная структура
Возможность рекурсии
Описание с использованием БНФ
59 COBOL CODASYL Стандарт языка, принятый для создания
коммерческих приложений
58 Lisp Маккарти Язык для исследований в области
искусственного интеллекта, предназначенный
для работы со списками и лямбда –
выражениями (был создан в МТИ).
9
Разработка в 60-х годах интегральных схем - целых устройств и узлов из
десятков и сотен транзисторов, выполненных на одном кристалле
полупроводника (то, что сейчас называют микросхемами) привело к
созданию ЭВМ 3-го поколения. В это же время появляется
полупроводниковая память, которая и по сей день используется в
персональных компьютерах в качестве оперативной.
Таблица 3.
Год Язык (идея) Создатель ЭВМ Заметки о языке
64 ПЛ/1 Рэдин (IBM) Универсальный язык программирования,
разрабатывавшийся для системы System/360.
64 БЭЙСИК Кемени,
Курц
Был разработан в Дартмундском колледже
как язык для обучения работе с
компьютером студентов всех
специальностей (и технических, и
гуманитарных). Начал разрабатываться в 59-
60х годах, когда существовал только
Фортран. Отличия от Фортрана:
o операторы ввода/вывода
o единая система чисел (без различия
на целые/вещественные)
Одновременно с разработкой языка велась
разработка ЭВМ, которая работала бы не в
пакетном режиме, а в режиме разделения
времени (отсюда – необходимость
операторов ввода. Позже появится и
структурно-ориентированный стандарт
TrueBASIC.
66 АЛГОЛ – W Хоар, Вирт Новый стандарт языка, который был
предложен Хоаром и Виртом как
альтернатива существующей спецификации
(буква W – от фамилии Вирт).
68 АЛГОЛ – 68 Этот вариант Алгола очень не понравился
10
Вирту.
68 Статья о
структурном
программирован
ии
Дейкстра В этой статье Дейкстра доказывал вредность
оператора “GOTO” (т.к. он нарушает
последовательность операторов) и
показывал, что любая программа может быть
написана с использованием 3-х структур:
Последовательность
Альтернатива
Повторение
69 AПЛ Кеннет
Айверсон
(IBM)
Очень компактный универсальный язык
программирования. Именно для этого языка
была организация ACM открыла первую
SIG(Software Interest Group).
70 ПАСКАЛЬ Никлаус
Вирт
Был разработан Виртом как
противопоставление Алголу-68. Язык с
высокой структурированностью. До сих пор
– лучший язык для обучения
программированию.
71 ФОРТ Чарльз Мур Самый компактный язык программирования.
Обладает высокой степенью расширяемости.
Первое промышленное применение –
управление радиотелескопами.
72 С Керниган,
Ричи,
Bell Lab.
Язык был разработан для написания
операционной системы UNIX. В рекламных
целях UNIX в университетах
распространялся почти бесплатно, вместе с
UNIX’ом распространялся и С. До сих пор С
– один из лучших языков для системного
программирования.
72 ПРОЛОГ Алан
Колмери
Логический язык программирования, в
котором описываются факты и отношения
между ними.
75 HOLWG Уайтекер HOLWG – рабочая группа по управлением
Уайтекера, была создана Пентагоном для
получения языка, который мог бы
11
использоваться на всех компьютерах
министерства обороны США. Результатом в
79 году стал язык Ада.
76 UCSD Pascal Кеннет Боулз Apple II Введены модули
79 АДА Жан Ихбиа Универсальный язык программирования,
был разработан по заказу МО США. После
создания вышло требование переписать все
существующие приложения на Аде.
81 Turbo Pascal Андерс
Хейлсберг
Обычно считается, что период с 1975 г. принадлежит компьютерам
четвертого поколения. Их элементной базой стали большие интегральные
схемы (БИС. В одном кристалле интегрировано до 100 тысяч элементов).
Быстродействие этих машин составляло десятки млн. операций в секунду, а
оперативная память достигла сотен Мб. Появились микропроцессоры (1971 г.
фирма Intel), микро-ЭВМ и персональные ЭВМ. Стало возможным
коммунальное использование мощности разных машин (соединение машин в
единый вычислительный узел и работа с разделением времени).
Таблица 4.
Год Язык
(идея)
Создатель ЭВМ Заметки о языке
85 С++ Бьярн
Страуструп
Объектно-ориентированное расширение языка С
85 Object
Pascal
Ларри
Теслер,
Никлаус
Вирт
Apple
86 Perl Ларри Уолл Язык создавался в помощь системному
администратору операционной системы Unix для
обработки различного рода текстов и выделения
нужной информации. Развился до мощного средства
12
работы с текстами. Является интерпретируемым
языком и реализован практически на всех
существующих платформах. Применяется при
обработке текстов, а также для динамической
генерации веб-страниц на веб-серверах.
87 Oberon Никлаус
Вирт
Оберон отличается от Модулы-2 отсутствием многих
необязательных конструкций; добавлены же в язык
средства объектно-ориентированного
программирования -- расширяемые записи. Оберон --
это самый простой универсальный язык.
91 Python Интерпретируемый объектно-ориентированный язык
программирования. По структуре и области
применения близок к Perl, однако менее
распространен и более строг и логичен.
92 Oberon-2 Никлаус
Вирт В 1992 году были приняты расширения языка
Оберон, предложенные Ханспетером Мёссенбёком.
Расширенный язык получил название Оберон-2.
Основное нововведение -- связанные с типами
процедуры. Сейчас Оберон-2 является фактическим
стандартом языка
93 Delphi Андерс
Хейлсберг
Delphi 1 был первым инструментарием разработки
Windows приложений, объединившим в себе
оптимизирующий компилятор, визуальную среду
программирования и мощные возможности работы с
базами данных.
95 PHP Расмус
Лердорф Cкриптовый язык программирования общего
назначения, интенсивно применяющийся для
создания веб-приложений. В настоящее время
поддерживается подавляющим большинством
хостинг-провайдеров и является одним из лидеров
среди языков программирования, применяющихся
для создания динамических веб-сайтов.
96 Java Кен Арнольд,
Джеймс Наследует синтаксис C и C++ и избавлен от
некоторых неприятных черт последнего.
13
Гослинг Отличительной особенностью языка является
компиляция в код некоей абстрактной машины, для
которой затем пишется эмулятор (Java Virtual
Machine) для реальных систем. Кроме того, в Java нет
указателей и множественного наследования, что
сильно повышает надежность программирования.
99 C# Андерс
Хейлсберг
Основной язык разработки приложений для
платформы Microsoft .NET. Компилятор с C# входит
в стандартную установку самой .NET, поэтому
программы на нём можно создавать и компилировать
даже без инструментальных средств, вроде Visual
Studio. C# относится к семье языков с C-подобным
синтаксисом, из них его синтаксис наиболее близок к
C++ и Java. Язык имеет статическую типизацию,
поддерживает полиморфизм, перегрузку операторов
(в том числе операторов явного и неявного
приведения типа), делегаты, атрибуты, события,
свойства, обобщённые типы и методы, итераторы,
анонимные функции с поддержкой замыканий, LINQ,
исключения, комментарии в формате XML.
ЭВМ пятого поколения — это ЭВМ будущего. Программа разработки,
так называемого, пятого поколения ЭВМ была принята в Японии в 1982 г.
Предполагалось, что к 1991 г. будут созданы принципиально новые
компьютеры, ориентированные на решение задач искусственного интеллекта.
С помощью языка Пролог и новшеств в конструкции компьютеров
планировалось вплотную подойти к решению одной из основных задач этой
ветви компьютерной науки - задачи хранения и обработки знаний. Коротко
говоря, для компьютеров пятого поколения не пришлось бы писать
программ, а достаточно было бы объяснить на "почти естественном" языке,
что от них требуется. Предполагается, что их элементной базой будут
служить не СБИС, а созданные на их базе устройства с элементами
14
искусственного интеллекта. Для увеличения памяти и быстродействия будут
использоваться достижения оптоэлектроники и биопроцессоры.
К сожалению, японский проект ЭВМ пятого поколения повторил
трагическую судьбу ранних исследований в области искусственного
интеллекта. Более 50-ти миллиардов йен инвестиций были потрачены
впустую, проект прекращен, а разработанные устройства по
производительности оказались не выше массовых систем того времени.
Однако, проведенные в ходе проекта исследования и накопленный опыт по
методам представления знаний и параллельного логического вывода сильно
помогли прогрессу в области систем искусственного интеллекта в целом.
Алгоритмы
Слово «алгоритм» происходит от имени узбекского математика Хорезми
(по арабски ал - Хорезми), который в IX веке разработал правила 4-х
арифметических действий над числами в десятичной системе счисления.
Алгоритм - точное предписание по выполнению ограниченного числа
определенных действий, последовательно преобразующих исходные данные
в конечный результат.
Совокупность действий по преобразованию исходных данных в
конечный результат составляет процесс выполнения алгоритма, называемый
вычислительным процессом или просто процессом. Тот кто выполняет
алгоритм называется процессор (человек или часть ЭВМ). Алгоритм,
предназначенный для выполнения ЭВМ называется программой.
Алгоритм обладает следующими свойствами:
дискретностью - алгоритм состоит из конкретных действий;
определенностью - точностью, не оставляющей место для произвола,
т.е. алгоритм понятен для возможных исполнителей благодаря этому
свойству процесс выполнения алгоритма носит механический характер;
15
результативностью - свойством приводить к искомому результату за
конечное число действий (шагов);
Массовостью - пригодностью для решения задач из некоторого класса.
Пример
Дано X, Вычислить 3x2+6x+2. Допустимые операции - сложение и
умножение 2-х чисел. Т.о. есть предпосылки для разработки алгоритма:
исходные данные - X;
действия - сложение, умножение, которое применительно к выражению
3x2+6x+2 дадут искомый результат.
Т.к. среди действий нет возведения в степень выражение необходимо
записать в виде 3xx+6x+2.
Получим следующий алгоритм:
начало;
вычислить X´X, результат обозначить через Y;
вычислить 3´4, результат обозначить через Z;
вычислить 6´X, результат обозначить через W;
вычислить Z+W, результат обозначить через U;
вычислить U+2, результат обозначить через S;
конец;
Язык Turbo Pascal
Язык программирования Pascal это:
Алгоритмический язык программирования высокого уровня
Разработан Никлаусом Виртом в 1970 году для обучения студентов
программированию.
Хорошо структурированный язык с жестким контролем типов данных
В дальнейшем под языком Pascal будем подразумевать Turbo Pascal.
Любой алгоритмический язык программирования состоит из следующих
элементов:
Алфавит
16
Лексемы
o Идентификаторы
o Константы
o Знаки операций и разделители
o Комментарии
Выражения
Типы данных и операции
Операторы
Структура программы
Алфавит
Алфавит языка: кодовая таблица ASCII. Для оригинального языка
Pascal:
1. латинские буквы и символ подчеркивания: ABC...Zabc...z_
2. арабские цифры: 012...9
3. 22 специальных символа: +-*/=><.,:;@')(][}{#$^
Символы алфавита используются для построения базовых элементов
Pascal-программ - минимальных единиц языка, имеющих самостоятельный
смысл. Базовые элементы также называют лексемами.
Лексемы
4. Служебные (зарезервированные) слова.
5. Имена. Они вводятся для обозначения в программе переменных, констант,
типов, процедур и функций.
6. Числа и символьные строки.
7. Знаки операций и разделители. Они формируются из одного или
нескольких специальных символов.
8. Комментарии - произвольная последовательность символов (не
обязательно из алфавита языка, то есть допускаются и русские буквы),
заключенную в фигурные скобки { }.
17
Идентификаторы
Правило составления идентификаторов: первый символ – латинская
буква, остальные – латинские буквы, цифры или символ подчеркивания
Определите, какие выражения являются правильным идентификатором:
3Yfgh
V x
R_36%
Fgh23_
&A
Выражения
Выражения могут содержать, переменные, константы, вызовы функций,
знаки операций, круглые скобки. Записываются в соответствии с правилами
использования операций
Определите, какие выражения записаны правильно:
A+B
Sin(x)*(2+y)
D(+2/)c
‘A’*2
Операторы
В языке Pascal предусмотрены следующие операторы:
оператор присваивания
составной оператор
пустой оператор
условный оператор
оператор варианта
оператор цикла с предусловием
18
оператор цикла с постусловием
оператор цикла с параметром
присоединения
оператор процедуры
Оператор присваивания
Самым простым действием над переменной является занесение в нее
величины соответствующего типа. Иногда говорят об этом, как о присвоении
переменной конкретного значения. Такая команда (оператор) в общем виде
выглядит на языке Паскаль следующим образом:
<Имя переменной>:=<Выражение>;
Выражение, указанное справа от знака ":=", должно приводить к
значению того же типа, какого и сама переменная, или типа, совместимого с
переменной относительно команды присваивания. Например, переменной
типа Real можно присвоить значение типа Integer или Word (впрочем,
наоборот делать нельзя). Выражение будет сначала вычислено, затем, его
результат будет положен в ячейки памяти, отведенные для переменной.
Структура программы
[Program <Имя программы>;]
[Uses <Имена подключаемых модулей>;]
[Label <Список меток>;]
[Const <Объявления констант>;]
[Type < Объявления типов>;]
[Var < Объявления переменных>;]
[<Раздел подпрограмм>]
Begin
[<Операторы>]
19
End.
Разделы, заключенные в квадратные скобки, являются необязательными.
Таким образом, минимальная синтаксически допустимая программа
выглядит следующим образом:
Begin
End.
Нетрудно догадаться, что эта программа будет компилироваться и
запускаться (сразу же завершая свою работу), но никаких действий
выполнять не будет.
Более осмысленный пример на языке Pascal:
program Summa;
var
A,B,Sum : integer;
begin
Wгitе(’Введите два числа: ‘);
Readln(A,B);
Sum :=А+В;
Wгitеln(’Сумма равна’,Sum);
Wгitе(’Нажмите Enter’);
Readln;
end.
Для запуска программы из среды Turbo Pascal выберите команду Run в
меню Run. Вы попадете в окно пользователя, появится сообщение:
Введите два числа:
Наберите два любых целых числа с пробелом между ними и нажмите
Enter. Появится следующее сообщение:
Сумма равна
а за ним - сумма двух чисел. В следующей строке появится сообщение:
20
Нажмите клавишу Enter
Программа будет ожидать нажатия клавиши Епtег. Для наблюдения за
выводом из своей программы, выберите команду User Screen в меню Window
(или нажмите Аlt-F5).
Основные типы данных. Ввод-вывод
Любые данные, т.е. константы, переменные, значения функций или
выражения, в турбо Паскале характеризуются своими типами. Тип
определяет множество допустимых значений, которые может иметь тот или
иной объект, а также множество допустимых операций, которые применимы
к нему. Кроме того, тип определяет также и формат внутреннего
представления данных в памяти ПК.
Операции языка Pascal и их приоритеты
На этом рисунке приведены группы операций в порядке убывания их
приоритетов. Внутри группы приоритет операций одинаков.
Объявление переменных
var
Операции языка Pascal
Унарные
Бинарные
Аппликативные
Мультипликативные
Операции отношения
21
<имя переменной>:<имя типа1>;
<имя1>[,<имя2>,…]:<тип2>;
var
a,b:integer;
ch:char;
Типы данных
Турбо Паскаль характеризуется разветвленной структурой типов
данных.
В Турбо Паскале предусмотрен механизм создания новых типов данных,
благодаря чему общее количество типов, используемых в программе, может
быть сколь угодно большим.
22
Простые типы
К простым типам относятся порядковые и вещественные типы.
Порядковые типы отличаются тем, что каждый из них имеет конечное
число возможных значений. Эти значения можно определенным способом
упорядочить и, следовательно, с каждым из них можно сопоставить
некоторое целое число – порядковый номер значения.
Вещественные типы, строго говоря, тоже имеют конечное число
значений, которое определяется форматом внутреннего представления
вещественного числа. Однако количество возможных значений
Типы данных
Простые
Структурированные
Порядковые
Вещественные
Символьный
Целочисленные
Перечисление
Логический
Диапазон
Массивы
Строки
Записи
Файлы
Указатели
23
вещественных типов настолько велико, что сопоставить с каждым из них
целое число (его номер) не представляется возможным.
Порядковые типы
К порядковым типам относятся целые, логические, символьный,
перечисляемый и тип-диапазон. К любому из них применима функция
ORD(X), которая возвращает порядковый номер значения выражения Х. Для
целых типов функция ORD(X) возвращает само значение Х, т.е. ORD(X)=X
для Х, принадлежащего любому целому типу. Применение ORD(X) к
логическому, символьному и перечисляемому типам дает положительное
целое число в диапазоне от 0 до 1(логический тип), от 0 до 255(символьный),
от 0 до 65535(перечисляемый). Тип-диапазон сохраняет все свойства
базового порядкового типа, поэтому результат применения к нему функции
ORD(X) зависит от свойств этого типа.
К порядковым типам можно также применять функции:
PRED (X) – возвращает предыдущее значение порядкового типа(значение
которое соответствует порядковому номеру ORD (X)-1 то есть
ORD(PRED(X))=ORD(X)-1
SUCC (X) - возвращает следующее значение порядкового типа, которое
соответствует порядковому номеру ORD (X)+1 т.е.
ОRD(SUCC(X)=ORD(X)+1.
Например, если в программе определена переменная
Var
C: Char;
.........
с:=’5’;
то функция PRED(C) вернет значение ‘4’, а функция SUCC(C) значение ‘6’.
Если представить себе любой порядковый тип как упорядоченное
множество значений, возрастающих слева направо и занимающих на
24
числовой оси некоторый отрезок, то функция PRED(C) не определена для
левого, а SUCC(X) для правого конца отрезка.
Целые типы.
Диапазон возможных значений целых типов зависит от их внутреннего
представления, которое может занимать один, два или четыре байта. В табл.
1 приводится название целых типов, длина их внутреннего представления в
байтах и диапазон их возможных значений.
Таблица 5.
Длина, байт Название типа Диапазон значений
1 Byte 0..255
1 Shorting -128.127
2 Word 0...65535
2 Integer -32768...32767
4 longint -2147483648...
2147483647
При использовании процедур и функций с целочисленными
параметрами следует руководствоваться «вложенностью типов», т.е. везде,
где может использоваться WORD, допускается использование BYTE(но не
наоборот), в LONGINT «входит» INTEGER, который, в свою очередь
включает в себя SHORTINT.
Перечень процедур и функций, применимых к целочисленным типам
приведен в таблице 2. Буквами b, s, w, i, l обозначены выражения
соответственно типа BYTE, SHORTINT, WORD, INTEGER, LONGINT, x –
выражение любого из этих типов; буквы vb, vs, vw, vi, vl, vx обозначают
переменные соответствующих типов. В квадратных скобках –
необязательный параметр.
25
Таблица 6.
Обращение Тип результата Действие
Abs (x) X Возвращает модуль х
Chr (b) Char Возвращает символ по его коду
Dec(vx[,i]) Процедура Уменьшает значение vx на i, при
отсутствии i -на 1
Inc(vx,[i]) Процедура Увеличивает значение vx на i, при
отсутствии i -на 1
Hi(i) Byte Возвращает старший байт аргумента
Hi(w) Byte То же
Lo (i) Byte Возвращает младший байт аргумента
Lo(w) Byte То же
Odd(l) Boolean Возвращает TRUE , если аргумент
нечетное число, False – если четное
Random(w) Как у параметра Возвращает псевдослучайное число,
равномерно распределенное на
интервале 0 <= х <w
Sqr(X) То же Возвращает квадрат аргумента
Swap(i) Integer Меняет местами байты в слове
Swap(w) Word То же
При действиях с целыми числами тип результата будет соответствовать
типу операндов, а если операнды относятся к различным целым типам, - типу
того операнда, который имеет максимальную мощность (максимальный
диапазон значений). Возможное переполнение результата никак не
контролируется, что может привести к недоразумениям, например:
Var
26
A: integer;
X, y: real;
Begin
A:=32767;
X:= a+2;
Y:=longint (a)+2;
Writeln (x:10:0,y:10:0 );
End.
В результате прогона получим
-32677 32769
Логический тип
Значениями логического типа может быть одна из предварительно
объявленных констант FALSE(ложь) или TRUE (истина). Для них
справедливы правила:
Ord (false) = 0;
Ord (true)=1;
False<True;
Succ (False)=True;
Pred (true)=False;
Поскольку логический тип относится к порядковым типам, его можно
использовать, например, в операторе
...............
Var
i : Boolean;
. . . .
for i:=false to true do ...
27
Логические операции
Таблица 7.
A Not A
False True
True False
Таблица 8.
A B A and B
False False False
False True False
True False False
True True True
Таблица 9.
A B A or B
False False False
False True True
True False True
True True True
Таблица 10.
A B A xor B
False False False
False True True
True False True
True True False
Пример
if I mod 7 = 0 then
if I mod 13 = 0 then
writeln(I);
28
if (I mod 7 = 0) and (I mod 13 = 0) then
writeln(I);
Символьный тип
Значением символьного типа является множество всех символов ПК.
Каждому символу приписывается целое число в диапазоне 0..255. Это число
служит кодом внутреннего представления символа, его возвращает функция
ORD.
Для кодировки используется код ASCII. Это 7-битный код, т.е. с его
помощью можно закодировать лишь 128 символов в диапазоне от 0 до 127. В
то же время в 8-битном байте, отведенном для хранения символов в Турбо
Паскале, можно закодировать в два раза больше символов в диапазоне 0..255.
Первая половина символов ПК с кодами 0..127 соответствует стандарту
ASCII. Вторая половина с кодами 128-255 не ограничена жесткими рамками
стандарта и может меняться на ПК разных типов.
Символы с кодами 0…31 относятся к служебным кодам. Если эти коды
используются в символьном тексте программы, они считаются пробелами.
При использовании их в операциях ввода - вывода они могут иметь
следующее самостоятельное значение:
Таблица 11.
Символ Код Значение
Bel 7 Звонок: вывод на экран этого символа
сопровождается звуковым сигналом
Ht 9 Горизонтальная табуляция: при выводе на
экран смещает курсор в позицию кратную
8, плюс 1
Lf 10 Перевод строки: при выводе на экран все
последующие символы будут выводиться,
29
начиная с той же позиции, но на
следующей строке.
VT 11 Вертикальная табуляция: при выводе на
экран заменяется спец.символом
FF 12 Прогон страницы: при выводе на принтер
формирует страницу, при выводе на экран
заменяется спец. символом
Cr 13 Возврат каретки: водится нажатием на
клавишу enter (при вводе с помощью read
или readln означает команду “ввод” и в
буфер ввода не помещается; при выводе
означает команду “продолжить вывод с
начала текущей строки”)
Sub 26 Конец файла: вводится с клавиатуры
нажатием ctrl+Z; при выводе заменяется
спец. знаком
Esc 27 Конец работы: вводится с клавиатуры
нажатием на клавишу esc; при выводе
заменяется спец.символом
К типу CHAR применимы операции отношения, а также встроенные
функции:
CHR (B) – функция типа CHAR преобразует выражение В типа BYTE в
символ и возвращает его своим значением;
UPCASE (CH) – функция типа CHAR; возвращает символ в верхнем
регистре, если он определен для аргумента CH типа CHAR, в противном
случае возвращает сам символ Ch:
Var c1,c2:char;
Begin
30
C1:=upcase (‘s’);
C2:=upcase (‘ ф ’);
Writeln (c1,’ ‘, c2);
End.
В результате прогона программы на экран будет выдано
S ф
Так как функция UPCASE не обрабатывает кириллицу.
Раздел описания типов
Раздел описания типов начинается ключевым словом Type и содержит
описания новых типов данных, созданных программистом:
Type
<Имя типа1>=<Описание1>;
<Имя типа2>=<Описание2>;
...
Перечисляемый тип
Задается перечислением тех значений, которые он может получать.
Каждое значение именуется некоторым идентификатором и располагается в
списке, обрамленным круглыми скобками:
Type
Colors = (red, white, blue);
Применение перечисляемых типов делает программы нагляднее.
Если, например, в программе используются данные, связанные с
месяцами года то:
Type
31
Тип месяц = (янв, фев, мар, апр, май, июн, июл, авг, сен,
окт, ноя, дек);
Var
Месяц :ТипМесяц;
..........
If месяц= авг then writeln(‘Море!!!’);
Этот фрагмент программы был бы очень наглядным. Увы! В Паскале
нельзя использовать кириллицу в идентификаторах, поэтому мы вынуждены
писать так:
tуре
TypeMonth=(jan, feb, mar, may, apr, jun, jul, aug, sep, oct,
nov, dec);
Var
Month:TypeMonth;
..........
If Month = aug then writeln(‘Хорошо бы поехать к морю!’);
Соответствия между значениями перечисляемого типа и порядковыми
номерами этих значений устанавливается порядком перечисления: первое
значение в списке получает значение 0, второе – 1 и т.д. Максимальная
мощность перечисляемого типа составляет 65535 значений, поэтому
фактически перечисляемый тип задает некоторое подмножество целого типа
WORD и может рассматриваться как компактное объявление сразу группы
целочисленных констант со значениями 0,1 и т.д.
Использование перечисляемых типов повышает надежность программ
благодаря возможности контроля тех значений, которые получат
соответствующие переменные. Пусть задано:
Type
Colors =(black, red, white);
Ordenal = (one, two, three);
32
Days = (monday, tuesday, wednesday);
С точки зрения мощности и внутреннего представления все три типа
эквивалентны:
Ord (black)=0,…, ord (white)=2,
Ord (one)=0, ... , ord (three)=2,
Ord (monday)=0, ... , ord (wednesday)=2,
Однако если определены переменные
Var
col: colors;
Num:ordenal;
Day:days;
То допустимы операторы
Col:=black;
Num:=succ (two);
Day:=pred (tuesday);
но недопустимы
Col:=one;
Day:=black;
и т.д.
Как уже упоминалось, между значениями перечисляемого типа и
множеством целых чисел существует однозначное соответствие, задаваемое
функцией ord(x). В Турбо Паскале допускается и обратное преобразование:
любое выражение типа WORD можно преобразовать в значения
перечисляемого типа, если только значение целочисленного выражения не
превышает мощности перечисляемого типа.
33
Например, для рассмотренного выше объявления типов эквивалентны
следующие присваивания:
Col:=one;
Col:=colors (0);
Разумеется присваивание
Col:=0;
Будет недопустимым.
Переменные любого перечисляемого типа можно объявлять без
предварительного описания этого типа:
Var col: (black, white);
Тип диапазон
Это подмножество есть подмножество своего базового типа, в качестве
которого может выступать любой порядковый тип, кроме типа-диапазона.
Тип-диапазон задается границами своих значений внутри базового типа:
<мин. знач.>..<макс. знач.>
Например:
Type
Digit = ‘0’.. '9';
Dig2 = 48..57;
Тип диапазон не обязательно описывать в разделе type, а можно
указывать непосредственно при объявлении переменной:
Var
Date: 1..31;
34
Month: 1 .. 12;
Lchr:’A’..’Z’;
При объявлении типа диапазона нужно руководствоваться следующими
правилами:
1. Два символа «..» рассматриваются как один символ, поэтому между ними
недопустимы пробелы;
2. Левая граница диапазона не может превышать правую границу.
Тип-диапазон наследует все свойства базового типа, но с
ограничениями, связанными с его меньшей мощностью. В частности если
определена переменная
Type
Days = (mo, tu, we, th, fr, sut, sa);
Weekend = sa.. su;
Var
w: Weekend;
...................
W:=sa;
то ord(w) вернет значение 5, в то время как PRED(W) приведет к ошибке.
Вещественные типы
В отличие от порядковых типов, значения которых всегда
сопоставляется с рядом целых чисел и, следовательно, представляются в ПК
абсолютно точно, значения вещественных типов определяют произвольное
число лишь с некоторой конечной точностью, зависящей от внутреннего
формата вещественного числа.
Таблица 12.
Длина, байты Название типа Мантисса Диапазон
десятичного
35
порядка
4 Single 7...8 -45...+38
6 Real 11...12 -39...+38
8 Double 15...16 -324...+308
10 Extended 19...20 -4951...+4932
8 Comp 19...20 -2^63+1...
+2^63-1
Как видно из таблицы вещественное число в Паскале занимает от 4-х до
10 смежных байт и имеет следующую структуру в памяти ПК.
S E M
Здесь s-знаковый разряд числа; е – экспоненциальная часть, содержит
двоичный порядок.m- мантисса числа.
Мантисса имеет длину от 23 до 63 двоичных разрядов, что и
обеспечивает точность 7..8 для single и 19..20 для extended десятичных цифр.
Десятичная точка подразумевается перед левым (старшим) разрядом
мантиссы, но при действиях с числом ее положение сдвигается влево или
вправо в соответствии с двоичным порядком числа, хранящимся
экспоненциальной части, поэтому действия над вещественными числами
называют арифметикой с плавающей точкой, запятой.
Паскаль характеризуется богатой гаммой вещественных типов, однако
доступ к типам single, double, extended возможен только при особых режимах
компиляции. Дело в том, что эти типы рассчитаны на аппаратную поддержку
арифметики с плавающей точкой и для их эффективного использования в
состав ПК должен входить арифметический сопроцессор 8087, 80287 или их
отечественный аналог К1810ВМ87.
Компилятор позволяет создавать программы, работающие на любых ПК
и использующие любые вещественные типы. В процессе запуска Турбо
36
Паскаль проверяет состав аппаратных средств и выявляет наличие или
отсутствие сопроцессора.
Арифметический сопроцессор всегда обрабатывает числа в формате
extended, а три других вещественных типа в этом случае получаются
простым усечением результатов до нужных размеров и применяются я в
основном для экономии памяти.
Например:
{$N+, E+}
Type
RealType= real;
Var
Epsilon:= realtype;
Begin
Epsilon:=1;
while 1+epsilon/2>1 do
Epsilon:=epsilon/2;
Writeln (epsilon);
End.
Независимо от объявления типа REALTYPE на печать будет выдан рез-т:
1.08420217248550Е-0019,что соответствует типу extended.
Чтобы получить правильный результат программу необходимо изменить
следующим образом:
{$N+, E+}
Type
Realtype = real;
Var
epsilon, eps1: realtype;
Begin
Epsilon:=1;
Repeat
37
Epsilon:=epsilon/2;
Eps1:=epsilon+1;
Until eps1>1;
Writeln (2*epsilon)
End.
Следует учесть, что тип REAL оптимизирован для работы без
сопроцессора. Если ваш ПК оснащен сопроцессором, использование типа
REAL приведет к дополнительным затратам времени на преобразование
REAL к EXTENDED. Поэтому никогда не используйте REAL на ПК с
сопроцессором, т.к. дополнительные затраты времени на преобразование
типов могут свести на нет все преимущества сопроцессора. При разработке
программ, критичных ко времени счета, следует заменять его типами SINGLE
или DOUBLE: по сравнению с типом REAL скорость вычислений на машинах
с сопроцессором в этом случае увеличивается в 2-3 раза. Если в ПК нет
арифметического сопроцессора, скорость обработки данных всех
вещественных типов приблизительно одинакова.
Особое положение в Паскале занимает тип COMP, который трактуется
как вещественное число без экспоненциальной и дробной части. COMP- это
"большое" целое число со знаком, сохраняющее 19.. 20 значащих десятичных
цифр. В то же время в выражениях COMP полностью совместим с любыми
другими вещественными типами: над ними определены все вещественные
операции, он может использоваться как аргумент математических функций и
т.д. Наиболее подходящей областью применения типа COMP являются
бухгалтерские расчеты: денежные суммы выражаются в копейках и действия
над ними сводятся к операциям с достаточно длинными целыми числами.
Для работы с вещественными данными могут использоваться
встроенные математические функции, представленные в таблице 3. В этой
таблице REAL означает любой вещественный тип, INTEGER- любой целый
тип.
38
Таблица 13.
Обращение Тип параметра Тип результата Примечание
Abs(x) Real, integer Тип аргумента Модуль аргумента
ArcTan(x) Real Real Арктангенс (радианы)
Cos (x) “ “ Косинус (радианы)
Exp (x) “ “ Экспонента
Frac (x) “ “ Дробная часть числа
Int (x) “ “ Целая часть числа
Ln (x) “ “ Логарифм натуральный
Pi - Real П = 3.141592653...
Random - Real Равномерное
псевдослучайное число
0<=x<1
Random(i) Integer Integer Равномерное
псевдослучайное целое
число 0<=i<1
Randomize - - Инициация датчика
случайных чисел
Sin(x) Real Real Синус (радианы)
Sqr(x) Real, integer Тип аргумента Квадрат аргумента
Sqrt (x) real Real Корень квадратный
Операторы языка Паскаль
Составной оператор
Составной оператор – это последовательность произвольных операторов
программы, заключенная в операторные скобки – зарезервированные слова
begin…end. Составные операторы – важный инструмент Турбо Паскаля,
39
дающий возможность писать программы по современной технологии
структурного программирования (без операторов перехода goto).
Язык Турбо Паскаль не накладывает никаких ограничений на характер
операторов, входящих в составной оператор. Среди них могут буть и другие
составные операторы – Турбо Паскаль допускает произвольную глубину их
вложенности:
Begin
.......
Begin
.......
Begin
.......
End;
.......
End;
.......
End;
Фактически, весь раздел операторов, обрамленный словами begin…end,
представляет собой один составной оператор. Поскольку зарезервированное
слово end является закрывающей операторной скобкой, оно одновременно
указывает и конец предыдущего оператора, поэтому ставить перед ним
символ «;» необязательно. Однако, в процессе работы над программой перед
словом end могут появиться новые операторы. В этом случае часто возникает
ошибка из-за отсутствия символа «;» после предпоследнего оператора
(программист добавляет новый оператор, после которого символ «;» не
нужен, а поставить его там где он теперь необходим забывает). С этой точки
зрения символ «;» лучше ставить и перед словом end. Это будет означать, что
между последним оператором и операторной скобкой end располагается
пустой оператор. Пустой оператор не содержит ни каких действий, просто в
программу добавляется лишняя точка с запятой.
40
Условный оператор
Условный оператор позволяет проверить некоторое условие и в
зависимости от результатов проверки выполнить то или иное действие.
Таким образом, условный оператор – это средство ветвления
вычислительного процесса.
Структура условного оператора имеет следующий вид:
if <условие> then <оператор1> else <оператор2>;
где if, then, else – зарезервированные слова (если, то, иначе);
<условие> – произвольное выражение логического типа;
<оператор1>, <оператор2>, – любые операторы языка Турбо Паскаль.
Условный оператор работает по следующему алгоритму. Сначала
вычисляется условное выражение <условие>. Если результат есть true
(истина), то выполняется <оператор1>, а <оператор2> пропускается; если
результат есть false (ложь), наоборот, <оператор1> пропускается, а
выполняется <оператор2>. Например:
var
x, y, max : real;
.......
if x>max then y:=max
else y:=x;
При выполнении этого фрагмента переменная y получит значение
переменной x, если только это значение не превышает max, в противном
случае y станет равным max.
Часть else <оператор2> условного оператора может быть опущена. Тогда
при значении true условного выражения выполняется <оператор1>, в
противном случае этот оператор пропускается:
var
41
x, y, max : real;
.......
if x>max then max:=x;
y:=x;
В этом примере переменная y всегда будет иметь значение переменной
x, а в max запоминается значение x если оно больше значения max.
Поскольку любой из операторов <оператор1> и <оператор2> может
быть любого типа, в том числе и условным, а в то же время не каждый из
«вложенных» условных операторов может иметь часть else <оператор2>, то
возникает неоднозначность трактовки условий, т.е. непонятно какой else к
какому if…then относится. Эта неоднозначность в Турбо Паскале решается
следующим образом: любая встретившаяся часть else соответствует
ближайшей к ней «сверху» части if…then условного оператора. Например:
var
a, b, c, d : integer;
a:=1;
b:=2;
c:=3;
d:=4;
if a>b then
if c<d then
if c<0 then c:=0
else a:=b; {a=1}
if a>b then
if c<d then
if c<0 then c:=0
else
else
else a:=b; {a=2}
42
Обратите внимание на то, что перед else точка с запятой не ставится.
Символ «;» в Паскале означает конец оператора, а оператор if заканчивается
там где заканчивается <оператор2> (если есть часть else).
В случае, когда необходимо выполнить несколько операторов, а не один
(<оператор1> или <оператор2>) их необходимо заключить в операторные
скобки begin … end, получив, тем самым, составной оператор.
Рассмотрим программу, которая вводит произвольное целое число в
диапазоне 0..15, преобразует его к шестнадцатеричному основанию и
выводит на экран полученное.
var
n : integer; {Вводимое число}
ch : char; {Результат}
begin
write(‘n= ‘); {Вывести приглашение (подсказку)}
readln(n); {Ввести число}
{Проверить число на принадлежность диапазону 0..15}
if (n>=0) and (n<=15) then
begin {Да, принадлежит}
if n<10 then ch:=chr(ord(‘0’)+n)
else ch:=chr(ord(‘A’)+n-10);
writeln(‘n=’, ch);
end
else writeln(‘Ошибка’);
end.
В шестнадцатеричной системе счисления используются 16 цифр в
каждом разряде: цифры 0..9 обозначают первые 10 возможных значений
разряда, буквы A..F – остальные шесть.
В программе учитывается непрерывность и упорядоченность множества
цифр 0..9 и множества букв A..F.
43
Оператор выбора Case
Если у вас не два возможных варианта выполнения программы, а
больше, то может использоваться оператор выбора CASE. Структура этого
оператора в Турбо Паскале:
CASE <ключ_выбора> OF
C1 : <оператор1>;
C2 : <оператор2>;
. . .
CN : <операторN>;
[ELSE <оператор0>;]
END;
Здесь <ключ_выбора> - это выражение порядкового типа, в зависимости
от значения которого принимается решение; C1,...,CN - значения, с которыми
сравнивается значение <ключа>; <оператор1>,..., <операторN> - оператор
(возможно составные), из которых выполняется rnr, с константой которого
происходит первое совпадение значения <ключа>, <оператор0> выполнится,
если значение ключа не совпадает ни с одной из констант C1,...,CN.
Пример
Program calc;
Var a,b,rez:real;
oper:char;
Begin
write(‘Введите два числа: ’);
readln(a,b);
write(‘Введите операцию: ‘);
readln(oper);
case oper of
‘+’:rez:=a+b;
‘-’:rez:=a-b;
‘*’:rez:=a*b;
44
‘/’:rez:=a/b;
end;
writeln(‘Результат=‘,rez);
end.
Операторы повтора
В языке Турбо Паскаль имеются три различных оператора, с помощью
которых можно запрограммировать повторяющиеся фрагменты программ.
Такие операторы называются циклами. Различаются эти операторы способом
определения количества необходимых повторов, или, другими словами,
когда необходимо завершить работу цикла.
Первый вариант цикла называется циклом с предпроверкой условия и
записывается следующим образом:
while <условие выполнения цикла> do <оператор>;
Выполнение цикла начинается с проверки <условия выполнения цикла>.
Если оно истинно, то выполняется <оператор> (тело цикла). Потом снова
проверяется <условие выполнения цикла> и, если оно истинно, то
выполняется <оператор> и т.д. Если на коком-то шаге <условие выполнения
цикла> станет ложным, то выполнение цикла будет завершено. Таким
образом, пока <условие выполнения цикла> истинно будет выполняться
<оператор>. Если <условие выполнения цикла> изначально ложно, то
<оператор> ни разу не будет выполнен.
Если в цикле необходимо выполнить несколько операторов, то
необходимо воспользоваться составным оператором:
while <условие выполнения цикла> do
begin
<операторы>
end;
45
Второй вариант цикла называется циклом с постпроверкой условия и
записывается следующим образом:
repeat
<операторы>
until <условие выхода из цикла>;
Выполнение цикла начинается с выполнения <операторов> (тела цикла),
потом проверяется <условие выхода из цикла>. Если оно ложно, то снова
выполняются <операторы>. Потом снова проверяется <условие выхода из
цикла> и т.д. Если на коком-то шаге <условие выхода из цикла> станет
истинным, то выполнение цикла будет завершено. Таким образом, будут
выполняться <операторы> до тех пор, пока <условие выхода из цикла> не
станет истинным. Кикам бы ни было <условие выхода из цикла> в начале
работы цикла, <операторы> будут выполнены, по крайней мере, один раз.
Тело цикла может содержать несколько операторов, причем нет
необходимости использовать составной оператор, поскольку в качестве
операторных скобок здесь выступают ключевые слова repeat и until.
Оба оператора цикла предполагают, что
1. на момент начала выполнения цикла определены (имеют начальные
значения) все переменные участвующие в вычислении значений условий и
2. тело цикла содержит оператор или операторы, которые изменяют эти
переменные таким образом, чтобы условие изменило свое значение и цикл
завершил свою работу.
Следующий вариант оператора цикла включает в себя оба этих условия.
и записывается следующим образом:
for <переменная цикла>:=<начальное значение> to <верхняя
граница> do
<оператор>;
46
Выполнение цикла начинается с присвоения <переменная цикла>
<начального значения>. Далее, если значение <переменная цикла> меньше
или равно значению <верхней границы>, то выполняется <оператор>. Потом
значение переменной цикла увеличивается на единицу и снова проверяется
условие, что значение <переменной цикла> меньше или равно значению
<верхней границы>, выполняется <оператор> и т.д.
Есть еще один вариант цикла for:
for <переменная цикла>:=<начальное значение> downto <нижняя
граница>
do <оператор>;
Здесь на каждом шаге значение переменной цикла уменьшается на
единицу.
Если в теле цикла необходимо выполнить несколько операторов, то
снова можно воспользоваться составным оператором.
Следующая маленькая программа выводит на экран кодовую таблицу:
Program Str3;
Var
I : Byte;
Begin
For I:=32 to 255 do
Write('VV',I:4, '-',Chr(I))
End.
Цикл в программе начинается с 32 потому, что символы с кодами от 0 до
31 являются управляющими и не имеют соответствующего графического
представления.
Пример
47
Совершенными называются числа, равные сумме всех своих делителей,
включая 1. Например, 6 – совершенное число, поскольку 6=1+2+3.
Требуется найти и вывести на экран все совершенные числа вместе с их
делителями, лежащие в диапазоне от 4 до 10000.
Var I,sum,del1,n:word;
Begin
for i:=4 to 10000 do
Begin
sum:=1; n:= trunc(sqrt(i));
for del1:=2 to n do
if I mod del1 =0 then
if del1<> I div del1 then
begin
sum:=sum+del1+(I div del1);
end
else
sum:=sum+del1;
if sum=i then
writeln(i);
End;
End.
Пример
Вычислить квадратный корень из произвольного вещественного числа,
введенного с клавиатуры, используя метод Ньютона:
Здесь A – введенное число, Y0=A.
Если A – отрицательное число, то необходимо вывести сообщение об
ошибке.
Результат должен иметь относительную точность 1·10-6. Как только
получено значение Y0 оно используется для получения следующего
48
приближения Y0, по которому в свою очередь вычисляется Y0, и так до тех
пор, пока не будет достигнута требуемая точность, то есть, пока не станет
Var A,Y1,Y:real;
Begin
write(‘Введите число:’);
readln(A);
Y1:=A;
repeat
Y:=Y1;
Y1:=(Y+A/Y)/2;
until abs(Y1-Y)<0.000001;
writeln(‘Корень из числа=‘,Y1:10:5);
end.
Вывод трехзначных чисел из неповторяющихся цифр
For i:=0 to 9 do
for j:=0 to 9 do
for k:=0 to 9 do
if (i<>j)and(i<>k)and
(j<>k) then
write(I,j,k,’ ‘);
Вывод чисел из нечетных цифр
i:=1;
While i<=9 do
begin
J:=1;
while j<=9 do
begin
K:=1;
49
while k<=9 do
begin
write(I,j,k,’ ‘);
Inc(k,2);
end;
Inc(j,2);
end;
Inc(I,2);
End;
Оператор перехода
В языке Турбо-Паскаль имеются различные управляющие операторы,
позволяющие написать любую программу. Тем не менее в языке имеется
оператор безусловного перехода.
Безусловный переход приводит к передаче управления из одного места в
программе в другое. Структура оператора перехода следующая:
goto <метка>
Здесь goto ключевое слово (англ.: перейти на [метку]).
Метка это произвольный идентификатор, позволяющий пометить
некоторый оператор, чтобы ссылаться на него. Для совместимости со
стандартным Паскалем разрешается в качестве метки использовать целые
числа. Метка ставится перед оператором получающим управление и
отделяется от него двоеточием. Как любой идентификатор метку необходимо
описать в разделе описания меток, который начинается с ключевого слова
label, за которым следует список меток:
...............
label m,1,loop;
begin
.........
goto 1;
m: .........
goto loop;
50
1: ............
goto m;
Метка, описанная в программе, обязательно должна использоваться.
Метка, описанная в функции или процедуре, должна использоваться в той
функции или процедуре, в которой она описана.
Структурированные типы. Массивы
Массивы являются структурированным типом. Структурированные
типы данных отличаются от обычных типов тем, что переменные таких
типов, имеют более одного значения, т.е. состоят из нескольких
компонентов. Структурированные типы характеризуются методом
структурирования и типами своих компонентов. Если тип компонента
является структурированным, то получаемый в результате
структурированный тип имеет более одного уровня структурирования.
Одномерные массивы
Массивы содержат фиксированное число элементов одного типа, так
называемого типа элемента. Переменные типа массив объявляются
следующим образом:
var
<имя переменной>:array[<тип индекса>] of <тип элемента>;
Допустимыми индексными типами являются все порядковые типы, за
исключением длинного целого и поддиапазонов длинного целого. Массив
может быть проиндексирован по каждой размерности всеми значениями
соответствующего индексного типа; число элементов поэтому равно числу
значений в каждом индексном типе. Число размерностей не ограничено.
Приведем пример переменной типа массив:
51
var
A: array[1..100] of Real
Если тип элемента в типе массив также является массивом, то результат
можно рассматривать как массив массивов или как один многомерный
массив. Например,
array[boolean] of array[1..100] of Real
интерпретируется компилятором точно так же, как массив:
array[boolean,1..10] of Real
Конкретный элемент массива обозначается с помощью ссылки на
переменную массива, за которой указывается индекс, определяющий данный
элемент.
A[5]:=3.4;
For i:=1 to 100 do
Begin
writeln(‘Введите элемент A[‘,I,’]:’);
Readln(A[i]);
End;
При объявлении массивов удобно пользоваться константами,
задающими количество элементов в массиве. Объявление констант:
Const
<имя конст.>=<значение>;
52
Примеры
При обработке массивов возникают такие задачи, как ввод элементов
массива, нахождение суммы, произведения, среднего и т.д., поиск
некоторого элемента в массиве, сортировка элементов массива, вывод
элементов массива.
Сумма элементов массива
Const N=10;
Var a:array[1..N] of real;
i:word;
sum:real;
Begin
sum:=0;
for i:=1 to N do
sum:=sum+a[i];
writeln(‘Сумма=‘,sum:10:5);
end.
Произведение элементов массива
Const N=10;
Var a:array[1..N] of real;
i:word;
p:real;
Begin
p:=1;
for i:=1 to N do
p:=p*a[i];
writeln(‘Произведение=‘,p:10:5);
end.
Заполнение массива псевдослучайными числами:
randomize;
53
for i:=1 to N do
a[i]:=x1+random*(x2-x1);
Заполнение массива числами Фибоначчи:
b[1]:=1;
b[2]:=1;
for i:=3 to N do
b[i]:=b[i-1]+b[i-2];
Возведение в квадрат четных элементов массива (элементов с четными
значениями):
for i:=1 to N do
if b[i] mod 2=0 then
b[i]:=sqr(b[i]);
Возведение в квадрат элементов массива с четным индексом:
i:=2;
while i<=N do
begin
a[i]:=sqr(a[i]);
Inc(I,2);
End;
Ниже приведена программа формирования элементов массива с
помощью датчика случайных чисел, вывод элементов массива на экран,
вычисление суммы всех элементов.
program rand1;
const n1=100; {максимальный размер массива}
type mas = array[1..n1] of integer;
var a:mas;
54
i, {индекс элемента массива}
n,s: integer;
begin
writeln('Введите число элементов массива:');
read(n);
{ Формирование массива с помощью датчика случайных чисел}
randomize;{Инициализация датчика случайных чисел }
for i:=1 to n do
a[i]:=random(10);
writeln('Полученный массив');
for i:=1 to n do
write (a[i]:5);
writeln;
s:=0; { Нахождение суммы }
for i:=1 to n do
s:=s+a[i];
writeln('s=',s);
end.
В приведенном примере в качестве исходных данных вводится размер
массива. Хотелось бы обратить внимание на использование константы n1.
Она сделает программу более универсальной, позволяя работать с
целочисленными массивами, размерность которых может изменяться от 1
до 100. Если будет введено число, меньшее 1 и большее 100, то возникнет
ошибка. Для формирования массива (да и во всех случаях, когда требуется
перебор всех элементов массива) лучше всего подходит оператор цикла со
счетчиком. В каждой итерации оператора цикла с датчика получаем
псевдослучайное число и присваиваем его очередному элементу массива
(индекс является переменной цикла). Результатом работы программы
является сформированный массив и сумма элементов этого массива.
Аналогично решается задача нахождения произведения элементов
массива, только начальное значение для произведения выбирается равным 1
и знак "+" меняется на знак "*".
55
Ниже приведена программа определения максимального элемента
массива и суммы положительных элементов, а также замены максимального
элемента массива суммой положительных элементов массива.
program sum;
const n1=100; {максимальный pазмеp массива}
type
mas = array[1..n1] of integer;
var
a:mas;
i, {индекс элемента массива}
n,s,
imax:integer;{индекс максимального элемента}
begin
writeln('Введите число элементов массива:');
read(n);
{Ввод массива}
for i:=1 to n do
begin
write ('Введите ',i,'-й элемент:');
read(a[i])
end;
s:=0;
imax:=1;{пpедполагаем, что пеpвый
элемент максимальный}
for i:=1 to n do
begin
{если элемент положительный, то
прибавляем его к сумме}
if a[i]>0 then s:=s+a[i];
{если текущий элемент массива больше
максимального, то запоминаем его индекс}
if a[imax]<a[i] then imax:=i;
end;
writeln('максимальный элемент массива =',a[imax]);
a[imax]:=s;{ замена максимального элемента суммой }
56
writeln('s=',s);
writeln('Обpаботанный массив:');
for i:=1 to n do
writeln (a[i]);
end.
В дальнейшем, в схемах алгоритма, подробно изображать ввод и вывод
массива не будем, чтобы алгоритм был нагляднее.
В приведенном примере массив вводится с клавиатуры. Для ввода
массива используется оператор цикла со счетчиком. За начальное значение
для индекса максимального элемента берем 1, т.е. предполагаем, что
первый элемент максимальный. Далее в цикле перебираются все элементы
массива и сравниваются c нулем для того, чтобы прибавлять или не
прибавлять элемент к сумме. В этом же цикле каждый элемент сравнивается
с a[imax] для выяснения, не встретился ли элемент, больший прежнего
максимального, и если встретился, то запоминается его индекс, чтобы
в следующий раз сравнивать текущий элемент с большим из перебранных. В
условном операторе if a[i]>a[imax] ветвь else отсутствует; это означает,
что в случае невыполнения условия imax остается без изменения, что и
обеспечивает наличие в области памяти с идентификатором imax значение
индекса максимального из перебранных элементов.
Ниже приведена программа сортировки элементов одномерного массива
"методом пузырька" в порядке неубывания. Суть сортировки этим методом
состоит в том, что сравниваются два соседних элемента и, если они
расположены в порядке убывания, то меняются местами, и этот факт
фиксируется в переменной fl.
После сравнения всех элементов массива принимается решение по
состоянию fl об очередном прохождении по массиву.
program sort1;
const n1=100; {максимальный pазмеp массива}
type
57
mas = array[1..n1] of real;
var
a:mas;
i, {индекс элемента массива}
n:integer;
fl:boolean;{флаг пеpестановок}
d:real;{дополнительная пеpеменная для пеpестановки
местами двух элементов массива}
begin
writeln('Введите число элементов массива:');
read(n);
{Ввод массива}
for i:=1 to n do
begin
write ('Введите ',i,'-й элемент:');
read(a[i])
end;
writeln('Исходный массив');
for i:=1 to n do
write (a[i]:5);
writeln;
{ Соpтиpовка }
repeat { повторить }
fl:=true;{ флаг поднять}
{ в очередной раз просматриваем элементы массива }
for i:=1 to n-1 do
if a[i]>a[i+1] then {сравниваем два соседних элемента}
begin{ меняем местами соседние элементы}
d:=a[i];
a[i]:=a[i+1];
a[i+1]:=d;
fl:=false;{если был обмен,то флаг опускаем }
end;
until fl;{если флаг не опускался,то массив отсортирован }
writeln('Обpаботанный массив:');
58
for i:=1 to n do
write (a[i]:5:2);
writeln;
end.
Основной цикл repeat прекращает выполняться, когда значение
логической переменной fl остается равной true после выполнения
вложенного цикла for. Это происходит в том случае, если ни одну пару
элементов не удается переставить, что указывает на то, что все элементы
стоят на своих местах.
Многомерные массивы
Если тип элемента в типе массив также является массивом, то результат
можно рассматривать как массив массивов или как один многомерный
массив. Например,
array[boolean] of array[1..10] of Real;
интерпретируется компилятором точно так же, как массив:
array[boolean,1..10] of Real;
В случае многомерного массива можно использовать несколько
индексов или несколько выражений в индексе. Например:
Matrix[I][J]
что тождественно записи:
Matrix[I,J]
Примеры
При обработке двумерных массивов возникают такие же задачи, как и
при обработке одномерных массивов: ввод элементов массива, нахождение
суммы, произведения, среднего и т.д., поиск некоторого элемента в массиве,
сортировка элементов массива, вывод элементов массива.
59
Ввод элементов двумерного массива
For i:=1 to N do
for j:=1 to M do
begin
writeln(‘Введите элемент a2[‘,I,’,’,j,’]: ‘);
readln(a2[I,j]);
end;
Вывод на экран:
For i:=1 to N do
begin
for j:=1 to M do
write(a2[I,j]:8);
writeln;
end;
Ниже приведена программа алгоритма формирования элементов массива
с помощью датчика случайных чисел, вывод элементов массива на экран,
вычисление суммы всех элементов двумерного массива.
program rand2;
const n1=10; {максимальнoе количество стpок массива}
m1=10; { максимальное количество столбцов массива}
type mas = array[1..n1,1..m1] of integer;
var a: mas;
i, { текущий номеp строки }
j, { текущий номеp столбца }
n,s,m : integer;
begin
writeln('Введите число стpок и столбцов массива:');
read(n,m);
randomize;
for i:=1 to n do
for j:=1 to m do
a[i,j]:=random(10);
60
writeln('Полученный массив');
for i:=1 to n do
begin
for j:=1 to m do
write (a[i,j]:5);
writeln;
end;
s:=0;
for i:=1 to n do
for j:=1 to m do
s:=s+a[i,j];
writeln('s=',s);
end.
Анализируя предложенную программу, можно заметить, что для ввода,
вывода и нахождения суммы элементов массива используются три раза
вложенные циклы. Так как массив располагается в непрерывной области
памяти построчно, более рационально будет и обрабатывать элементы
построчно. В программе во вложенных циклах для каждого значения
индекса i индекс j изменяется от 1 до m, т.е. индекс j изменяется чаще.
Таким образом, обрабатываются элементы массива построчно. Хотелось бы
обратить внимание на вывод элементов массива на экран. Здесь для каждого
значения i в теле цикла выполняются два оператора: первый оператор
цикла выводит на экран в строчку элементы одной строки, а второй
оператор вывода переводит курсор на новую строку, что как раз и
обеспечивает вывод матрицы в виде прямоугольника.
Следующий пример иллюстрирует работу с диагоналями матрицы. Дана
квадратная матрица. Заменить отрицательные элементы побочной диагонали
на сумму элементов главной диагонали матрицы. При изучении
поставленной задачи следует напомнить, что главная диагональ проходит из
правого верхнего в левый нижний угол. Так как мы работаем с квадратной
матрицей, то только на главной диагонали будут лежать элементы, индексы
строк и столбцов которых одинаковы. Именно этот факт и используется при 61
решении задачи. Мы не будем перебирать все элементы массива и смотреть,
совпали ли индексы, а сразу задаем оба индекса с помощью одного
идентификатора i. Побочная диагональ проходит из правого верхнего в
левый нижний угол матрицы. Нетрудно заметить, что при движении по
побочной диагонали номер строки возрастает от 1 до n, номер столбца
убывает от n до 1. Таким образом, только на побочной диагонали лежат
элементы, у которых номер столбца определяется по формуле j=n-i+1.
program diag;
const n1=10; {максимальнoе количество стpок массива}
type
mas = array[1..n1,1..n1] of integer;{квадpатная матpица}
var a:mas;
i, { текущий номеp стpоки }
j, { текущий номеp столбца }
n,s:integer;
begin
writeln('Введите число стpок и столбцов массива:');
read(n);
for i:=1 to n do
for j:=1 to n do
begin
writeln('Введите элемент массива');
read(a[i,j]);
end;
writeln('Исходный массив');
for i:=1 to n do
begin
for j:=1 to n do
write (a[i,j]:5);
writeln;
end;
s:=0;
for i:=1 to n do {Сумма элементов главной диагонали }
62
s:=s+a[i,i];
writeln('s=',s);
for i:=1 to n do{Замена элементов побочной диагонали}
begin
j:=n-i+1;
if a[i,j]<0 then a[i,j]:=s;
end;
writeln('Полученный массив');
for i:=1 to n do
begin
for j:=1 to n do
write (a[i,j]:5);
writeln;
end;
end.
Фрагмент программы для транспонирования матрицы относительно
побочной диагонали:
For i:=1 to N-1 do
for j:=1 to N-i do
begin
tmp:=c2[I,j];
c2[I,j]:=c2[N-j+1,N-i+1];
c2[N-j+1,N-i+1]:=tmp;
end;
И последний пример на обработку двумерных массивов. Дана
прямоугольная матрица. Отсортировать столбцы матрицы в порядке
неубывания максимальных элементов столбцов.
Решение задачи сводится к формированию одномерного массива из
максимальных элементов столбцов, а уж затем сортируется этот
одномерный массив и параллельно – столбцы матрицы. Чтобы не
запутать читателя, в этой задаче используем знакомый метод сортировки
63
"пузырьком". Исходный массив имеет идентификатор a, а промежуточный
одномерный массив – b.
program sort2;
const n1=10; {максимальнoе количество стpок массива}
m1=10; {максимальнoе количество столбцов массива}
type
mas = array[1..n1,1..m1] of integer;{квадpатная матpица}
var
a:mas;
b:array[1..m1] of integer;
{массив из максимальных элементов столбцов}
i, { текущий номеp стpоки }
j, { текущий номеp столбца }
n,m,d:integer;
fl:boolean;
begin
writeln('Введите число стpок и столбцов массива:');
read(n,m);
for i:=1 to n do
for j:=1 to m do
begin
writeln('Введите элемент массива');
read(a[i,j]);
end;
writeln('Исходный массив');
for i:=1 to n do
begin
for j:=1 to m do
write (a[i,j]:5);
writeln;
end;
{Фоpмиpование одномеpного массива из максимальных
элементов столбцов}
for j:=1 to m do {Пеpебиpаем все столбцы}
64
begin
b[j]:=a[1,j];{Пpинимаем пеpвый элемент в столбце за
максимальный }
for i:=2 to n do{Пеpебиpаем все элементы в столбце}
if a[i,j]>b[j] then b[j]:=a[i,j];
end;
end;
{Сортировка одномерного и двумерного массива}
repeat
fl:=true;{Поднять флаг}
for j:=1 to m-1 do {Перебрать элементы одномерного
массива}
if b[j]>b[j+1] then { Проверить нужна ли перестановка }
begin
fl:=false;{опустить флаг}
{Переставить элементы одномерного массива и}
d:=b[j];
b[j]:=b[j+1];
b[j+1]:=d;
{столбцы двумерного массива}
for i:=1 to n do
begin
d:=a[i,j];
a[i,j]:=a[i,j+1];
a[i,j+1]:=d;
end;
end;
until fl;{Завершить сортировку,если флаг не опускался}
writeln('Отсортированный массив');
for i:=1 to n do
begin
for j:=1 to m do
write (a[i,j]:5);
writeln;
end;
65
end.
В этой программе можно обойтись без дополнительного массива, но
тогда пришлось бы во время сортировки массива каждый раз искать
максимальный элемент столбца, даже если максимальный в этом столбце
мы уже находили.
Тип данных String
Тип STRING (строка) в Турбо Паскале широко используется для
обработки текстов. Он во многом похож на одномерный массив символов
ARRAY [O..N] OF CHAR, однако, в отличие от последнего, количество
символов в строке-переменной может меняться от 0 до N, где N - макси-
мальное количество символов в строке. Значение N определяется объяв-
лением типа STRING[N] и может быть любой константой порядкового типа,
но не больше 255 . Турбо Паскаль разрешает не указывать N, в этом случае
длина строки принимается максимально возможной, а именно N-255 .
Строка в Турбо Паскале трактуется как цепочка символов. К любому
символу в строке можно обратиться точно так же, как к элементу одномер-
ного массива ARRAY [O..N] OF CHAR, например:
var
st : string;
if st[5] - 'A' then .. .
Самый первый байт в строке имеет индекс 0 и содержит текущую длину
строки. Первый значащий символ строки занимает второй байт и имеет
индекс 1. Над длиной строки можно осуществлять необходимые действия и
таким способом изменять длину. Например, удалить из строки все ведомые
пробелы можно следующим образом:
var
st : strlng[10];
i : word;
66
i := 10;
while (st[l]=' ') and (i<>0) do begin dec(i):
st[0] := chr(i) end;
Значение ORD(st[0]) , т.е. текущую длину строки, можно получить и с
помощью функции LENGTH(st), например:
while ( Iength(st)<>0) and (st[ length(st)]-' ') do
s[0] := chr(length(st)-1)
К строкам можно применять операцию «+» - сцепление, например:
st := 'а' + 'b':
st := st + 'с'; {st содержит 'abc'}
Если длина сцепленной строки превысит максимально допустимую
длину N, то «лишние» символы отбрасываются. Следующая программа,
например, напечатает символ 1:
var
st:string[1];
begin
st:='123';
writeln(st)
end.
Все остальные действия над строками и символами реализуются с
помощью встроенных процедур и функций.
CONCAT(S1 [,S2, ... ,SN]) - функция типа STRING; возвращает строку,
представляющую собой сцепление строк-параметров SI, S2,... ,SN.
COPY(ST, INDEX, COUNT) - функция типа STRING; копирует из строки
ST COUNT символов, начиная с символа с номером INDEX.
DELETE(ST, INDEX, COUNT) - процедура; удаляет COUNT символов
из строки ST, начиная с символа с номером INDEX.
67
INSERT(SUBST, ST, INDEX) - процедура; вставляет подстроку SUBST в
строку ST, начиная с символа с номером INDEX.
LENGTH(ST) - функция типа INTEGER; возвращает длину строки ST.
POS(SUBST, ST) - функция типа INTEGER: отыскивает в строке ST
первое вхождение подстроки SUBST и возвращает номер позиции, с которой
она начинается; если подстрока не найдена, возвращается ноль.
STR(X [:WIDTH [:DECIMALS]], ST) - процедура; преобразует число Х
любого вещественного или целого типов в строку символов ST так, как это
делает процедура WRITELN перед выводом; параметры WIDTH и DECIMALS,
если они присутствуют, задают формат преобразования:
WIDTH определяет общую ширину поля, выделенного под
соответствующее символьное представление вещественного или целого
числа X, а DECIMALS - количество символов в дробной части (имеет смысл
только в том случае, когда Х - вещественное число).
VAL(ST, X, CODE) - процедура; преобразует строку символов ST во
внутреннее представление целой или вещественной переменной X, которое
определяется типом этой переменной; параметр CODE содержит ноль, если
преобразование прошло успешно, и тогда в Х помещается результат
преобразования, в противном случае он содержит номер позиции в строке
ST. где обнаружен ошибочный символ, и в этом случае содержимое Х не
меняется; ведущие пробелы в строке ST должны отсутствовать.
UPCASE(CH) - функция типа CHAR; возвращает для символьного
выражения CH, которое должно представлять собой строчную латинскую
букву, соответствующую заглавную букву; если значением CH является
любой другой символ, функция возвращает его без преобразований.
Примеры:
var
х : real;
у : Integer:
st,st1: string;
68
st:=concat('12','345'); {строка st содержит 12345}
st1:=copy(st,3,length(st)-2); {st1 содержит 345}
insert('-',st1,2); {строка st1 содержит 3-45}
delete(st,pos{'2',st),3); {строка st содержит 12}
str(pi:6:2,st); {строка st содержит 3.14)
st1:='3,1415':
Сравнение строк:
'Turbo' < 'Turbo Pascal'
'Паскаль' > 'Turbo Pascal'
Структурированный тип данных – записи
Записи
Запись – это структура данных, состоящая из фиксированного числа
компонентов, называемых полями записи. В отличие от массива, компоненты
(поля) записи могут быт различного типа. Чтобы можно было ссылаться на
тот или иной компонент записи, поля именуются.
Структура объявления записи такова:
<имя типа>= record <список полей> end;
Здесь <имя типа> – правильный идентификатор;
record, end – зарезервированные слова;
<список полей> – список полей; представляет собой последовательность
разделов записи, между которыми ставится точка с запятой.
Каждый раздел записи состоит из одного или нескольких
идентификаторов полей, отделяемых друг от друга запятыми. За
идентификатором (идентификаторами) ставится двоеточие и описание типа
поля (полей), например:
Type
Birthday=record
69
day, month: byte;
year : word;
end;
var
a,b: Birthday;
...
В этом примере тип Birthday (день рождения) содержит три поля с
именами day, month и year (день, месяц и год); переменные a, b сидержат
записи типа Birthday.
Как и в массиве, значения переменных типа запись можно присваивать
другим переменным того же типа, например
A:=b;
К каждому из компонентов записи можно получить доступ, если
использовать составное имя, т.е. указать имя переменной, затем точку и имя
поля:
a.day:=27;
b.year:=1939;
Для вложенных полей приходится продолжать уточнения:
var
c: record
name : string;
bd : Birthday;
end;
...
if c.bd.year=1939 then ...
70
Оператор WITH
Если в программе содержится большое число обращений к
компонентам записи (полям), то указание имени записи и имени поля с
помощью сочленяющей точки будет громоздким . Для упрощения вводится
оператор WITH - присоединение. Его еще называют оператором над
записями.
Общая форма
WITH имя переменной-записи DO оператор
Тогда в операторе или составном операторе следующем за DO имя
переменной записи можно опускать , а использовать только имя поля.
Пример обращения к полям:
gl.price := 200;
with gl do
begin
price := 200;
number := 10
end;
Оператор with удобнее использовать, если требуется обращаться к
нескольким полям одной и той же записи.
Пример. Сведения о товарах на складе хранятся в текстовом файле. Для
каждогс товара отводится одна строка, в первых 20 позициях которой
записано наименование товара, а затем через произвольное количество
пробелов его цена и количе ство единиц. Программа по запросу выдает
сведения о товаре или сообщение о тол что товар не найден.
program store;
const Max_n = 100;
type
71
str20 = string [20];
goods = record
name : str20;
price : real;
number : integer;
end;
var stock : array[l..Max_n] of goods;
i, j, len : integer;
name : str20;
found : boolean;
count : integer;
begin
write('Введите количество товаров: ');
readln(count);
for i:=1 to count do begin
writeln(‘Введите в отдельных строках наимен., цену и кол-во’);
with stock[i] do
readln(name, price, number);
…
end.
Рассмотрим пример программы с использованием массива структур. В
ведомости, содержащей фамилии группы студентов, оценки по физике,
математике и программированию определить средний балл каждого студента
и средний балл в группе.
В программе использованы следующие обозначения :
n1– максимальное количество студентов в группе;
n – реальное количество студентов в группе;
student– идентификатор типа, представляющий запись с полями
fam, fiz, mat, pr и ss;
fam – поле записи, содержащее фамилию студента;
72
fiz, mat, pr – поле записи, содержащее оценки по физике, математике
и программированию соответственно;
ss – поле записи, содержащее средний балл студента;
ved – массив, содержащий элементы типа student;
sg – средний балл группы;
i – индекс элемента массива ved;
Программа выглядит следующим образом:
program stud;
const n1=30;
type student=record
fam:string[10];
fiz,mat,pr:integer;
ss:real;
end;
var ved:array[1..n1] of student;
i,n:integer;
sg:real;
begin
writeln('сколько студентов в группе?');
read(n);
for i:=1 to n do
with ved[i] do
begin
writeln('введите фамилию студента');
read(fam);
writeln('введите оценки');
read(fiz,mat,pr)
end;
sg:=0;
for i:=1 to n do
with ved[i] do
begin
ss:=(fiz+mat+pr)/3; {вычисление среднего балла
73
студента}
sg:=sg+ss;
end;
sg:=sg/n;{вычисление среднего балла группы}
writeln('ведомость группы');
write('! фамилия ! физика ! матем ! прогр !');
writeln('! cp. балл !')
for i:=1 to n do
with ved[i] do
begin
write('!',fam:10,'!',fiz:10,'!',mat:10,'!',pr:10);
writeln('!',ss:10:2,'!');
end;
writeln('средний балл в группе =',sg);
end.
Иногда бывает необходимо иметь в программе несколько родственных,
но не совсем идентичных записей. Такая необходимость возникает,
например, для программы, которая обрабатывает информацию о человеке и
тогда, в зависимости от значения поля sex (мужской или женский),
появляются поля:
время прохождения очередных военных сборов;
род войск, в которых проходил военный сбор;
или же:
любимые цветы.
Для таких случаев в Турбо-Паскале предусмотрены записи с
вариантами. Такие записи содержат фиксированную и вариантную часть,
которая начинается с ключевого слова case. Рассмотрим пример:
type personsex=(male,female);
person = record
name,secondname,surname : string[20];
birthday : date;
case sex : personsex of
male : ( army1 : date;
74
army2 : string[20]);
female : (flower : srting[20]);
end;
Следует отметить, что вариантная часть всегда располагается после
фиксированной части, а отводимая память вычисляется по самому большому
варианту, т.е. различные варианты одной записи как бы "накладываются"
друг на друга.
Структурированный тип данных - множество
Определение множеств
Множества – это наборы однотипных логически связанных друг с
другом объектов. Характер связей между объектами лишь подразумевается
программистом и никак не контролируется Турбо Паскалем. Количество
элементов, входящих в множество, может меняться от 0 до 256 (множество,
не содержащее элементов, называется пустым). Именно непостоянством
количества своих элементов множества отличаются от массивов и записей.
Два множества считаются эквивалентными тогда и только тогда, когда
все их элементы одинаковы, причем порядок следования элементов в
множестве безразличен. Если все элементы одного множества входят также и
в другое, говорят о включении первого множества во второе. Пустое
множество включается в любое другое.
Пример определения и задания множеств:
type
DigitChar = set of '0'..'9';
Digit = set of 0..9;
var
s1,s2,s3: DigitChar;
s4,s5,s6: Digit;
...
s1:=['1','2','3'];
s2:=['3','2','1'];
75
s1:=['2','3'];
s1:=[0..3,6];
s1:=[4,5];
s1:=[3..9];
...
В этом примере множества s1 и s2 эквивалентны, а множество s3
включено в s2, но не эквивалентно ему.
Описание типа множество имеет вид
<имя типа> = set of <базовый тип>;
Здесь <имя типа> – правильный идентификатор;
set, of – зарезервированные слова (множество, из);
<базовый тип> – базовый тип элементов множества, в качестве которого
может использоваться любой порядковый тип, кроме Word, Integer, Longint.
Для задания множества используется так называемый конструктор
множества: список спецификаций элементов множества, отделяемых друг от
друга запятыми; список обрамляется квадратными скобками.
Спецификациями элементов могут быть константы или выражения базового
типа, а также – тип-диапазон того же базового типа.
Операции над множествами
Над множествами определены следующие операции:
* – пересечение множеств; результат содержит элементы, общие для
обоих множеств. Например, s4*s6 содержит [3], s4*s5 – пустое множество.
+ – объединение множеств; результат содержит элементы из первого
множества дополненные недостающими элементами из второго множества:
s4+s5 содержит [0,1,2,3,4,5,6];
s5+s6 содержит [3,4,5,6,7,8,9];
- – разность множеств; результат содержит те элементы из первого
множества, которые не принадлежат второму:
s6-s5 содержит [3,6,7,8,9];
76
s4-s5 содержит [0,1,2,3,6];
= – проверка эквивалентности; возвращает true, если множества
эквивалентны.
<> – проверка неэквивалентности; возвращает true, если множества
неэквивалентны.
<= – проверка вхождения; возвращает true, если первое множество
является подмножеством второго.
>= – проверка вхождения; возвращает true, если второе множество
является подмножеством первого.
in – проверка принадлежности; в этой бинарной операции первый
элемент – выражение, а второй – множество того же типа; возвращает true,
если выражение имеет значение, принадлежащее множеству:
3 in s6 возвращает true;
2*2 in s1 возвращает false;
Пример
В следующем примере реализуется алгоритм выделения из первой сотни
натуральных чисел всех простых. В его основе (также как и в примере из
лабораторной работы №3) лежит прием, известный под названием «решето
Эратосфена». Сравните программы, приведенные здесь и в лабораторной
работе №3.
{ Выделение всех простых чисел из первых N целых }
const
N = 100; {Количество элементов исходного множества}
type
SetOfNumber = set of 1..N:
var
n1,next,i : word; {Вспомогательные переменные}
BeginSet, {Исходное множество}
PrimerSet : SetOfNumber; {Множество простых чисел} BEGIN
BeginSet := [2..N]; {Создать исходное множество}
77
PrimerSet := [1]; {Первое простое число}
Next := 2; {Следующее простое число}
while BeginSet <> [] do {Начало основного цикла}
begin
n1:= next; {n1-число,кратное очередному
простому (next)}
while n1 <- N do {Цикл удаления из исходного
множества непростых чисел:}
begin
BeginSet := BeginSet-[n1];
n1 :=n1+next; {Следующее кратное}
end; {Конец цикла удаления}
PrimerSet := PrimerSet+[next];
repeat {Получить следующее простое, которое есть
первое невычеркнутое из исходного множества}
inc(next)
until (next In BeginSet) or (next > N)
end; {Конец основного цикла)
{ Вывод результата: }
for i := 1 to N do
if I in PrimerSet then write(i:8);
writeln;
END.
Процедуры и функции
Описание подпрограммы
Описание подпрограммы состоит из заголовка и тела подпрограммы.
Заголовок процедуры имеет вид:
PROCEDURE <имя> [(<сп.ф. п. >)];
Заголовок функции:
78
FUNCTION <имя> [(<.сп.ф.п.>)] : <тип>:
Здесь <имя> - имя подпрограммы (правильный идентификатор);
<сп.ф.п-> - список формальных параметров,
<тип> - тип возвращаемого функцией результата.
Полный формат описания процедуры:
Procedure <Имя процедуры> (<Имя форм. параметра 1>:<Тип1>;
< Имя форм. параметра 2>:<Тип2>);
<Раздел описаний>
Begin
<Тело процедуры>
End;
Раздел описаний может иметь такие же подразделы, как и раздел
описаний основной программы (описание процедур и функций - в том
числе). Однако все описанные здесь объекты "видимы" лишь в этой
процедуре. Они здесь локальны также, как и имена формальных параметров.
Объекты, описанные ранее в разделе описаний основной программы и не
переопределенные в процедуре, называются глобальными для этой
подпрограммы и доступны для использования.
Формат описания функции:
Function <Имя функции> (<Имя форм. параметра 1>:<Тип>;
< Имя форм. параметра 2>:<Тип>?) : <Тип результата>;
<Раздел описаний>
Begin
<Тело функции>
End;
В теле функции обязательно должна быть хотя бы команда присвоения
такого вида:
<Имя функции>:=<Выражение>;
79
Указанное выражение должно приводить к значению того же типа, что и
тип результата функции, описанный выше.
Сразу за заголовком подпрограммы может следовать одна из стан-
дартных директив ASSEMBLER, EXTERNAL. FAR, FORWARD, INLINE,
INTERRUPT, NEAR. Эти директивы уточняют действия компилятора и
распространяются на всю подпрограмму и только на нее, т.е. если за
подпрограммой следует другая подпрограмма, стандартная директива,
указанная за заголовком первой, не распространяется на вторую.
Например, директива FORWARD используется при опережающем
описании для сообщения компилятору, что описание подпрограммы следует
где-то дальше по тексту программы (но в пределах текущего программного
модуля).
Вызов процедуры представляет в программе самостоятельную
инструкцию:
<Имя процедуры>(<Фактический параметр 1>, <Фактический параметр
2>,…);
Типы фактических параметров должны быть такими же, что и у
соответствующих им формальных.
Вызов функции должен входить в выражение. При вычислении значения
такого выражения функция будет вызвана, действия, находящиеся в ее теле,
будут выполнены, в выражение будет подставлено значение результата
функции.
Параметры
Список формальных параметров необязателен и может отсутствовать.
Если же он есть, то в нем должны быть перечислены имена формальных
параметров и их тип, например:
Procedure SB (a : real; b : Integer: с : char);
80
Как видно из примера, параметры в списке отделяются друг от друга
точками с запятой. Несколько следующих подряд однотипных параметров
можно объединять в подсписки, например, вместо
Function F (а : real; b : real) : real;
можно написать проще:
Function F (a, b : real) : real;
Операторы тела подпрограммы рассматривают список формальных
параметров как своеобразное расширение раздела описаний: все переменные
из этого списка могут использоваться в любых выражениях внутри
подпрограммы. Таким способом осуществляется настройка алгоритма
подпрограммы на конкретную задачу.
Любой из формальных параметров подпрограммы может быть либо
параметром-значением, либо параметром-переменной. В предыдущем
примере параметры А и В определены как параметры-значения. Если
параметры определяются как параметры-переменные, перед ними
необходимо ставить зарезервированное слово VAR, например:
Function Power (var a : real; b : real) : real;
Здесь параметр А – параметр-переменная, а В – параметр-значение.
Определение формального параметра тем или иным способом
существенно только для вызывающей программы: если формальный
параметр объявлен как параметр-переменная, то при вызове подпрограммы
ему должен соответствовать фактический параметр в виде переменной нуж-
ного типа; если формальный параметр объявлен как параметр-значение, то
при вызове ему может соответствовать произвольное выражение. Контроль
81
за неукоснительным соблюдением этого правила осуществляется
компилятором Турбо Паскаля.
Для того чтобы понять, в каких случаях использовать параметры-
значения, а в каких - параметры-переменные, рассмотрим, как
осуществляется замена формальных параметров на фактические в момент
обращения к подпрограмме.
Если параметр определен как параметр-значение, то перед вызовом
подпрограммы это значение вычисляется, полученный результат копируется
во временную память и передается подпрограмме. Важно учесть, что даже
если в качестве фактического параметра указано простейшее выражение в
виде переменной или константы, все равно подпрограмме будет передана
лишь копия переменной (константы). Если же параметр определен как
параметр-переменная, то при вызове подпрограммы передается сама
переменная, а не ее копия. Любые возможные изменения в подпрограмме
параметра-значения никак не воспринимаются вызывающей программой, так
как в этом случае изменяется копия фактического параметра, в то время как
изменение параметра-переменной приводит к изменению самого
фактического параметра в вызывающей программе.
Представленный ниже пример поясняет изложенное. В программе
задаются два целых числа 5 и 7, эти числа передаются процедуре INC2, в
которой они удваиваются. Один из параметров передается как параметр-
переменная, другой - как параметр-значение. Значения параметров до и после
вызова процедуры, а также результат их удвоения выводятся на экран.
const
а : integer = 5;
b : integer = 7;
PROCEDURE lnc2 (var с : Integer; b : Integer);
begin {Inc2}
с := с + с;
b := b + b;
writeln(' удвоенные:', c:5, b:5);
82
end; {Inc2}
BEGIN {main}
writeln(' исходные:'. a:5, b:5);
lnc2(a,b);
writeln(' результат:', a:5, b:5);
END. {main}
В результате прогона программы будет выведено:
Исходные: 5 7 удвоенные 10 14 результат 10 7
Как видно из примера, удвоение второго формального параметра в
процедуре INC2 не вызвало изменения фактической переменной В, так как
этот параметр описан в заголовке процедуры как параметр-значение. Этот
пример может служить еще и иллюстрацией механизма "закрывания"
глобальной переменной одноименной локальной: хотя переменная В
объявлена как глобальная (она описана в вызывающей программе перед
описанием процедуры), в теле процедуры ее «закрыла» локальная пере-
менная В, объявленная как параметр-значение.
Итак, параметры-переменные используются как средство связи ал-
горитма, реализованного в подпрограмме, с «внешним миром»: с помощью
этих параметров подпрограмма может передавать результаты своей работы
вызывающей программе. Разумеется, в распоряжении программиста всегда
есть и другой способ передачи результатов - через глобальные переменные.
Однако злоупотребление глобальными связями делает программу, как
правило, запутанной, трудной в понимании и сложной в отладке. В
соответствии с требованиями хорошего стиля программирования
рекомендуется там, где это возможно, использовать передачу результатов
через фактические параметры-переменные.
С другой стороны, описание всех формальных параметров как
параметров-переменных нежелательно по двум причинам. Во-первых, это
83
исключает возможность вызова подпрограммы с фактическими параметрами
в виде выражений, что делает программу менее компактной. Во-вторых, и
главных, в подпрограмме возможно случайное использование формального
параметра, например, для временного хранения промежуточного результата,
т.е. всегда существует опасность непреднамеренно «испортить» фактическую
переменную. Вот почему параметрами-переменными следует объявлять
только те, через которые подпрограмма в действительности передает
результаты вызывающей программе. Чем меньше параметров объявлено
параметрами-переменными и чем меньше в подпрограмме используется
глобальных переменных, тем меньше опасность получения
непредусмотренных программистом побочных эффектов, связанных с
вызовом подпрограммы, тем проще программа в понимании и отладке. По
той же причине не рекомендуется использовать параметры-переменные в
заголовке функции: если результатом работы функции не может быть
единственное значение, то логичнее использовать процедуру или нужным
образом декомпозировать алгоритм на несколько подпрограмм.
Существует одно обстоятельство, которое следует учитывать при
выборе вида формальных параметров. Как уже говорилось, при объявлении
параметра-значения осуществляется копирование фактического параметра во
временную память. Если этим параметром будет массив большой
размерности, то существенные затраты времени и памяти на копирование
при многократных обращениях к подпрограмме могут стать решающим
доводом в пользу объявления такого параметра параметром-переменной или
передачи его в качестве глобальной переменной.
Примеры
Определение максимума из трех чисел:
Program Fn;
Var
A,B,C :Real;
84
Function Max(A,B:Real):Real;
{Описываем функцию Max с формальными}
Begin {параметрами A и B, которая принимает }
If A>B Then
Max:=A {значение максимального из них }
Else
Max:=B
{Здесь A и B - локальные переменные }
End;
Begin
Writeln('Введите три числа');
Readln(A,B,C);
Writeln('Максимальным из всех является ', Max(Max(A,B),C))
End.
Упорядочивание трёх чисел в порядке неубывания:
Program Pr;
Var
S1,S2,S3 :Integer;
Procedure Swap(Var A,B: Integer);
{Процедура Swap с параметрами-переменными}
Var C : Integer; {C - независимая локальная переменная}
Begin
C:=A; A:=B; B:=C {Меняем местами содержимое A и B}
End;
Begin
Writeln('Введите три числа');
Readln(S1,S2,S3);
If S1>S2 Then Swap(S1,S2);
If S2>S3 Then Swap(S2,S3);
If S1>S2 Then Swap(S1,S2);
Writeln('Числа в порядке неубывания:V',S1,S2,S3)
End.
85
Рассмотрим следующий пример. В языке Турбо Паскаль нет операции
возведения в степень, однако с помощью встроенных функций LN(X) и
ЕХР(Х) нетрудно реализовать новую функцию с именем, например, POWER,
осуществляющую возведение любого вещественного числа в любую
вещественную степень. В следующем примере вводится пара чисел X и У и
выводится на экран дисплея результат возведения Х сначала в степень +У, а
затем - в степень - У. Для выхода из программы нужно ввести Ctrl-Z и
«Ввод».
var
х,у : real;
FUNCTION Power(a,b : real) : real;
begin {Power}
if a > 0 then Power := exp(b * In(a))
else
If a < 0 then
Power := exp(b * ln(abs(a))) eIse
if b=0 then Power := 1 else Power := 0
end {Power};
BEGIN {main}
repeat
readln(x,y);
wrlteln(power(x,y):12:10, power (x, -у) :15:10)
until EOF;
END. {main}
Для вызова функции POWER мы просто указали ее в качестве параметра
при обращении к встроенной процедуре WRITELN. Параметры Х и У в
момент обращения к функции - это фактические параметры. Они
подставляются вместо формальных параметров А и В в заголовке функции и
затем над ними осуществляются нужные действия. Полученный результат
присваивается идентификатору функции - именно он и будет возвращен как
86
значение функции при выходе из нее. В программе функция POWER
вызывается дважды - сначала с параметрами Х и Y, а затем Х и -У, поэтому
будут получены два разных результата.
Механизм замены формальных параметров на фактические позволяет
нужным образом настроить алгоритм, реализованный в подпрограмме. Турбо
Паскаль следит за тем, чтобы количество и тип формальных параметров
строго соответствовали количеству и типам фактических параметров в
момент обращения к подпрограмме. Смысл используемых фактических
параметров зависит от того, в каком порядке они перечислены при вызове
подпрограммы. В примере первый по порядку фактический параметр будет
возводиться в степень, задаваемую вторым параметром, а не наоборот.
Пользователь должен сам следить за правильным порядком перечисления
фактических параметров при обращении к подпрограмме.
Параметры-массивы и параметры-строки
Может сложиться впечатление, что объявление переменных в списке
формальных параметров подпрограммы ничем не отличается от объявления
их в разделе описания переменных. Действительно, в обоих случаях много
общего, но есть одно существенное различие: типом любого параметра в
списке формальных параметров может быть только стандартный или ранее
объявленный тип. Поэтому нельзя, например, объявить следующую
процедуру:
Procedure S (a : array (1..10] of real);
так как в списке формальных параметров фактически объявляется тип-
диапазон, указывающий границы индексов массива.
Если мы хотим передать какой-то элемент массива, то проблем, как
правило, не возникает, но если в подпрограмму передается весь массив, то
следует первоначально описать его тип. Например:
87
type
atype = array [1..10] of real;
PROCEDURE S (a : atype);
Поскольку строка является фактически своеобразным массивом, ее
передача в подпрограмму осуществляется аналогичным образом:
type
intype = string[15];
outype = string[30];
FUNCTION St (s : intype) : outype;
Требование описать любой тип-массив или тип-строку перед
объявлением подпрограммы на первый взгляд кажется несущественным.
Действительно, в рамках простейших вычислительных задач обычно заранее
известна структура всех используемых в программе данных, поэтому
статическое описание массивов не вызывает проблем. Однако разработка
программных средств универсального назначения, например, типа широко
используемых в среде Фортрана ОС ЕС библиотек подпрограмм для научных
и инженерных расчетов, связана со значительными трудностями. По
существу, речь идет о том, что в Турбо Паскале невозможно использовать в
подпрограммах массивы с «плавающими» границами изменения индексов.
Например, если разработана программа, обрабатывающая матрицу из 10 х 10
элементов, то для обработки матрицы из 9 х 11 элементов необходимо
переопределить тип, т.е. перекомпилировать всю программу (сейчас речь
идет не о динамическом размещении массивов в куче, а о статическом
описании массивов и передаче их как параметров в подпрограммы). Этот
недостаток, как и отсутствие в языке средств обработки исключительных
ситуаций (прерываний), унаследован из стандартного Паскаля и представляет
собой объект постоянной и вполне заслуженной его критики. Разработчики
Турбо Паскаля не рискнули кардинально изменить свойства базового языка,
88
но, тем не менее, включили в него некоторые средства, позволяющие в
известной степени смягчить отмеченные недостатки.
Прежде всего, в среде Турбо Паскаля можно установить режим
компиляции, при котором отключается контроль за совпадением длины
фактического и формального параметра-строки. Это позволяет легко решить
вопрос о передаче подпрограмме строки произвольной длины. При передаче
строки меньшего размера формальный параметр будет иметь ту же длину,
что и параметр обращения; передача строки большего размера приведет к ее
усечению до максимального размера формального параметра. Следует
сказать, что контроль включается только при передаче строки, объявленной
как формальный параметр-переменная. Если соответствующий параметр
объявлен параметром-значением, эта опция игнорируется и длина не
контролируется.
Работа с файлами
Файловые переменные
Под файлом понимается либо именованная область внешней памяти ПК
(жесткого диска, гибкой дискеты, электронного «виртуального» диска), либо
логическое устройство – потенциальный источник или приемник
информации.
Любой файл имеет три характерные особенности. Во-первых, у него
есть имя, что дает возможность программе работать одновременно с не-
сколькими файлами. Во-вторых, он содержит компоненты одного типа.
Типом компонентов может быть любой тип Турбо Паскаля, кроме файлов.
Иными словами, нельзя создать «файл файлов». В-третьих, длина вновь
создаваемого файла никак не оговаривается при его объявлении и ограни-
чивается только емкостью устройств внешней памяти.
Файловый тип или переменную файлового типа можно задать одним из
трех способов:89
<имя> = FILE OF <mun>;
<имя> = TEXT:
<имя> = FILE;
Здесь <имя> - имя файлового типа (правильный идентификатор);
FILE, OF - зарезервированные слова (файл, из);
TEXT - имя стандартного типа текстовых файлов;
<тип> - любой тип Турбо Паскаля, кроме файлов.
Например:
type
product = record name string;
соde : word;
cost : comp;
end;
text80 = file of string[80];
var
f1 : file of char;
f2 : text;
f3 : file;
f4 : text80;
f5 : file of product;
В зависимости от способа объявления можно выделить три вида файлов:
типизированные файлы (задаются предложением FILE OF...);
текстовые файлы (определяются типом TEXT);
нетипизированные файлы (определяются типом FILE).
В наших примерах FI, F4 и F5 - типизированные файлы, F2 -текстовый
файл, F3 - нетипизированный файл. Вид файла, вообще говоря, определяет
способ хранения информации в файле. Однако в Турбо Паскале нет средств
контроля вида ранее созданных файлов. При объявлении уже существующих
файлов программист должен сам следить за соответствием вида объявления
характеру файла.
90
Любой программе доступны два предварительно объявленных файла со
стандартными файловыми переменными: INPUT - для чтения данных с
клавиатуры и OUTPUT - для вывода на экран. Стандартный Паскаль требует
обязательного упоминания этих файлов в заголовке программы, например,
так
PROGRAM NameOfProfgram( input, output);
В Турбо Паскале это необязательно, вот почему заголовок программы
можно опускать.
Связывание файловой переменной с именем файла
Любые другие файлы, а также логические устройства становятся
доступны программе только после выполнения особой процедуры открытия
файла (логического устройства). Эта процедура заключается в связывании
ранее объявленной файловой переменной с именем существующего или
вновь создаваемого файла, а также в указании направления обмена
информацией: чтение из файла или запись в него.
Файловая переменная связывается с именем файла в результате
обращения к стандартной процедуре ASSIGN:
ASSIGN (<ф.п.>, <имя файла или, л.у.>);
Здесь <ф.п.> - файловая переменная (правильный идентификатор,
объявленный в программе как переменная файлового типа);
<имя файла или л.у.> - текстовое выражение, содержащее имя файла или
логическое устройство.
Если имя файла задается в виде пустой строки, например, ASSIGN(f,"),
то в зависимости от направления обмена данными файловая переменная
связывается со стандартным файлом INPUT или OUTPUT.
91
Имя файла - это любое выражение строкового типа, которое строится
по правилам определения имен в MS DOS (операционной системе ПК):
имя содержит до восьми разрешенных символов; разрешенные
символы - это прописные и строчные латинские буквы, цифры и
символы: !@#$%^&()'~-_
имя начинается с любого разрешенного символа;
за именем может следовать расширение - последовательность до
трех разрешенных символов; расширение, если оно есть, отделяется
от имени точкой.
Перед именем может ставиться так называемый путь к файлу: имя диска
и/или имя текущего каталога и имена каталогов вышестоящих уровней.
Имя диска - это один из символов A...Z , после которого ставится
двоеточие. Имена А: и В: относятся к дисковым накопителям на гибких
дискетах, имена С:. D: и т.д. - к жестким дискам.
За именем диска может указываться имя каталога, содержащего файл.
Если имени каталога предшествует обратная косая черта, то путь к файлу
начинается из корневого каталога, если черты нет - из текущего каталога,
установленного в системе по умолчанию. За именем каталога может
следовать одно или несколько имен каталогов нижнего уровня. Каждому из
них должна предшествовать обратная косая черта. Весь путь к файлу
отделяется от имени файла обратной косой чертой. Максимальная длина
имени вместе с путем - 79 символов, например:
var
finp:text;
fout : file of string;
const
name = ' c:\dir\subdir\out.txt';
assign(finp, '123.dat');
assign(fout, name);
92
Инициация файла
Инициировать файл означает указать для этого файла направление
передачи данных. В Турбо Паскале можно открыть файл для чтения, для
записи информации, а также для чтения и записи одновременно.
Для чтения файл инициируется с помощью стандартной процедуры
RESET:
RESET (<ф. п.>);
Здесь <ф.п.> - файловая переменная, связанная ранее процедурой
ASSIGN с уже существующим файлом или логическим устройством-при-
емником информации.
При выполнении этой процедуры дисковый файл или логическое
устройство подготавливается к чтению информации. В результате специ-
альная переменная-указатель, связанная с этим файлом, будет указывать на
начало файла, т.е. на компонент с порядковым номером 0.
Если делается попытка инициировать чтение из несуществующего
файла или из логического устройства PRN, возникает ошибка периода
исполнения, которая может быть сообщена программе ненулевым значением
встроенной функции IORESULT типа WORD. Например, следующий
фрагмент программы позволяет установить, существует ли требуемый файл
на диске:
var
f : file of char;
assign(f,'myfile.dat');
{$I-} {Отключить контроль ошибок ввода-вывода}
reset(f):
{SI+} {Включить контроль ошибок ввода-вывода)
If IOResult о 0 then
..... {Файл не существует}
else
93
..... {Файл существует}
В этом фрагменте с помощью директивы компилятора {$I-} отключается
автоматический контроль ошибок ввода-вывода. Если этого не сделать, то
отсутствие файла приведет к аварийному завершению программы.
В Турбо Паскале разрешается обращаться к типизированным файлам,
открытым процедурой RESET (т.е. для чтения информации), с помощью
процедуры WRITE (т.е. для записи информации). Такая возможность
позволяет легко обновлять ранее созданные типизированные файлы и при
необходимости расширять их. Для текстовых файлов, открытых процедурой
RESET, нельзя использовать процедуру WRITE или WRITELN.
Стандартная процедура REWRITE (<ф.п.>) инициирует запись
информации в файл или в логическое устройство, связанное ранее с
файловой переменной <ф. п. >. Процедурой REWRITE нельзя инициировать
запись информации в ранее существовавший дисковый файл: при
выполнении этой процедуры старый файл уничтожается и никаких
сообщений об этом в программу не передается. Новый файл
подготавливается к приему информации и его указатель принимает значение
0.
Стандартная процедура APPEND (<ф.п.>) инициирует запись в ранее
существовавший текстовый файл для его расширения, при этом указатель
файла устанавливается в его конец. Процедура APPEND применима только к
текстовым файлам, т.е. их файловая переменная должна иметь тип TEXT (см.
выше). Процедурой APPEND нельзя инициировать запись в типизированный
или нетипизированный файл. Если текстовый файл ранее уже был открыт с
помощью RESET или REWRITE, использование процедуры APPEND приведет
к закрытию этого файла и открытию его вновь, но уже для добавления
записей.
94
Процедуры и функции для работы с файлами
Ниже описываются процедуры и функции, которые можно использовать
с файлами любого вида. Специфика работы с типизированными, текстовыми
и нетипизированными файлами рассматривается в следующих разделах.
Процедура CLOSE. Закрывает файл, однако связь файловой переменной
с именем файла, установленная ранее процедурой ASSIGN, сохраняется.
Формат обращения:
CLOSE (<ф.п.>)
При создании нового или расширении старого файла процедура
обеспечивает сохранение в файле всех новых записей и регистрацию файла в
каталоге. Функции процедуры CLOSE выполняются автоматически по
отношению ко всем открытым файлам при нормальном завершении
программы. Поскольку связь файла с файловой переменной сохраняется,
файл можно повторно открыть без дополнительного использования
процедуры ASSIGN.
Процедура RENAME. Переименовывает файл. Формат обращения:
RENAME (<ф.п.>, <новое имя>)
Здесь <новое имя> - строковое выражение, содержащее новое ими
файла. Перед выполнением процедуры необходимо закрыть файл, если он
ранее был открыт процедурами RESET, REWRITE или APPEND.
Процедура ERASE. Уничтожает файл. Формат обращения:
ERASE(<ф.п.>)
Перед выполнением процедуры необходимо закрыть файл, если он ранее
был открыт процедурами RESET, REWRITE или APPEND.
95
Следующий фрагмент программы показывает, как можно использовать
процедуры RENAME и CLOSE при работе с файлами. Предположим, что
требуется отредактировать файл, имя которого содержит переменная NAME.
Перед редактированием необходимо убедиться, что нужный файл имеется на
диске, и переименовать его - заменить расширение этого файла на .ВАК
(страховочная копия). Если файл с таким расширением уже существует, его
надо стереть.
var
fi : text; {Исходный файл}
fo : text; {Отредактированный файл}
name : string;
name_bak: string;
k,i : word;
const
bak = '.bak';
........
{ Получить в name_bak имя файла с расширением .ВАК: }
k := pos('.',name):
If k=0 then k := length(name) + 1;
name_bak := copy(name,1,k-1) + bak;
{ Проверить существование исходного файла: }
assign(fi,name):
{$I-} reset(fi);
If IOResult о 0 then halt; {Файл не существует}
close(fi):
{ Проверить существование .ВАК-файла: } assign(fo,name_bak);
reset(fo);
{$I+} If IOResult = 0 then
begin {Файл .ВАК существует}
close(fo);
erase(fo);
end;
{ Проверки закончены, подготовка к работе: }
96
rename(fi,name_bak);
reset(fi);
assign(fo,name);
rewrite(fo);
Обратите внимание: проверка на существование BAK-файла в данном примере необходима, так как обращение
rename(fi,name_bak);
вызовет ошибку в случае, если такой файл существует.
Процедура FLUSH. Очищает внутренний буфер файла и, таким
образом, гарантирует сохранность всех последних изменений файла на диске.
Формат обращения:
FLUSH (<ф.п.>);
Любое обращение к файлу в Турбо Паскале осуществляется через
некоторый буфер, что необходимо для согласования внутреннего пред-
ставления файлового компонента (записи) с принятым в ДОС форматом
хранения данных на диске. В ходе выполнения процедуры FLUSH все новые
записи будут действительно записаны на диск. Процедура игнорируется,
если файл был инициирован для чтения процедурой RESET.
Функция EOF(<ф.п.>): BOOLEAN. Логическая функция, тестирующая
конец файла. Возвращает TRUE, если файловый указатель стоит в конце
файла. При записи это означает, что очередной компонент будет добавлен в
конец файла, при чтении - что файл исчерпан.
Процедура CHDIR. Изменение текущего каталога. Формат обращения:
CHDIR (<путь>)
Здесь <путь> - строковое выражение, содержащее путь к устанав-
ливаемому по умолчанию каталогу.97
Процедура GETDIR. Позволяет определить имя текущего каталога
(каталога по умолчанию). Формат обращения:
GETDIR(<ycmpoйcmвo>, <каталог>)
Здесь <устройство> - выражение типа WORD , содержащее номер
устройства: 0 - устройство по умолчанию, 1 - диск А, 2 - диск В и т.д.;
<каталог> - переменная типа STRING, в которой возвращается путь к
текущему каталогу на указанном диске.
Процедура MKDIR. Создает новый каталог на указанном диске. Формат
обращения:
MKDIR(<каталог>);
Здесь <каталог> - выражение типа STRING, задающее путь к каталогу.
Последним именем в пути, т.е. именем вновь создаваемого каталога не может
быть имя уже существующего каталога.
Процедура RMDIR. Удаляет каталог. Формат обращения:
RMDIR(<каталог>);
Удаляемый каталог должен быть пустым, т.е. не содержать файлов или
имен каталогов нижнего уровня.
Функция IORESULT : WORD. Возвращает условный признак последней
операции ввода-вывода.
Если операция завершилась успешно, функция возвращает ноль. Коды
ошибочных операций ввода-вывода представлены в прил.З. Следует
помнить, что IORESULT становится доступной только при отключенном
автоконтроле ошибок ввода-вывода. Директива компилятора {$I-} отклю-
чает, а директива {$I+} включает автоконтроль. Если автоконтроль от-
ключен, а операция ввода-вывода привела к возникновению ошибки,
98
устанавливается флаг ошибки и все последующие обращения к вводу-выводу
блокируются, пока не будет вызвана функция IORESULT.
Ряд полезных файловых процедур и функций становится доступным при
использовании библиотечного модуля DOS.TPU, входящего в стандартную
библиотеку TURBO.TPL . Эти процедуры и функции указаны ниже. Доступ к
ним возможен только после объявления USES DOS в начале программы
(подробнее о работе с модулями см. гл.9).
Текстовые файлы
Текстовые файлы связываются с файловыми переменными, принад-
лежащими стандартному типу TEXT. Текстовые файлы предназначены для
хранения текстовой информации. Именно в такого типа файлах хранятся,
например, исходные тексты программ. Компоненты (записи) текстового
файла могут иметь переменную длину, что существенно влияет на характер
работы с ними.
Текстовый файл трактуется в Турбо Паскале как совокупность строк
переменной длины. Доступ к каждой строке возможен лишь последова-
тельно, начиная с первой. При создании текстового файла в конце каждой
записи (строки) ставится специальный признак EOLN (End Of LiNe -конец
строки). а в конце всего файла - признак EOF (End Of File - конец файла). Эти
признаки можно протестировать одноименными логическими функциями
(см. ниже). При формировании текстовых файлов используются следующие
системные соглашения:
EOLN - последовательность кодов ASCII 13 (CR) и 10 (LF);
EOF - код 26 стандарта ASCII.
Для доступа к записям применяются процедуры READ, READLN,
WRITE, WRITELN. Они отличаются возможностью обращения к ним с
переменным числом фактических параметров, в качестве которых могут
использоваться символы, строки и числа. Первым параметром в любой из
перечисленных процедур может стоять файловая переменная. В этом случае
99
осуществляется обращение к дисковому файлу или логическому устройству,
связанному с переменной процедурой ASSIGN. Если файловая переменная
не указана, происходит обращение к стандартным файлам Input и Output.
Процедура READ. Обеспечивает ввод символов, строки чисел. Формат
обращения:
READ (<ф.п.>,<сп.ввода>);
или
READ (<сп.ввода>);
Здесь <сп.ввода> - список ввода: последовательность из одной или более
переменных типа CHAR, STRING, а также любого целого или вещественного
типа.
При вводе переменных типа CHAR выполняется чтение одного символа
из файла и присваивание считанного значения переменной. Если перед
выполнением чтения указатель файла достиг конца очередной строки. то
результатом чтения будет символ CR (ASCII код 13), а если достигнут конец
файла, то - символ EOF (код 26). При вводе с клавиатуры символ CR
вводится при нажатии на клавишу «Ввод», а символ EOF - при
одновременном нажатии клавиш CTRL и Z .
При вводе переменных типа STRING количество считанных процедурой
и помещенных в строку символов равно максимальной длине строки, если
только раньше не встретились символы CR или EOF. В этом случае сами
символы CR и EOF в строку не помещаются. Если количество символов во
входном потоке данных больше максимальной длины строки, «лишние»
символы до конца строки отбрасываются, а новое обращение к READ
возвращает пустую строку. Таким образом, процедура READ не в состоянии
прочесть последовательность строк: первая строка будет прочитана
100
нормально, а все последующие окажутся пустыми. Для ввода после-
довательности строк нужно использовать процедуру READLN (см. ниже).
При вводе числовых переменных процедура READ вначале выделяет
подстроку во входном потоке по следующему правилу: все ведущие
пробелы, символы табуляции и маркеры конца строк EOLN пропускаются;
после выделения первого значащего символа, наоборот, любой из пе-
речисленных символов или символ EOF служат признаком конца подстроки.
Выделенная таким образом подстрока затем рассматривается как символьное
представление числовой константы соответствующего типа и преобразуется
во внутреннее представление, а полученное значение присваивается
переменной. Если в подстроке был нарушен требуемый формат
представления численной константы, возникает ошибка ввода-вывода. Если
при пропуске ведущих пробелов встретился символ EOF, переменная
получает значение 0. Отметим, что в Турбо Паскале не предусмотрен ввод
шестнадцатиричных констант.
Процедура READ прекрасно приспособлена к вводу чисел. При об-
ращении к ней за вводом очередного целого или вещественного числа
процедура «перескакивает» маркеры конца строк, т.е. фактически весь файл
рассматривается ею как одна длинная строка, содержащая текстовое
представление чисел. В сочетании с проверкой конца файла функцией EOF
процедура READ позволяет организовать простой ввод массивов данных,
например, так:
const
N = 1000; { максимальная длина ввода }
var
f : text;
m : array [1..N] of real;
i : Integer;
BEGIN
assign(f, 'prog.dat');
101
reset(f);
i := 1;
while not EOF(f) and (I <= N) do
begin
read(f,m[i]);
inc(i);
end;
сlоse(f);
end.
Процедура READLN. Обеспечивает ввод символов, строк и чисел. Эта
процедура идентична процедуре READ за исключением того, что после
считывания последней переменной оставшаяся часть строки до маркера
EOLN пропускается, поэтому следующее обращение к READLN или READ
начинается с первого символа новой строки. Кроме того, эту процедуру
можно вызвать без параметра <сп.ввода> (см. процедуру READ), что
приведет к пропуску всех символов текущей строки вплоть до EOLN.
Процедура WRITE. Обеспечивает вывод информации в текстовый файл
или передачу ее на логическое устройство. Формат обращения:
WRITE(<ф.п.>,<сп.вывода>) или WRITE(<сп.вывода>);
Здесь <сп.вывода> - список вывода: последовательность из одного или
более выражений типа CHAR, STRING, BOOLEAN, а также любого целого
или вещественного типа.
Файловая переменная <ф.п.>, если она указана, должна быть пред-
варительно описана как переменная типа TEXT и связана с именем файла
или логическим устройством процедурой ASSIGN. Если файловая пере-
менная отсутствует, подразумевается вывод в стандартный файл OUTPUT,
который обычно связан с экраном ПК.
Любой элемент списка вывода может иметь форму
102
OutExpr [ : MInWidth [ : DecPlaces ] ]
Здесь OUTEXPR - выводимое выражение;
MINWIDTH, DECPLACES - выражения типа WORD (квадратные скобки
означают возможность отсутствия заключенных в них параметров).
Подпараметр MINWIDTH , если он присутствует, указывает мини-
мальную ширину поля, в которое будет записываться символьное пред-
ставление значения OUTEXPR. Если символьное представление имеет
меньшую длину, чем MINWIDTH, оно будет дополнено слева пробелами,
если - большую длину, то подпараметр MINWIDTH итерируется и выводится
необходимое число символов.
Подпараметр DECPLACES задает количество десятичных знаков в
дробной части вещественного числа. Он может использоваться только
совместно с MINWIDTH и только по отношению к выводимому выражению
одного из вещественных типов.
Если ширина поля вывода не указана, соответствующий параметр
выводится вслед за предыдущим без какого-либо их разделения.
Символы и строки передаются выводному файлу без изменений, но
снабжаются ведущими пробелами, если задана ширина поля вывода и эта
ширина больше требуемой для вывода.
При выводе логических выражений в зависимости от их значения
выводятся строки TRUE или FALSE. (Ввод логических констант проце-
дурами READ или READLN не предусмотрен).
При выводе на экран в случае, когда длина выводимой последова-
тельности символов превышает ширину экрана или созданного на нем окна,
«лишние» символы переносятся на следующую экранную строку. При
заполнении экрана или окна его содержимое сдвигается вверх на одну
строку.
Процедура WRITELN. Эта процедура полностью идентична процедуре
WRITE за исключением того, что выводимая строка символов завершается
103
кодами CR и LF. При вызове WRITELN можно опускать параметр
<сп.вывода>: в этом случае в файл передается маркер EOLN, что при выводе
на экран приведет к переводу курсора в начало следующей строки.
Логическая функция EOLN. Возвращает TRUE, если во входном
текстовом файле достигнут маркер конца строки. Формат обращения:
EOLN(<ф.n.>);
Если параметр <ф.п.> опущен, функция проверяет стандартный файл
INPUT.
Существует некоторое отличие в работе функций EOLN и EOF с
дисковыми файлами и логическими устройствами. Дело в том, что для
логического устройства невозможно предвидеть, каким будет результат
чтения очередного символа. Поэтому при работе с логическим устройством
функция EOLN возвращает TRUE, если последним считанным с устройства
символом был EOLN или EOF, в то время как при чтении с диска TRUE
возвращается в случае, если следующим считываемым символом будет
EOLN или EOF. Аналогичное различие наблюдается и в функции EOF: для
логического устройства TRUE возвращается в случае, если последним
символом был EOF, а при чтении с диска - если следующим считываемым
символом будет EOF. Иными словами, функции тестируют соответствующие
признаки для логического устройства после очередного чтения, а для файла -
перед чтением.
Логическая функция SEEKEOLN. Пропускает все пробелы и знаки
табуляции до маркера конца строки EOLN или до первого значащего символа
и возвращает TRUE, если маркер обнаружен. Формат обращения;
SEEKEOLN(<ф.п.>);
Если параметр <ф.п.> опущен, функция проверяет стандартный файл
INPUT,
104
Логическая функция SEEKEOF. Пропускает все пробелы, знаки
табуляции и маркеры конца строки EOLN до маркера конца файла или до
первого значащего символа и возвращает TRUE, если маркер обнаружен.
Формат обращения:
SEEKEOF(<ф.п.>);
Если параметр <ф.п.> опущен, функция проверяет стандартный файл
INPUT.
В следующем примере, иллюстрирующем работу с текстовым файлом,
подсчитывается общее количество символов в файле и результат делится на
40000 - таким способом можно оценить объем рукописи в так называемых
учетно-издательских листах:
var
f: text;
s: string;
const
Sum: longint = 0; {Здесь будет количество символов}
BEGIN
Writе('Имя файла: '); {Запросить}
Readln(s); {и ввести имя файла}
assign(f ,s);
Reset(f); {Открыть файл}
while not EOF(f) do {Подсчитать}
begin {количество}
Readln(f,s); {символов}
inc(Sum, Length(s)) {в тексте}
end; {этой программы}
Close(f); {Закрыть файл}
Writeln('Объем - ',Sum/40000:6:2,' уч.изд.л.');
END.
105
Типизированные файлы
Длина любого компонента типизированного файла строго постоянна,
что дает возможность организовать прямой доступ к каждому из них (т.е.
доступ к компоненту по его порядковому номеру).
Перед первым обращением к процедурам ввода-вывода указатель файла
стоит в его начале и указывает на первый компонент с номером 0. После
каждого чтения или записи указатель сдвигается к следующему компоненту
файла. Переменные в списках ввода-вывода должны иметь тот же тип, что и
компоненты файла. Если этих переменных в списке несколько, указатель
будет смещаться после каждой операции обмена данными между
переменными и дисковым файлом.
Процедура READ. Обеспечивает чтение очередных компонентов
типизированного файла. Формат обращения:
READ (<ф.п.>,<сп.ввода>);
Здесь <сп.ввода> - список ввода, содержащий одну или более
переменных такого же типа, что и компоненты файла.
Файловая переменная <ф. п. > должна быть объявлена предложением
FILE OF... и связана с именем файла процедурой ASSIGN. Файл необходимо
открыть процедурой RESET. Если файл исчерпан, обращение к READ
вызовет ошибку ввода-вывода.
Процедура WRITE. Используется для записи данных в типизированный
файл. Формат обращения:
WRITE (<ф.п.>,<сп.вывода>);
Здесь <сп. вывода> - список вывода, содержащий одно или более
выражений того же типа, что и компоненты файла.
Процедура SEEK. Смещает указатель файла к требуемому компоненту.
Формат обращения:
106
SEEK (<ф. п. >,<N компонента>);
Здесь <N компонента> - выражение типа LONGINT, указывающее
номер компонента файла.
Первый компонент файла имеет номер 0. Процедуру нельзя применять к
текстовым файлам.
Функция FILESIZE. Возвращает значение типа LONGINT, которое
содержит количество компонентов файла. Формат обращения:
FILESIZE(<ф.п.>);
Функцию нельзя использовать для текстовых файлов. Чтобы пере-
местить указатель в конец файла, можно написать:
seek(FileVar, FileSize(FileVar));
где FILEVAR - файловая переменная.
Функция FILEPOS. Возвращает значение типа LONGINT, содержащее
порядковый номер компонента файла, который будет обрабатываться
следующей операцией ввода-вывода. Формат обращения:
FILEPOS(<ф.п.>);
Функцию нельзя использовать для текстовых файлов. Первый ком-
понент файла имеет порядковый номер 0.
Нетипизированные файлы
Нетипизированные файлы объявляются как файловые переменные типа
FILE и отличаются тем, что для них не указан тип компонентов. Отсутствие
типа делает эти файлы, с одной стороны, совместимыми с любыми другими
107
файлами, а с другой - позволяет организовать высокоскоростной обмен
данными между диском и памятью.
При инициации нетипизированного файла процедурами RESET или
REWRITE можно указать длину записи нетипизированного файла в байтах.
Например, так:
var
f : file;
..........
assign(f, 'myfile.dat');
reset(f,5l2);
Длина записи нетипизированного файла указывается вторым
параметром при обращении к процедурам RESET или REWRITE, в качестве
которого может использоваться выражение типа WORD. Если длина записи
не указана, она принимается равной 128 байтам.
При работе с нетипизированными файлами могут применяться все
процедуры и функции, доступные типизированным файлам, за исключением
READ и WRITE, которые заменяются соответственно высокоскоростными
процедурами BLOCKREAD и BLOCKWRITE . Для вызова этих процедур
используются следующие предложения:
BLOCKREAD(<ф.п.>,<буф>,<N>[,<NN>]);
BLOCKWRITE(<ф.п.>,<буф>,<N>[,<NN>]);
Здесь <буф> - буфер: имя переменной, которая будет участвовать в
обмене данными с дисками;
<N> - количество записей, которые должны быть прочитаны или
записаны за одно обращение к диску;
<NN> - необязательный параметр, содержащий при выходе из про-
цедуры количество фактически обработанных записей.
108
За одно обращение к процедурам может быть передано до N*RECS байт,
rppRECS - длина записи нетипизированного файла. Передача идет, начиная с
первого байта переменной <буф>. Программист должен позаботиться о том,
чтобы длина внутреннего представления переменной <буф> была
достаточной для размещения всех N*RECS байт при чтении информации с
диска. Если при чтении у казана переменная <буф> недостаточной длины
или если в процессе записи на диск не окажется нужного свободного
пространства, возникнет ошибка ввода-вывода, которую можно за-
блокировать, указав необязательный параметр <NN> (переменная типа
WORD).
После завершения процедуры указатель смещается на <NN> записей.
Процедурами SEEK, FILEPOS и FILESIZE можно обеспечить доступ к любой
записи нетипизированного файла.
Указатели и динамическая память
Указатель - это ссылка на данные или код вашей программы. Он
представляет адрес в памяти элемента, на который указывает. Использование
указателей позволяет писать большие и более гибкие программы и особенно
полезно, когда вы начинаете писать объектно-ориентированные программы.
Данная глава должна помочь вам лучше использовать указатели,
независимо от того, начинаете ли вы работать с Паскалем или уже давно
программируете на Паскале, но раньше не работали с указателями. Она
охватывает следующие темы:
Зачем и когда используются указатели.
Что такое указатель.
Как использовать указатели.
Эффективная работа с указателями.
109
1. Для чего используются указатели?
Рано или поздно каждый программист, работающий на Паскале,
попадает в ситуацию, требующую использования указателей. Указатели
требуется применять в следующих случаях:
Если ваша программа работает с большими объемами данных
(общий объем которых превышает 64К).
Если ваша программа во время компиляция использует данные
неизвестного размера.
Если программа использует временные буферы данных.
Если ваша программа работает с несколькими типами данных.
Если ваша программа использует связанные списки данных или
объектов.
Давайте подробнее рассмотрим каждую причину использования
указателей.
Работа с большими объемами данных
По мере того как программы становятся более сложными, и требуются
работа с большим количеством данных, область объемом в 64К,
зарезервированная в Turbo Pascal для данных, может оказаться
недостаточной, чтобы содержать все необходимые программе данные.
Указатели позволяют вам обойти эту проблему.
Когда вы описываете в Turbo Pascal глобальные переменные,
компилятор выделяет для них память в области, которая называется
сегментом данных. Сегмент данных имеет максимальный размер 64К. Это
означает, что общий объем всех ваших глобальных переменных не может
превышать 64К. Для многих программ этот предел значения не имеет, но в
некоторых случаях вам может потребоваться больший объем.
Примечание: Локальные переменные не помещаются в сегмент данных
и в пределе 64К не учитываются.
110
Предположим, например, что у вас есть программа, требующая массива
в 400 строк по 100 символов каждая. Для этого массива требуется примерно
40К, что меньше максимума в 64К. Если остальные ваши переменные
помещаются в оставшиеся 24К, массив такого объема проблемы не
представляет.
Но что если вам нужно два таких массива? Это потребовало бы 80К, и
64К сегмента данных не хватит. Чтобы работать с большими объемами
данных, вам нужно использовать динамически распределяемую область
памяти. Ваша программа может выделить в динамически распределяемой
области 80К, поддерживая указатель в виде ссылку на адрес данных.
Указатель занимает в сегменте данных только 4 килобайта.
Что такое динамически распределяемая область памяти?
Динамически распределяемая область памяти - это вся память, которую
ваша операционная система делает доступной для программы и которая не
используется ее кодом, сегментом данных и стеком. Объемом
распределяемой динамической памяти вы можете управлять с помощью
директивы компилятора $M.
Обычно в Turbo Pascal вы можете зарезервировать память в
динамически распределяемой области, получить к ней доступ через
указатель, а затем снова освободить память. Подробности о распределении
памяти в динамически распределяемой области вы можете найти ниже в
разделе "Как использовать указатели?".
Работа с данными неизвестного размера
Некоторые элементы данных Turbo Pascal (в частности, строки и
массивы) требуют задания размеров во время компиляции, даже если при
выполнении программы вам не потребуется вся выделенная память. Простым
примером может быть программа, считывающая вводимую пользователем
строку, например, имя пользователь. Чтобы записать имя в обычной
строковой переменной, вам потребовалось бы зарезервировать достаточно
111
памяти для максимальной возможной строки, даже если набранное имя
содержит всего несколько букв. Если вы распределяете переменные в
динамически распределяемой области памяти во время выполнения, то
можете выделить точно столько байт, сколько необходимо для фактической
строки данных.
Это тривиальный пример, но в приложении, содержащем сотни и тысячи
таких элементов данных (таких как множественные окна или считываемые из
файлов списки) выделение точного объема пространства может вместо
ситуации нехватки памяти привести к успешному выполнению.
Работа с временными буферами данных
Указатели и динамически распределяемая область памяти особенно
полезны в тех случаях, когда вам требуется временное выделение памяти, и
вы не хотите удерживать ее на все время выполнения программы. Например,
редактору файлов обычно требуется буфер данных для каждого
редактируемого файла. Вместо описания на этапе компиляции, что вам
необходимо определенное число буфером заданного размера, которые всегда
распределяются для файлов, вы можете выделить их столько, сколько
необходимо в каждый конкретный момент, что делает память доступной для
других целей.
Другим общим примером использования временной памяти является
сортировка. Обычно когда вы сортируете большой объем данных, то делаете
копию массива, сортируете копию, а затем записываете отсортированные
данные обратно в исходный массив. Это сохраняет целостность ваших
данных, но требует также наличия во время сортировки двух копий данных.
Если вы хотите распределить сортируемый массив в динамически
распределяемой памяти, то можете отсортировать его и скопировать обратно
в оригинал, а затем уничтожить сортируемый массив, освободив память для
других нужд.
112
Работа с несколькими типами данных
Одной из общих причин использования указателей является ссылка на
переменные структуры данных, то есть записи или массивы, которые не
всегда имеют одну и ту же структуру. Например, вы можете выделить блок
памяти, зарезервированный для "протокола" элементов строк различной
длины, набранных в поле ввода данных. Чтобы прочитать список протокола,
подпрограмма должна просмотреть блок и найти отдельные строки. Для
указания начала блока вы можете использовать простой указатель. В этом
случае указатель работает аналогично передаче функции или процедуре
нетипизированного параметра var - вы просто хотите сообщить, где что-то
находится, без указания того, что это такое.
Примечание: О нетипизированных параметрах-переменных
рассказывается в Главе ?? ("Процедуры и функции").
Связанные списки
Одним из общих случаев использования указателей является соединение
связанных списков записи. Во многих простых приложениях типа баз данных
вы можете размещать записи данных в массивах или типизированных
файлах, но иногда требуется что-то более гибкое, чем массив, который имеет
фиксированный размер. Распределяя динамические записи, так что каждая
запись имеет поле, указывающее на следующую запись, вы можете
построить список, содержащий столько элементов, сколько вам требуется.
Что такое указатель?
Указатель - это какой-либо адрес в памяти вашего компьютера. Это
может быть адрес переменной, записи данных, либо процедуры или функции.
Обычно вам не важно, где расположен элемент в памяти. Вы можете просто
ссылаться на него по имени, и Turbo Pascal знает, где его нужно искать.
Именно это происходит, когда вы описываете переменную. Например,
если программа включает в себя следующий код, то вы указываете
113
компилятору на необходимость зарезервировать область в памяти, на
которую будете ссылаться по имени SomeNumber.
var SomeNumber: Integer;
Вам не нужно беспокоиться о том, где SomeNumber находится в памяти.
Именно для этого задается имя.
Адрес размещения SomeNumber в памяти можно найти с помощью
операции @. @SomeNumber - это адрес вашей целочисленной переменной.
Вы можете присвоить этот адрес переменной-указателю, то есть переменной,
содержащей адрес данных или кода в памяти.
Ссылочный тип
Чтобы хранить указатели, вам требуется переменная-указатель, а для
создания переменной-указателя вам необходим ссылочный тип (или тип
"указатель"). Простейшим ссылочным типом является стандартный тип с
именем Pointer. Переменная типа Pointer - это общий (нетипизированный)
указатель, то есть, просто адрес. Он не содержит информации о том, на что
он указывает.
Таким образом, чтобы использовать тот же пример SomeNumber, вы
можете присвоить его адрес переменной-указателю:
var
SomeNumber: Integer;
SomeAddress: Pointer;
begin
SomeNumber := 17; {присвоить SomeNumber
значение}
SomeAddress := @SomeNumber; {присвоить SomeAddress
адрес}
SomeAddress := Addr(SomeNumber); {другой способ
получения
адреса}
114
end.
Нетипизированные указатели в Паскале не используются, поскольку они
очень ограничены. Они наиболее полезны, когда указываемый элемент будет
изменяться, так как нетипизированный указатель совместим с любым другим
указателем. Типизированные указатели значительно более полезны, и как вы
узнаете в следующем разделе, они более надежны.
Типизированные указатели
Обычно вы определяете ссылочные типы, которые указывают на
конкретный вид элемента, например, целое значение или запись данных. Как
вы далее увидите, можно извлечь преимущество из того факта, что указателю
известно, на что он указывает. Чтобы определить типизированный указатель,
вы можете описать новый тип, определенный символом каре (^), за которым
следуют один или более идентификаторов. Например, чтобы определить
указатель на Integer, вы можете сделать следующее:
type PIneger = ^Integer;
Теперь вы можете описать переменные типа PInteger. Если вы не
собираетесь часто использовать ссылочный тип, то можете просто описать
переменные, как указатели на уже определенный тип. Например, если вы
определили PInteger как ^Integer, то следующие описания переменной
эквивалентны:
var
X: ^Integer:
Y: PInteger;
115
Разыменование указателей
До сих пор мы видели, как можно присваивать указателям значения, но
если вы не можете получить значения обратно, польза от этого невелика.
Разыменовав типизированный указатель, вы можете интерпретировать так,
как если бы это была переменная типа, на которую он указывает. Чтобы
разыменовать указатель, поместите символ каре (^) после идентификатора
указателя.
Ниже показаны некоторые примеры разыменования указателя:
type PInteger = ^Integer;
var
SomeNumber: Integer; { присвоить SomeNumber 17 }
SomeAddress := @SomeNumber; { SomeAddress указывает
на SomeNumber }
Writeln(SomeNumber); { напечатать 17 }
Writeln(SomeAddress); { не допускается; указатели
печатать нельзя }
Writeln(SomeAddress^); { напечатать 17 }
AnotherAddress := SomeAddress; { также указывает на
SomeNumber }
AnotehrAddress^ := 99; { новое значение для
SomeNumber }
Writeln(SomeNumber); { напечатать 99 }
end.
Наиболее важными строками здесь являются следующие:
AnotherAddress := SomeAddress; { также указывает
на SomeNumber }
AnotehrAddress^ := 99; { новое значение для
SomeNumber }
116
Как использовать указатели?
Теперь вы получили достаточно хорошее представление о том, в каких
ситуациях вы можете использовать указатели, и можно рассмотреть их
фактическое применение. В данном разделе охватываются следующие темы:
Распределение динамических переменных.
Освобождение выделенной для динамических переменных памяти.
Распределение и освобождение выделенных объемов памяти.
Проверка доступного в динамически распределяемой области
пространства.
Turbo Pascal предусматривает две пары процедур для выделения и
освобождения памяти, распределяемой для динамических переменных. Чаще
всего используются процедуры New и Dispose, которые отвечают
большинству потребностей. Процедуры GetMem и FreeMem выполняют те
же функции, но на более низком уровне.
Выделение памяти для динамических переменных
Одним из наиболее важных моментов использования указателей
является распределение динамических переменных в динамически
распределяемой области памяти. Turbo Pascal предусматривает два способа
выделения для указателя памяти: процедура New и процедура GetMem.
Использование New как процедуры
New - это очень простая процедура. После описания переменной-
указателя вы можете вызвать процедуру New для выделения пространства в
динамически распределяемой памяти для указываемого переменной
элемента. Приведем пример:
var
IntPointer: ^Integer;
StringPointer: ^String;
begin
117
New(IntPointer); { выделяет в динамически
распреде-
ляемой области два байта }
New(StringPointer); { выделяет в динамически
распреде-
. ляемой области 256 байт }
.
.
end.
После вызова процедуры New переменная-указатель указывает на
память, выделенную в динамически распределяемой памяти. В данном
примере IntPointer указывает на двухбайтовую область, выделенную
процедурой New, а IntPointer^ - это допустимая целочисленная переменная
(хотя это целочисленное значение еще не определено). Аналогично,
StringPointer указывает на выделенный для строки 256-байтовый блок, а его
разыменование дает доступную для использования строковую переменную.
Использование New как функции
Кроме выделения памяти для конкретной динамической переменной вы
можете использовать New как функцию, возвращающую указатель
конкретного типа. Например, если PInteger - это тип, определенный как
^Integer, а IntPopinter имеет тип PInteger, то следующие два оператора
эквивалентны:
IntPointer := New(PInteger);
Это особенно полезно в случаях, когда может потребоваться
присваивать переменной-указателю элементы различных типов. Иногда
желательно распределять динамическую переменную, не присваивая явно
указатель конкретной переменной. Вероятно, вы можете сделать это только
создав для процедуры и функции параметр:
118
SomeProcedure(New(PointerType));
В этом случае SomeProcedure будет добавлять передаваемый параметр к
некоторому списку. В противном случае распределяемая память будет
потеряна. Библиотеки Borland Turbo Vision и Turbo Pascal широко
используют этот метод для присваивания динамических объектов спискам.
Освобождение памяти, выделенной для динамических переменных
Память, распределенная для переменных с помощью New, после
завершения работы с ними должна освобождаться. Это позволит
использовать динамически распределяемую память для других переменных.
Чтобы освободить память, выделенную для динамической переменной, вы
должны использовать процедуру Dispose. В предыдущем примере вы можете
добавить следующее:
Dispose(StringPointer);
Dispose(IntPointer);
Нужно помнить, что если вы распределяете динамические переменные с
помощью New, то освобождать выделенную для них память после
завершения работы с этими переменными нужно с помощью Dispose.
Процедуры GetMem и FreeMem
Иногда нежелательно выделять память тем способом, как это делает
New. Вам может потребоваться выделить больше или меньше памяти, чем
это делает New по умолчанию, либо до начала выполнения вы можете просто
не знать, сколько памяти вам нужно использовать. Turbo Pascal выполняет
такое распределение с помощью процедуры GetMem.
Процедура GetMem воспринимает два параметра: переменную-
указатель, для которой вы хотите распределить память, и число
распределяемых байт.
119
Динамическое выделение памяти для строки
Пусть, например, у вас есть прикладная программа, которая считывает
1000 строк из файла и записывает их в динамическую память. Вы не знаете,
насколько длинной будет каждая из этих строк, поэтому вам потребуется
описать строковый тип такого размера, который будет соответствовать
максимальной возможной строке. Если предположить, что не все строки
имеют максимальную длину, то у вас будет бесполезно использоваться
память.
Чтобы решить эту проблему, вы можете считать каждую строку в буфер,
затем выделить столько памяти, сколько требуется для фактических данных в
строке. Пример этого показан ниже:
type PString = ^String;
var
ReadBuffer: String;
LinewRead: array[1..1000] of PString;
TheFile: Text;
LineNumber: Integer;
begin
Assign(TheFile, 'FOO.TXT');
Reset(TheFile);
for LineNumber := 1 to 1000 do
begin
Readln(ReadBuffer);
GetMem(LinesRead[LineNumber], Length(ReadBuffer) + 1);
LinesRead[LineNumber]^ := ReadBuffer;
end;
end.
120
Вместо выделения для строк 256К (256 символов на строку 1000 раз) вы
выделили 4К (4 байта на указатель 1000 раз), плюс объем, фактически
занимаемый текстом.
Освобождение выделенной памяти
Аналогично тому, как требуется освобождать память, выделенную с
помощью New, вам нужно освобождать память, распределенную с помощью
процедуры GetMem. Это можно сделать с помощью процедуры FreeMem.
Аналогично тому, как каждому вызову New должен соответствовать парный
вызов Dispose, каждому вызову процедуры GetMem должен соответствовать
вызов FreeMem.
Как и GetMem, процедура FreeMem воспринимает два параметра:
освобождаемую переменную и объем освобождаемой памяти. Важно, чтобы
объем освобождаемой памяти точно совпадал с объемом выделенной памяти.
New и Dispose, основываясь на типе указателя, всегда знают, сколько байт
нужно выделять или освобождать. Но в случае GetMem и FreeMem объем
выделяемой памяти находится всецело под вашим контролем.
Если вы освободите меньше байт, чем было выделено, то оставшиеся
байты теряются (происходит "утечка" динамически распределяемой памяти).
Если вы освободите большее число байт, чем было выделено, то можете
освободить память, распределенную для другой переменной, что может
привести к порче данных. В защищенном режиме освобождение большего
объема памяти, чем было выделено, вызовет ошибку по нарушению защиты
(GP).
Предположим, например, что вы собираетесь выделить память для
одной или более записей данных типа TCheck:
type
PCheck = ^ TCheck;
TCheck = record
Amount: Real;
121
Mounth: 1..12;
Day: 1..31;
Year: 1990..2000;
Payee: string[39];
end.
Каждая запись типа TCheck занимает 50 байт, поэтому, если у вас есть
переменная ThisCheck типа PCheck, вы можете распределить динамическую
запись следующим образом:
GetMem(ThisGheck, 50);
а позднее освободить ее с помощью вызова:
FreeMem(ThisCheck, 50);
Использование с процедурой GetMem функции SizeOf
Однако убедиться, что вы каждый раз выделяете и освобождаете один и
тот же объем памяти, недостаточно. Вы должны обеспечить распределение
правильного объема памяти. Предположим, вы изменили определение
TCheck. Например, если вы переопределили TCheck.Payee как 50-
символьную строку вместо 39-символьной, то не сможете получить и
освобождать достаточно памяти. Надежнее всего использовать в программе
функцию SizeOf, например:
GetMem(ThisCheck, SizeOf(TCheck));
.
.
.
FreeMem(ThisCheck, SizeOf(TCheck));
122
Это не только обеспечивает, что вы выделяете и освобождаете один и
тот же объем, но гарантирует, что при изменении размера типа ваша
программа все равно будет выделять нужную память.
Проверка объема доступной динамически распределяемой памяти
В Turbo Pascal определены две функции, возвращающие важную
информацию о динамически распределяемой области памяти: MemAvail и
MaxAvail.
Функция MemAvail возвращает общее число байт, доступных для
распределения в динамической памяти. Перед выделением большого объема
в динамически распределяемой памяти полезно убедиться, что такой объем
памяти доступен.
Функция MaxAvail возвращает размер наибольшего доступного блока
непрерывной памяти в динамически распределяемой области. Первоначально
при запуске программы MaxAvail равно MemAvail, поскольку вся
динамически распределяемая область памяти является доступной и
непрерывной. После распределения нескольких блоков памяти пространство
в динамически распределяемой области скорее всего станет
фрагментированным. Это означает, что между частями свободного
пространства имеются распределенные блоки. Функция MaxAvail возвращает
размер наибольшего свободного блока.
Общие проблемы использования указателей
Указатели позволяют вам делать в Паскале некоторые важные вещи, но
есть пара моментов, которые при работе с указателями нужно отслеживать.
При использовании указателей допускаются следующие общие ошибки:
разыменование неинициализированных указателей;
потери динамически распределяемой памяти ("утечки").
123
Разыменование неинициализированных указателей
Одним из общих источников ошибок при работе с указателями является
разыменование указателя, который еще не был инициализирован. Как и в
случае других переменных Паскаля, значение переменной-указателя не будет
определено, пока вы не присвоите ей значение, так что она сможет указывать
на какой-то адрес в памяти.
Перед использованием указателей им всегда нужно присваивать
значения. Если вы разыменовываете указатель, которому еще не присвоено
значение, то считанные из него данные могут представлять собой случайные
биты, а присваивание значения указываемому элементу может затереть
другие данные, вашу программу или даже операционную систему. Это
звучит несколько пугающе, но при определенной дисциплине такие вещи
легко отслеживаются.
Использование пустого указателя
Чтобы избежать разыменования указателей, которые не указывают на
что-либо значащее, нужен некоторый способ информирования о том, что
указатель недопустим. В Паскале предусмотрено зарезервированное слово
nil, которое вы можете использовать в качестве содержательного значения
указателей, которые в данный момент ни на что не указывают. Указатель nil
является допустимым, но ни с чем не связанным. Перед разыменованием
указателя вы должны убедиться, что он отличен от nil (не пуст).
Предположим, например, что у вас есть функция, возвращающая
указатель на некоторый элемент в памяти. Вы можете указать, что такая
функция не может найти элемент, возвращая значение nil.
var ItemPointer: Pointer;
function FindIten: Pointer;
begin
.
124
.
.
{ найти элемент, возвращая указатель на него или
nil,
если элемент не найден }
end;
begin
ItemPointer := nil; { начнем в предположении
nil }
ItemPointer := FindItem; { вызвать
функцию }
if ItemPointer <> nil then ... { для надежности разымено-
вания ItemPointer }
end.
Потери динамически распределяемой памяти
При использовании динамически распределяемых переменных часто
возникает общая проблема, называемая утечкой динамической памяти.
Утечка памяти - это ситуация, когда пространство выделяется в динамически
распределяемой памяти и затем теряется - по каким-то причинам ваш
указатель не указывает больше на распределенную область, так что вы не
можете освободить пространство.
Общей причиной утечек памяти является переприсваивание
динамических переменных без освобождения предыдущих. Простейшим
случаем является следующий:
var IntPointer: ^Integer;
begin
New(IntPointer);
New(IntPointer);
125
end.
При первом вызове New в динамически распределяемой памяти
выделяется 8 байт, и на них устанавливается указатель IntPointer. Второй
вызов New выделяет другие 8 байт, и IntPointer устанавливается на них.
Теперь у вас нет указателя, ссылающегося на первые 8 байт, поэтому вы не
можете их освободить. В программе эти байты будут потеряны.
Естественно, утечка памяти может быть не такой очевидной, как в
Примере 8.6. Выделение памяти почти никогда не происходит в
последовательных операторах, но может выполняться в отдельных
процедурах или далеко отстоящих друг от друга частях программы. В любом
случае лучший способ отслеживания динамических переменных это
установка их в nil при освобождении. Тогда при попытке распределить их
снова вы можете убедиться что они имеют значение nil:
var IntPointer: ^Integer;
begin
New(IntPointer);
.
.
.
Dispose(IntPointer);
IntPointer := nil;
.
.
.
if IntPointer = nil then New(IntPointer);
end.
126
Модули Turbo Pascal
В данной главе разъясняется, что такое модуль, как он используется,
какие встроенные модули доступны пользователю, как писать собственные
программные модули и как компилировать их.
Turbo Pascal обеспечивает вам доступ к большому числу встроенных
констант, типов данных, переменных, процедур и функций. Некоторые из
них специфичны для Turbo Pascal, другие специфичны для приложений
Windows. Их количество велико, однако, в своей программе вы редко
используете их все сразу. Поэтому они разделены на связанные группы,
называемые модулями. В этом случае можно использовать только те модули,
которые необходимы в программе.
Используя модули, вы можете разбивать программу на отдельные части
и компилировать их отдельно. Программный модуль (unit) представляет
собой набор констант, типов данных, переменных, процедур и функций,
которые могут совместно использоваться несколькими программами.
Каждый модуль аналогичен отдельной программе на Паскале: он может
иметь основное тело, которое вызывается перед запуском вашей программы
и осуществляет необходимую инициализацию.
Все описания внутри модуля связаны друг с другом. Например, модуль
Strings содержит все описания, необходимые для подпрограмм работы со
строками с завершающим нулем.
Turbo Pascal предоставляет пользователю ряд стандартных модулей,
таких как System, Crt WinCrt и др. Они поддерживают наши программы
Turbo Pascal и все записаны в одну из трех библиотек исполняющей системы
(в зависимости от целевой платформы).
Ваша программа может использовать любую из процедур и функций в
этих модулях, и вам не потребуется писать их заново.
127
Структура модуля
Структура модуля аналогична структуре программы, однако есть
несколько существенных различий. Например, рассмотрим модуль:
unit <идентификатор>;
interface
uses <список модулей>; { Необязательный }
{ глобальные описания }
implementation
uses <список_модулей>; { Необязательный }
{ локальные описания }
{ реализация процедур и функций }
begin
{ код инициализации }
end.
Заголовок модуля начинается зарезервированным словом unit, за
которым следует имя модуля (идентификатор) точно так же, как и в случае
имени программы. Следующим элементом в модуле является ключевое слово
interface. Оно обозначает начало интерфейсной секции модуля - части,
доступной всем другим модулям или программам, в которых он
используется.
Программный модуль может использовать другие модули, для этого они
определяются в операторе uses. Оператор uses (если он имеет место) может
содержаться в двух местах. Во-первых, он может следовать сразу после
ключевого слова interface. В этом случае любые константы и типы данных,
описанные в интерфейсной секции этих модулей, могут использоваться в
любом описании в интерфейсной части данного модуля.
Во-вторых, он может следовать немедленно за ключевым словом
implementation. В этом случае все описания из этих модулей могут
использоваться только в секции реализации.
128
Интерфейсная секция
Интерфейсная часть - "общедоступная" часть в модуле - начинается
зарезервированным словом interface, следует сразу после заголовка модуля и
заканчивается перед зарезервированным словом implementation. Интерфейс
определяет, что является "видимым" (доступным) для любой программы (или
модуля), использующей данный модуль.
В интерфейсной части (секции) модуля можно определять константы,
типы данных, переменные, процедуры и функции. Как и в программе, они
могут быть расположены в любом порядке, и секции могут встречаться
повторно (например, ваша программа может содержать секцию var, за
которой следует секция const, а затем другая секция var).
Процедуры и функции, видимые для любой программы, использующей
данный модуль, описываются в секции интерфейса, однако их
действительные тела - реализации - находятся в секции реализации. Вам не
нужно использовать описания forward, и они не допускаются. В
интерфейсной части перечисляются все заголовки процедуры и функции.
Секция реализации содержит программную логику процедур и функций.
Секция реализации
Секция реализации - "приватная" часть - начинается зарезервированным
словом implementation. Все, что описано в секции интерфейса, является
видимым в секции реализации: константы, типы, переменные, процедуры и
функции. Кроме того, в секции реализации могут быть свои дополнительные
описания, которые не являются видимыми для программ, использующих этот
модуль. Программа не знает об их существовании и не может ссылаться на
них или обращаться к ним. Однако, эти скрытые элементы могут
использоваться (и, как правило, используются) "видимыми" процедурами и
функциями, то есть теми подпрограммами, чьи заголовки указаны в секции
интерфейса.
129
Оператор uses может содержаться в секции реализации (implementation)
и должен непосредственно следовать за ключевым словом implementation.
Обычные процедуры и функции, описанные в интерфейсной секции, то
есть те из них, которые не являются подставляемыми (inline), должны
повторно указываются в секции реализации. Заголовок procedure/function
должен быть или идентичным тому, который указан в секции интерфейса,
или иметь более краткую форму. В случае краткой формы наберите ключевое
слово (procedure или function), а за ним укажите имя подпрограммы
(идентификатор). Затем подпрограмма должна содержать все свои локальные
описания (метки, константы, типы, переменные и вложенные процедуры и
функции), за которыми должно находиться основное тело самой
подпрограммы. Пусть в интерфейсной части указаны следующие описания:
procedure ISwap(var V1,V2 : integer);
function IMax(V1,V2 : integer) : integer;
Тогда Секция реализации будет иметь следующий вид:
procedure ISwap;
var
Temp := integer;
begin
Temp := V1; V1 := V2; V2 := Temp
end; {конец процедуры Swap}
function IMax(V1,V2 : integer): integer;
begin
if V1 > V2
then IMax := V1
else IMax := V2
end; { конец функции Max }
130
Подпрограммы, локальные для секции реализации (то есть не описанные
в секции реализации), должны иметь полный (несокращенный) заголовок
procedure/function.
Секция инициализации
Обычно вся секция реализации модуля заключена между
зарезервированными словами implementation и end. Однако, если перед end
поместить зарезервированное слово begin, а между ними - операторы, то
получившийся составной оператор, очень похожий на основное тело
программы, становится секцией инициализации модуля (initialization).
Секция инициализации представляет собой место, где
инициализируются структуры данных (переменных), которые использует
программный модуль или которые он делает доступными программе,
использующей данный модуль. Вы можете использовать эту секцию для
открытия файлов, которые программа использует позднее.
При выполнении программы, использующей некоторый модуль, секция
инициализации этого модуля вызывается перед запуском основного тела
программы. Если программа использует более одного модуля, то секции
инициализации всех модулей вызываются (в порядке, указанном в операторе
uses в программе) перед тем, как выполнить основное тело программы.
Как используются модули?
Модули, которые использует ваша программа, уже оттранслированы и
хранятся, как машинный код, а не как исходный код на Паскале, поскольку
они не являются включаемыми файлами. Даже интерфейсная секция
хранится в специальном двоичном формате таблицы идентификаторов,
используемом в Turbo Pascal. Более того, определенные стандартные модули
хранятся в специальном файле (TURBO.TPL, TPW.TPL или TPP.TPL) и
автоматически загружаются в память вместе с Turbo Pascal.
131
В результате использование одного или нескольких модулей очень
незначительно увеличивает время компиляции вашей программы (обычно
менее, чем на секунду).
Фактически, если модуль скомпилирован, его использование сохраняет
вам время при перекомпиляции. Поскольку компилятор не перекомпилирует
модуль, пока он не изменяется, использование модулей в программе ускорит
процесс ее построения.
Как указывалось ранее, для использования специального модуля или
набора модулей необходимо в начале программы поместить оператор uses,
после которого указать список имен тех модулей, которые будут
использоваться. Имена их должны разделяться запятыми:
program MyProg;
uses thisUnit, thatUnit, theOtherUnit;
Когда компилятор встречает такой оператор uses, он прибавляет
информацию из секции интерфейса каждого модуля к таблице
идентификаторов и присоединяет машинный код, представленный в секции
реализации, к самой программе.
Модули присоединяются к таблице идентификаторов в указанном
порядке. Порядок модулей в операторе uses значения не имеет. Если модуль
thisUnit использует thatUnit или наоборот, вы можете описать их в любом
порядке, а компилятор определит, какой модуль нужно скомпоновать с
программой MyProg первым. Фактически, если модуль thisUnit использует
thatUnit, но MyProg не вызывает непосредственно ни одну из подпрограмм в
модуле thatUnit, вы можете "скрыть" подпрограммы модуля thatUnit, опустив
его в операторе uses:
unit thisUnit;
uses thatUnit;
.
132
.
.
program MyProg;
uses thisUnit, theOtherUnit;
.
.
.
В этом примере модуль thisUnit может вызывать любую подпрограмму
модуля thatUnit, а программа MyProg может вызывать любую из
подпрограмм модуля thisUnit или theOtherUnit. Однако, программа MyProg
не может вызывать подпрограммы модуля thatUnit, поскольку thatUnit не
указывается в операторе uses программы MyProg.
Если в программе не указан оператор uses, Turbo Pascal в любом случае
присоединит стандартный модуль System. Этот модуль обеспечит
выполнение некоторых стандартных подпрограмм Turbo Pascal, а также
нескольких подпрограмм, специфических для Turbo Pascal.
Ссылки на описания модуля
Как только вы включили модуль в свою программу, все константы, типы
данных, переменные, процедуры и функции, описанные в секции интерфейса
этого модуля, становятся доступными для вашей программы. Например,
допустим, имеется следующий модуль:
unit MyStuff;
interface
const
MyValue = 915;
type
MyStars = (Deneb,Antares,Betelgeuse);
var
MyWord : string[20];
133
procedure SetMyWord(Star : MyStars);
function TheAnswer : integer;
implementation
.
.
.
end.
Как можно видеть здесь в интерфейсной части модуля, та часть модуля,
которая находится в интерфейсной секции, является видимой для вашей
программы (и может ею использоваться). С учетом этого можно написать
следующую программу:
program TestStuff;
uses MyStuff;
var
I : integer;
AStar : MyStars;
begin
Writeln(myValue);
AStar := Deneb;
SetMyWord(AStar);
Writeln(MyWord);
I := TheAnswer;
Writeln(I)
end.
После включения в программу оператора uses MyStuff вы можете
ссылаться на все идентификаторы, описанные в интерфейсной секции модуля
МyStuff (МyWord, МyValue и так далее). Однако, рассмотрим следующую
ситуацию:
program TestStuff;
uses MyStuff;
134
const
MyValue = 22;
var
I : integer;
AStar : MyStars;
function TheAnswer : integer;
begin
TheAnswer := 1
end;
begin
Writeln(myValue);
AStar := Deneb;
SetMyWord(AStar);
Writeln(MyWord);
I := TheAnswer;
Writeln(I)
end.
В этой программе переопределяются некоторые из идентификаторов,
описанных в МyStuff. Будучи скомпилированной и выполненной, эта
программа будет использовать собственные определения для МyValue и
ТheAnswer, поскольку они были описаны позднее, чем определения в
МyStuff.
Вероятно, вам интересно знать, каким образом в такой ситуации можно
ссылаться на идентификаторы в МyStuff. Для этого необходимо перед
каждым идентификатором помещать имя МyStuff с точкой (.). Например,
рассмотрим еще одну версию этой программы:
program TestStuff;
uses MyStuff;
const
MyValue = 22;
135
var
I : integer;
AStar : MyStars;
function TheAnswer : integer;
begin
TheAnswer := 1
end;
begin
Writeln(MyStuff.MyValue);
AStar := Deneb;
SetMyWord(AStar);
Writeln(MyWord);
I := MyStuff.TheAnswer
Writeln(I)
end.
Эта третья программа даст такие же ответы, что и первая, даже в том
случае, если вы переопределите MyValue и TheAnswer. В действительности
вы имели полное право написать первую программу следующим образом:
program TestStuff;
uses MyStuff;
var
I : integer;
AStar : MyStuff.MyStars;
begin
Writeln(MyStuff.MyValue);
AStar := My.Stuff.Deneb;
MyStuff.SetMyWord(AStar);
Writeln(My.Stuff.MyWord);
I := MyStuff.TheAnswer;
Writeln(I)
136
end.
Отметим, что имя модуля может предшествовать любому
идентификатору: константе, типу данных, переменной или подпрограмме.
Оператор uses секции реализации
Turbo Pascal позволяет вам размещать в секции реализации оператор
uses. В случае его присутствия оператор uses должен следовать
непосредственно за ключевым словом implementation (аналогично тому, как в
интерфейсной секции оператор uses должен следовать непосредственно за
ключевым словом interface).
Размещение в секции реализации оператора uses позволяет "скрыть"
внутренние детали модуля, поскольку используемые в секции реализации
модули оказываются "невидимыми" для того, кто этот модуль использует.
Более важным, однако, является то, что это позволяет вам строить
взаимозависимые модули.
Поскольку программные модули в Borland Pascal не обязаны иметь
строго иерархическую структуру, то допускается использовать циклические
ссылки на модули. О циклических ссылках на модули рассказывается в Главе
10 "Справочного руководства по языку".
Стандартные модули
Модули библиотек исполняющей системы Turbo Pascal загружаются в
память вместе с Turbo Pascal; вы всегда можете их использовать. Обычно
библиотеки исполняющей системы (TURВО.TPL, TPW.TPL и TPP.TPL)
находятся в том же каталоге, что и компилятор (TURBO.EXE. BPW.EXE и
BP.EXE).
Создание ваших собственных модулей
Если вы хотите написать модуль, содержащий некоторые полезные
подпрограммы, и использовать эти подпрограммы в своих программах,
137
напишите модули и сохраните его под именем, заданным в заголовке модуля.
Turbo Pascal сохраняет файл с расширением .PAS, как и любой другой файл,
созданный в редакторе Turbo Pascal. В исходном файле может содержаться
только один модуль.
Компиляция модуля
Скомпилировать модуль вы можете двумя способами. Вы можете:
Скомпилируйте модуль с помощью команды Compile¦Compile.
Например, если ваш модуль называется MYUNIT.PAS, если целевой
платформой является Windows, он компилируется в MYUNIT.TWP.
Для компиляции программы, которая включает в себя оператор uses,
используйте команду Compile¦Make или Compile¦Build.
В зависимости от целевой платформы, создается файл .TPU, .TPW
или .TPP.
Доступность модуля для программы
Скопируйте свой новый файл .TPU, .TPW или .TPP в каталог модулей,
заданный в диалоговом окне Options¦Directories, или используйте параметр
командной строки /U при работе с компилятором режима командной строки.
Если вы поместите свой модуль в заданный каталог модулей, то сможете
ссылаться на этот модуль, даже если он не находится в текущем каталоге или
в библиотеках исполняющей системы.
Включите в любую программу, где вы хотите использовать свой новый
модуль, оператор uses. Например, если ваш новый модуль называется
INTLIB.TPW, то задайте в своей программе оператор следующего вида:
uses IntLib;
Чтобы найти модуль, имя которого указано в операторе uses, Turbo
Pascal проверяет его наличие в библиотеке исполняющей системы,
загруженной в память в время инициализации.
138
Примечание: О том, как поместить модуль в библиотеку исполняющей
системы, рассказывается ниже.
Если модуль в библиотеке исполняющей системы отсутствует, то
компилятор ищет его на диске, сначала в текущем каталоге, затем в
каталогах, заданных в качестве каталогов модулей (Options¦ Directories).
Компилятор предполагает, что имя файла совпадает с именем модуля, а
расширение имени файла - это .TPU, .TPW или .TPP. Исходный текст модуля
имеет расширение .PAS.
Пример. Теперь напишем небольшой модуль. Назовем его IntLib и
вставим в него две простые подпрограммы для целых чисел - процедуру и
функцию:
unit IntLib;
interface
procedure ISwap(var I,J : integer);
function IMax(I,J : integer) : integer;
implementation
procedure ISwap;
var
Temp : integer;
begin
Temp := I; I := J; J := Temp
end; { конец процедуры ISwap }
function IMax;
begin
if I > J
then IMax := I
else IMax := J
end; { конец функции IMax }
end. { конец модуля IntLib }
139
Наберите этот модуль, запишите его в файл INTLIВ.PAS, а затем
скомпилируйте, задав в качестве целевой платформы защищенный режим
DOS. В результате получим код модуля в файле INTLIВ.ТРP. Перешлем его в
каталог модулей (если такой имеется), или оставив в том же каталоге, где
находится следующая программа, которая использует модуль IntLib:
program IntTest;
uses IntLib;
var
A,B : integer;
begin
Write('Введите два целочисленных значения: ');
Readln(A,B);
ISwap(A,B);
Writeln('A = ',A,' B = ',B);
Writeln('Максимальное значение равно ',IMax(A,B));
end. { конец программы IntTest }
Модули и большие программы
До сих пор мы говорили о модулях как о библиотеках - наборах
полезных подпрограмм, которые могут использоваться несколькими
программами. Однако, у модуля есть еще одна функция - разбивать большую
программу на составные части.
Два аспекта Turbo Pascal способствуют использованию модулей в такой
функции:
высокая скорость компиляции и компоновки;
способность работать с несколькими файлами одновременно,
например, с программой и несколькими модулями.
Обычно большая программа разбивается на модули, которые
группируют процедуры по их функциям. Например, программа редактора
может быть разделена на части, выполняющие инициализацию, распечатку,
140
чтение и запись файлов, форматирование и так далее. Так же, как имеется
основная программа, определяющая глобальные константы, типы данных,
переменные, процедуры и функции, так же может иметь место и
"глобальный" модуль, который используется всеми другими модулями.
Набросок большой программы-редактора может иметь вид:
program Editor;
uses
WinCrt, String { стандартные модули из TPW.TPL }
EditGlobals, { модули, написанные пользователем }
EditInuit,
EditPrint,
EditRead, EditWrite,
EditFormat;
{ описание, процедуры и функции программы }
begin { основная программа }
end. { конец программы Editor }
Модули в данной программе могут содержаться в TPW.TPL, библиотеке
исполняющей системы Windows, или быть отдельными файлами .TPW. В
последнем случае Turbo Pascal выполняет за вас управление проектом. Это
означает, что при перекомпиляции программы Editor с помощью встроенного
в компилятор средства формирования Turbo Pascal сравнивает даты каждого
файла .PAS и .TPW и перекомпилирует любой модуль, исходный код
которого перекомпилирован.
Другая причина использования модулей в больших программах состоит
в ограничения кодового сегмента. Процессоры 8086 (и родственные им)
ограничивают размер сегмента кода 64 килобайтами. Это означает, что
основная программа и любой данный сегмент не может превышать 64К.
Turbo Pascal интерпретирует это, создавая для каждого модуля отдельный
141
сегмент кода. Без этого объем кода вашей программы не мог бы превышать
64К.
Общая характеристика базовых графических средств языка
Pascal.
Для реализации графических операций в прикладных программах
необходима базовая графическая система (БГС). Эта система может входить
в состав системы программирования(Borland Pascal, Borland C) или быть
частью операционного окружения (системы класса Windows).В частности, в
системе Borland Pascal базовые графические средства представлены набором
графических драйверов (файлы типа EGAVGA.BGI) и набором констант,
типов, переменных, процедур и функций, собранных в модуле Graph (файл
GRAPH.TPU).Аналогичные средства существуют и для языка С.
В целом, все процедуры и функции модуля Graph можно разбить на
следующие основные группы:
инициализация и завершение графического режима;
вывод основных графических примитивов;
установка атрибутов графических примитивов;
запрос значений атрибутов;
копирование.
Инициализация графического режима.
Инициализация должна выполняться перед непосредственным
использованием процедур модуля Graph для построения изображения.
Инициализация заключается в задании используемого графического драйвера
и графического режима для этого драйвера. Процедура инициализации
загружает указанный драйвер в оперативную память и настраивает систему
на работу в графическом режиме.
Для указания используемого драйвера можно воспользоваться
следующими константами:
Ega = 3142
Vga = 9
Detect = 0.
Константа Detect используется для указания необходимости
автоматического распознавания, имеющегося в наличии графического
адаптера.
После задания драйвера для указания соответствующего режима можно
воспользоваться следующими константами:
EgaHi = 1 - экран 640 на 350 точек с 16 цветами
VgaHi = 2 - экран 640 на 480 точек с 16 цветами
Инициализация графического режима производится вызовом процедуры
InitGraph (var GrDr:integer; PathDr: string), где параметры переменные GrDr и
GrMod определяют используемый драйвер и режим, а параметр PathDr
определяет путь к графическому драйверу в соответствии с правилами
MS/DOS. В простейшем случае параметр PathDr можно задать как пустую
строку, что соответствует ситуации, когда графический драйвер находится в
текущем каталоге. Теперь можно привести два варианта инициализации
графического режима - с явным и автоматическим заданием драйвера и
режима:
Вариант 1:
var GrDr,GrMod: integer;
begin
GrDr:=Vga; {используется драйвер VGA}
GrMod:=VgaHi;
InitGraph(GrDr,GrMod,’’);
Вариант 2:
var GrDr,GrMod: integer;
begin
GrDr:= Detect; {требуется автоопределение}
143
InitGraph(GrDr,GrMod,’’);
Для завершения работы в графическом режиме надо вызвать процедуру
CloseGraph, которая выгружает драйвер и восстанавливает исходное
состояние экрана.
Вывод основных графических примитивов.
Любое сложное изображение, в конечном счете, составляется из набора
нескольких простейших геометрических элементов, называемых
примитивами. Система Турбо Паскаль предлагает следующий набор
графических примитивов: точка, отрезок, окружность, дуга, дуга эллипса,
прямоугольник, круговой сектор, сектор эллипса, многоугольник, текст.
Каждый из этих примитивов на экране может выглядеть различным
образом, в соответствии со своими атрибутами. Основными атрибутами для
всех примитивов, кроме текста, являются: цвет, тип линии, толщина линии,
шаблон заполнения области (там, где эта операция имеет смысл).
Установка цвета вывода примитива производится процедурой
SetColor(цвет: integer),
где 0<=цвет<=15 и цветовая палитра совпадает с текстовым режимом.
Установка цвета фона производится процедурой
SetBkColor(цвет: integer).
Установка типа и толщины линии - процедурой
SetLineStyle(тип_линии, образец, толщина: integer).
Для задания типа линии предусмотрены следующие константы:
SolidLn=0 - непрерывная линия;
DottedLn=1 - точечная линия;
CenterLn=2 - штрихпунктирная линия;
DoshedLn=3 - пунктирная линия;
UserBitLn=4 - тип линии, определяемый пользователем;
в этом случае параметр ‘ образец ’ задает образец линии; во всех
остальных случаях следует брать ‘образец’=0.144
Замечание: атрибут ‘тип линии’ действует только для прямолинейных
примитивов. Для задания толщины линии можно использовать следующие 2
константы:
NormWidth=1 - нормальная толщина;
ThickWidth=3 - утолщенная линия.
Установка шаблона и цвета закраски замкнутых областей производится
с помощью процедуры
SetFillStyle(шаблон,цвет: integer).
Для задания шаблона можно использовать следующие константы:
EmptyFill=0 - закраска фоновым цветом;
SolidFill=1 - закраска заданным цветом;
LineFill=2 - закраска шаблоном вида - - - - - - -;
LtSlashFill=3 - закраска шаблоном вида / / / / / /;
SlashFill=4 - закраска шаблоном из толстых линий ////////;
BkSlashFill=5 - закраска шаблоном из толстых линий \\\\\\\;
LtBkSlashFill=6 - закраска шаблоном вида \ \ \ \ \ \;
HatchFill=7 - закраска редкой штриховкой;
XHatchFill=8 - закраска пересекающейся штриховкой;
InterLeaveFill=9 - закраска прерывистой линией;
WideDotFill=10 - закраска редкими точками;
CloseDotFill=11 - закраска частыми точками.
Пользователь имеет возможность создавать свои собственные шаблоны
заполнения с помощью процедуры SetFillPottern.
Для рисования т о ч к и на экране используется процедура
PutPixel(x,y,цвет: integer).
Точка - наименьший адресуемый элемент изображения, который
принято называть пикселом. Можно запросить цвет заданной точки экрана,
обратившись к функции
GetPixel(x,y: integer): integer.
Для вывода о т р е з к а прямой линии используются процедуры
145
Line(x1,y1,x2,y2: integer) - отрезок от (x1,y1) до
(x2,y2);
LineTo(x,y: integer)- отрезок от текущей точки до точки
(x,y);
LineRel(dx,dy: integer) - отрезок от текущей точки до
точки, определяемой смещением dx, dy.
Для задания текущей точки имеется специальный указатель текущей
позиции (УТП), который аналогичен курсору в текстовом режиме, но в
отличие от него невидим на экране. Изменение значения УТП отслеживается
графическими процедурами. Для перемещения УТП можно использовать
следующие процедуры:
MoveTo(x,y: integer) - перемещение УТП в точку (x,y);
MoveRel(dx,dy: integer) - смещение УТП на (dx,dy)
относительно его исходного положения.
Можно запросить значения УТП, обратившись к целочисленным
функциям GetX и GetY, которые аналогичны функциям WhereX и WhereY
для текстового режима.
Вывод прямоугольника с горизонтальными и вертикальными
сторонами производится процедурой
Rectangle(x1,y1,x2,y2: integer),
где (x1, y1) - левый верхний угол, а (x2, y2) - правый нижний угол
прямоугольника.
Вывод окружности с центром (x1, y1) и радиусом R производится
процедурой
Circle(x,y,R: integer).
Вывод дуги окружности производится процедурой
Arc(x,y,нач_угол,кон_угол,R: Integer),
где (x, y) - центр образующей окружности радиуса R, а нач_угол и
кон_угол задают начальный и конечный угол дуги (в градусах); дуга
146
рисуется против часовой стрелки в соответствии с общепринятыми
соглашениями.
Вывод дуги эллипса производится процедурой
Ellipse(x,y,нач_угол,кон_угол,RX,RY: integer),
где (x, y) - координаты центра, RX и RY - полуоси, а нач_угол и
кон_угол задают начальный и конечный углы дуги, аналогично дуге
окружности; если нач_угол=0, а кон_угол=359, то будет нарисован полный
эллипс.
Вывод контура многоугольника производится процедурой
DrawPoly(n: integer; var коорд_верш),
где n - число вершин, а параметр-переменная коорд_верш определяет
координаты всех вершин многоугольника. Тип этого параметра не задан,
поэтому он называется не типизированным, что является н е с т а н д а р т н о
й возможностью системы Турбо Паскаль. Соответствующий фактический
параметр может быть л ю б о г о типа. Например, удобно этот фактический
параметр описать как массив записей вида:
var коор : array[1..n] of record
x,y: integer
end;
При вычерчивании замкнутого многоугольника с m вершинами
начальная и конечная вершины рассматриваются как две отдельные
вершины, координаты которых одинаковы, поэтому n=m+1.
Для построения закрашенного многоугольника можно использовать
процедуру
FillPoly(n: integer; var коорд_верш),
где формальные параметры полностью аналогичны предыдущей
процедуре.
Отличие данной процедуры от предыдущей состоит в том, что сначала
рисуется контур многоугольника с использованием цвета, типа и толщины
линий, заданных процедурами SetColor и SetLineStyle, а затем многоугольник
147
закрашивается в соответствии с шаблоном и цветом, заданными процедурой
SetFillStyle.
Аналогичные действия, но по отношению к произвольной замкнутой
области, выполняется процедура
FloodFill(x,y,цвет_гран: integer).
Она заполняет замкнутую область с границей, определяемой заданным
цветом (параметр цвет_гран), в соответствии с заданным шаблоном закраски.
Если точка (x, y) находится внутри области, то закрашивается внутренность
области, иначе - весь экран за исключением заданной области.
Для вывода закрашенного кругового сектора можно использовать
процедуру
PieSlice(x,y,нач_угол,кон_угол,R: integer),
где формальные параметры полностью аналогичны параметрам
процедуры Arc.
Контур сектора вычерчивается в соответствии с заданным цветом, типом
и толщиной линии, а потом закрашивается в соответствии с заданным
шаблоном и цветом закраски.
Аналогичная процедура для закраски эллиптического сектора имеет
вид
Sector(x,y,нач_угол,кон_угол,RX,RY: integer).
Процедура
Bar(x1,x2,x2,y2: integer)
выводит прямоугольник , закрашенный в соответствии с установками
процедуры SetFillStyle.
Система программирования Турбо Паскаль предусматривает ряд
возможностей для вывода текстовой информации в графическом режиме.
Прежде всего, имеется возможность выбора одного из типов шрифта. По
умолчанию всегда выбирается точечный (растровый) шрифт, который для
вывода любого символа использует точечную матрицу размерности 8 на 8.
148
При желании вместо точечного шрифта можно использовать один из
штриховых (векторных) шрифтов, в которых любой символ описывается как
набор отрезков (векторов). Штриховые шрифты хранятся в специальных
файлах на диске и при необходимости загружаются в оперативную память.
Для задания типа шрифта можно использовать следующие константы:
DefaultFont=0 - точечный шрифт;
TriplexFont=1 - основной штриховой шрифт (файл triplex.chr);
SmallFont=2 - уменьшенный штриховой шрифт(файл small.chr);
SanSeriFont=3 - дополнительный штриховой шрифт(sanserif.chr);
GothicFont=4 - готический шрифт (файл gothic.chr).
Для задания горизонтального или вертикального направления вывода
текста можно использовать следующие константы:
HorizDir=0 - горизонтальное направление (слева - направо):
VertDir=1 - вертикальное направление(снизу-вверх).
Кроме того, имеется возможность управления размером символов. По
умолчанию для точечного шрифта выбирается наименьший возможный
размер, определяемый целочисленной константой NormSize=1.
Размер символов точечного шрифта можно увеличить в 2,3,...,10 раз.
Задание типа шрифта, направления вывода и размеров символов
производится процедурой
SetTextStyle(шрифт,направл,размер: integer).
Изменение высоты и ширины символов для штриховых шрифтов
производится процедурой
SetUserCharSize.
Выводимый текст можно выравнивать в горизонтальном или
вертикальном направлениях. Горизонтальное выравнивание определяется
следующими константами :
LeftText=0 - текст выводится вправо от заданной точки, т.е.
выравнивается по л е в о й границе текста;
149
CenterText=1 - центрирование текста, т.е. заданная точка соответствует
середине выводимого текста;
RightText=2 - текст выводится влево от заданной точки, т.е.
выравнивается по правой границе текста.
Аналогично, для вертикального выравнивания определены константы:
BottomText=0 - текст выводится н и ж е заданной точки;
CenterText=1 - центрирование текста;
TopText=2 - текст выводится в ы ш е заданной точки.
Нулевые значения констант выбираются по умолчанию. Для задания
выравнивания текста используется процедура
SetTextJustify(горизонт,вертик: integer).
Вывод текста производится двумя процедурами:
OutTextXY(x,y: integer; строка: string);
OutText(строка: string).
Первая выводит текст относительно заданной точки (x,y), а вторая -
относительно имеющего значение УТП (обе - с учетом выравнивания
текста).При использовании точечного шрифта (он всегда выбирается по
умолчанию) надо знать, что если выводимая строка не помещается
полностью на экране, то она просто не выводится!
Однако, при использовании штриховых шрифтов не выводится лишь та
часть строки, которая выходит за пределы экрана.
Задание области вывода
Область вывода (или область просмотра, поле вывода, графическое
окно)- это часть графического экрана, куда направляется весь графический
вывод без изменения остальной части экрана. В этом смысли понятие
области вывода аналогично понятию окна в текстовом режиме. Область
вывода задается процедурой
SetViewPort(x1,y1,x2,y2: integer; режим_отсечения:
boolean),
150
где (x1, y1) - координаты левого верхнего угла поля вывода, а (x2, y2) -
координаты правого нижнего угла. Булевский параметр ‘режим_отсечения’
определяет, включен или нет для данного поля вывода режим отсечения по
границе поля: если включен (константа ClipOn=true), то все выводимые
примитивы отсекаются по границе поля, т.е. не выходят за его пределы, в
противном случае (константа ClipOff = false) изображение может выходить за
границы области. Нормальное состояние области вывода соответствует
режиму включения отсечения.
На экране одновременно может существовать несколько областей
вывода. Для возврата в исходное состояние, соответствующее ПОЛНОМУ
экрану, можно сделать следующий вызов:
SetViewPort(0,0,GetMaxX,GetMaxY,True),
где GetMaxX и GetMaxY - функции модуля Graph, возвращающие
максимально возможные для данного графического режима значения
координат X и Y. Эти функции чрезвычайно полезны при написании
программ, работающих с различными адаптерами.
Для очистки области вывода можно использовать процедуру:
ClearViewPort.
151
Задания на лабораторные работы
Лабораторная работа №1.
Основные приемы работы в среде Turbo Pascal
Задание на лабораторную работу
Внимательно изучите следующий раздел. Следуя приведенным в них
указаниям, наберите, сохраните, скомпилируйте и посмотрите в отладчике
пример программы на языке Паскаль. Попробуйте повторить эти действия
самостоятельно с другим примером. В результате выполнения лабораторной
работы Вы должны научиться создавать, компилировать и отлаживать
программы на языке Паскаль в среде Turbo Pascal.
Работа в среде Turbo Pascal
Запуск среды Turbo Pascal
Для запуска среды достаточно, находясь в каталоге, содержащем
систему Турбо Паскаля, в ответ на подсказку DOS набрать TURBO и нажать
клавишу Enter. При этом запустится программа TURBO.EXE, которая и
создаёт среду. Для выхода из среды наберите Alt-Х.
Первое знакомство. Основное меню
При входе в интегрированную среду Турбо Паскаля 7.0 на экране
появляется окно, в верхней части которого высвечивается полоса с
надписями - заголовками секций меню, перечисляющими услуги,
предоставляемые средой. На рисунке приведен вид среды Turbo Pascal при
запуске ее из операционной системы Windows 95.
152
Вход в меню осуществляется одновременным нажатием клавиш Alt и
клавиши с буквой, выделенной в заголовке нужной секции меню. Например,
для входа в секцию File необходимо нажать Alt-F. Другим способом входа в
меню является нажатие функциональной клавиши F10 с последующим
перемещением в нужную позицию меню с помощью курсора (вправо или
влево). Выбранная позиция меню подсвечивается. При нажатии клавиши
Enter подсвеченная секция меню раскрывается в виде окна, содержащего
дальнейшую детализацию меню.
Например, если в меню оказывается подсвеченной секция Edit, то при
нажатии клавиши Enter раскрывается следующее окно:
153
Комбинация клавиш, указанные справа от названия действия,
определяют способ прямого входа в меню для выполнения указанного
действия.
Например, для выполнения действия «Сору» можно, находясь в меню, с
помощью курсора «наехать» на нужную позицию, подсветив ее, и нажать
клавишу Enter. Другим способом является использование клавиш. Не входя в
меню, можно выполнить операцию «Сору», нажав клавиши Ctrl-Ins.
Для выхода из меню достаточно нажать клавишу Esc.
Работа в редакционном окне, создание программы
Нажмите F10, чтобы войти в полосу меню, а затем «наедьте» Курсором
на позицию File, нажмите Enter (либо наберите Alt-F). Раскрывается секция
меню File:
154
Выберите строку New, нажмите клавишу Enter. На экране раскрывается
пустое окно, озаглавленное NONAME00.PAS. Это имя, данное средой по
умолчанию Вашей будущей программе. Если Вы повторите операцию,
раскроется еще одно окно, но уже с именем NONAME01.PAS. Таким образом
можно раскрыть достаточное число редакционных окон. Для Переключения
окон достаточно, удерживая нажатой клавишу Alt, нажать клавишу с цифрой
- окна пронумерованы. Например, для возврата в первое окно нужно набрать
Alt-1.
Итак, перед Вами пустое окно, в левом верхнем углу которого мигает
курсор. При наборе текста с помощью клавиатуры курсор будет
перемещаться. Приступите к вводу текста программы, нажимая Enter в конце
каждой строки:
program Summa;
var
A,B,Sum : integer;
begin
Wгitе(’Введите два числа: ‘);
Readln(A,B);
Sum :=А+В;
155
Wгitеln(’Сумма равна’,Sum);
Wгitе(’Нажмите Enter’);
Readln;
end.
Примечание. Не забывайте про точку с запятой, а за последним end
поставьте точку. Для удаления ошибочно набранного текста используйте
Backspace, а для передвижения внутри окна редактора используйте клавиши
со стрелками.
Сохранение программы
Для сохранения программы на диске выберите команду Save as из меню
Filе. Турбо Паскаль открывает диалоговый окно Save File As для
переименования файла и сохранения его в другом каталоге (директории) или
на другом диске.
Диалоговое окно содержит поле ввода Save file as, список файлов Files,
информационную панель, стандартные кнопки ОК, CANCEL, HELP и список
предыстории. Переключение между элементами окна осуществляется
клавишей Tab.
156
В поле ввода Save file as записывается имя, под которым Вы собираетесь
запомнить файл (либо файловая маска для поля Files).
В нашем случае необходимо набрать SUMMA.PAS н нажать Enter.
Рассмотрим детальнее остальные элементы диалогового бокса.
Поле Files содержит имена файлов в Текущем каталоге (директорий), в
соответствии с маской, установленной в поле Save file as.
Например, если в поле Save file as записано *.РАS, то в поле Files
появятся имена всех файлов каталога, содержащих расширение РАS.
Список предыстории добавляет к полю ввода все имена, которые
появлялись в нем во время последних вызовов диалогового окна. В список
предыстории можно войти в том случае, если справа от поля ввода Save file
as видите стрелку «вниз». Для входа в список следует нажать клавишу
«стрелка вниз» на клавиатуре. Этот список используется для повторного
ввода текста, который Вы уже вводили.
Выбор нужного элемента осуществляется курсором, при этом под-
свечивается выбранная позиция. Затем следует нажать клавишу Enter.
Выбранное имя файла попадает в поле ввода Save file as.
Если выбор не сделан, для выхода из списка предыстории нажмите
клавишу Esc. Информационная панель отображает путь к выбранному
файлу, его имя, дату, время создания и размер.
Кнопка ОК служит для подтверждения выполненных действий. Кнопка
CANCEL отменяет все действия и выводит из диалогового окна. Кнопка
HELP выводит окно с подсказкой.
Компиляция программы
Для компиляции программы выберите опцию Соmplile в основном
меню, для чего нажмите F10, С
Секция содержит подменю:
157
Команды меню Compile используются при компиляции и реализации
операций Make и Build.
Команда Compile компилирует файл в активном редакционном окне.
При компиляции или выполнения команды Make на экране высвечивается
окно состояния с результатами. После завершения компиляции или команды
Make для ликвидации окна статуса компиляции достаточно нажать любую
клавишу.
При обнаружении ошибки в верхней части редакционного окна
появляется сообщение.
Команда Make включает встроенный Project Manager для создания файла
.EXE.
Файлы рекомпилируются в соответствии со следующими правилами:
если Compile/Primary File содержит в списке первичный файл, он
компилируется, в противном случае компилируется последний файл,
загруженный в редактор. Турбо Паскаль проверяет все файлы, от
которых зависит компилируемый файл.
Если исходный файл для данного модуля (Unit) модифицировал-
ся после того, как объектный код (.TPU) файла был создан, модуль
перекомпилируется.
158
Если и интерфейс для данного модуля изменен, все другие
модули, от него зависящие, перекомпилируются.
Если модуль использует .OBJ file и он новее, чем .TPU file
данного модуля, модуль перекомпилируется.
Если модуль включает Include file и он новее, чем .TPU file
данного модуля, модуль перекомпилируется.
Команда Build перестраивает все файлы независимо от их НОВИЗНЫ.
Команда идентична команде Make, но не является условной (Make
перестраивает только файлы, изменённые после предыдущей сомпиляции).
Команда Dеstinаtiоn Memory (Disk) определяет место запоминания
выполняемого кода в памяти или на диске (файл с расширением .EXE).
Устанавливая Destination Disk, Вы увеличиваете память, доступную
среде для компиляции и отладки программы.
При установке Destination Memory при выходе из среды код исчезает.
Замечание. Даже если DestinatIon установлена в память, любые модули,
рекомпилированные с помощью Make или Build, хранят свои обновленные
файлы с расширением .TPU на диске.
При установке Destination на диск Турбо Паскаль создает файл .ЕХЕ,
имя которого выбирается из двух имен следующим образом: имя первичного
файла или, если не определено первичное имя, то назначается имя файла в
активном редакционном окне.
Турбо Паскаль запоминает результирующий файл .EXE в том же
каталоге, что и исходный файл или в каталоге, заданном в установке ЕХЕ &
TPU Directory меню Options/Directories.
Переустановка команды Destination происходит нажатием клавиши Enter
(установка Destination Memory сменится на Destination Disk и наоборот).
Итак, выполните команду Compile.
После начала компиляции в центре экрана появляется окно с
информацией о процессе компиляции. Если во время компиляции не
159
обнаружено ошибок, в этом окне появится сообщение «Compilation
successful: press any kеу» (компиляция успешна: нажмите любую клавишу).
Окно остается на экране до тех пор, пока Вы не нажмете любую
клавишу.
Как уже было сказано, при обнаружении ошибки, Турбо Паскаль
останавливает процесс трансляции, устанавливает курсор на ошибку в
редакционном окне и выдает сообщение об ошибке. Нажатие любой клавиши
убирает сообщение, а нажатие Ctrl-Q W обеспечивает его показ до тех пор,
пока Вы не измените файл или не перекомпилируете его.
Сделав исправления, сохраните обновленный файл и заново
скомпилируйте его.
Однако, для запоминания файла на этот раз нет необходимости
вызывать диалоговое окно Save as, достаточно нажать клавишу F2. Старый
вариант программы будет удален, а новый вариант будет сохранен в том же
файле. Если необходимо сохранить старый вариант, то надо воспользоваться
командой меню File/Save as и ввести новое имя файла или выбрать другой
каталог.
Выполнение программы
Для пуска программы выберите секцию Run в основном меню.
Секция содержит подменю:
160
Команды меню Run позволяют запускать Программу на выполнение,
начинать и заканчивать сеанс отладки.
Команда Run запускает Вашу программу на выполнение. При этом
используются любые параметры, передаваемые ей командой Run/Parameters.
Если исходный код модифицировался после последней компиляции,
компилятор автоматически сделает Make и свяжет программу.
Команда Program reset останавливает текущую отладку, освобождает
память, занятую программой и закрывает все файлы, используемые
программой.
Команда Go to cursor пускает программу от места останова (под-
свеченная строка исходного текста в редакционном окне) до строки, возле
которой установлен курсор. Если курсор находится на строке, не содержащей
выполняемых операторов, Турбо Паскаль высвечивает предупреждение. Эта
команда может инициировать отладку. Команда не устанавливает
постоянной точки прерывания, но позволяет программе останавливаться на
уже установленных постоянных точках, встречающихся до строки,
помеченной курсором. Если это произойдет, необходимо снова
воспользоваться командой Go to cursor. Удобно использовать эту команду
161
для предварительной установки run bar (подсвеченной строки, на которой
остановлена отладка).
Команда Trace into пускает Вашу программу построчно (оператор за
оператором). При достижении процедуры команда начинает пооператорное
выполнение процедуры (в отличие от команды Step Over, выполняющей
процедуру за один шаг).
Команда Step Over выполняет следующий оператор в текущей про-
цедуре, не трассируя вызовы процедур низшего уровня, даже если они
доступны отладчику, т.е. ее следует использовать для пооператорного
выполнения текущей процедуры без ухода в другие процедуры.
Итак, Вы в меню Run. Выберите команду Run. Вы попадете в окно
пользователя, появится сообщение:
Введите два числа:
Наберите два любых целых числа с пробелом между ними и нажмите
Enter. Появится следующее сообщение:
Сумма равна
а за ним - сумма двух чисел. В следующей строке появится сообщение:
Нажмите клавишу Enter
Программа будет ожидать нажатия клавиши Епtег. Для наблюдения за
выводом из своей программы, выберите команду User Screen в меню Window
(или нажмите Аlt-F5).
Отладка программы
Продемонстрируем использование интегрированного отладчика,
встроенного в среду Турбо Паскаля 7.0.
Интегрированный отладчик позволяет перемещаться по строкам
программы, одновременно наблюдая за изменением значений переменных.
Для начала сеанса отладки, выберите команду Trace Into меню Run (или
нажмите F7). Run bar (подсвеченная полоса) устанавливается на первой
строке (в данном случае begin).
162
Первое нажатие клавиши F7 инициализирует сеанс отладки. Теперь
нажмите F7, чтобы начать выполнение программы. Следующая выполнимая
строка - оператор Write. Нажмите F7 снова. Появится экран пользователя.
Это произойдет потому, что утверждение Readln ожидает ввода двух чисел.
Наберите два целых числа, разделенные пробелом. Нажмите Enter. Вы
вернетесь назад в редакционное окно, с Run bar на операторе присваивания.
Нажмите F7 и выполните оператор присваивания. Теперь полоса запуска
находится на операторе Writeln. Нажмите F7 дважды. Теперь Вы должны
выполнить Readln. НажмитеF7, посмотрите вывод своей программы и затем
нажмите Enter. Нажмите F7 и Вы выйдете из программы.
Для того чтобы наблюдать за значениями объявленных переменных,
войдите в секцию меню Debug.
Команда Add watch помещает наблюдаемое выражение в окно Watch.
При выборе Add Watch отладчик открывает диалоговое окно Add Watch. Во
входном боксе Watch expression высвечивается выражение по умолчанию (то,
на которое указывает курсор в редакционном окне).
Для поиска и выбора другого выражения (из числа уже
использовавшихся) можно открыть список предыстории. Если Вы вводите
163
допустимое выражение, нажав клавишу Enter или задействован Ok, отладчик
добавляет выражение и его текущее значение в окно Watch.
Если окно Watch является активным, для введения нового выражения
для наблюдения нужно нажать клавишу Ins.
Наберите А в окне ввода Watch Expression и нажмите Enter. А появится в
окне Watch вместе со своим текущим значением. Теперь используйте
команду Add Watch для добавления В и Sum в окно Watch.
Выберите Trace Into в секции Run (или нажмите F7) для того, чтобы
сделать шаг в своей программе. В окне Watch появятся А = 0, В =0, Sum = 0.
Когда после ввода чисел Вы нажмете Enter н вернетесь в редакционное окно,
значения А и В изменятся на введенные Вами. После выполнения оператора
вычисления суммы изменится и значение Sum.
Так, секция Options позволяет провести оптимальную настройку самой
среды, секция Edit содержит команды передачи фрагментов из одного окна в
другое либо внутри одного окна. Секция Search обеспечивает поиск и замену
фрагментов. Секция Window позволяет изменять размер окон, а также способ
их выдачи на экран. Наконец, секция Help поможет разобраться во всех
тонкостях использования среды.
Задание для самостоятельной работы
Введите текст программы в редакторе исходного кода среды Turbo
Pascal. Скомпилируйте программу и запустите ее на выполнение.
Определите, что делает программа.
Пример 1
Program P1;
var S:string;
begin
S:=’Просьба не беспокоить!’;
Writeln(S);
Readln;
164
End.
Пример 2
Program P2;
var i:integer;
begin
for i:=1 to 10 do
Writeln(i);
Readln;
End.
Пример 3
Program P3;
var i:integer;
begin
Write(’Введите цифру 1 или 2:’);
Readln(i);
if i=1 then Writeln(’Один’)
else Writeln(’Два’);
Readln;
End.
165
Лабораторная работа №2.
Типы данных, выражения, ввод/вывод
Задание на лабораторную работу
Напишите программу для расчета по двум формулам. Предварительно
подготовьте тестовые примеры для второй формулы с помощью
калькулятора (результаты вычисления по обеим формулам должны
совпадать). Отсутствующие в языке функции выразите через имеющиеся.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить раздел
«Основные типы данных. Ввод/вывод».
Варианты индивидуальных заданий
1. 2.
3.4.
5. 6.
7.8.
9.10.
166
11. 12.
13.
14.
15. 16.
17. 18.
19. 20.
167
Лабораторная работа №3.
Циклы
Задание на лабораторную работу
Изучите материал приведённый ниже. Продумайте алгоритм и напишите
программу на языке Pascal для решения указанной преподавателем задачи
(см. Варианты индивидуальных заданий). Программа должна
предусматривать ввод исходных данных с клавиатуры и вывод результата на
экран.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить раздел
«Операторы языка Pascal».
Варианты индивидуальных заданий
1. Ввести с клавиатуры последовательность из n (вводится с клавиатуры)
целых чисел. Вычислить в процессе ввода сумму и произведение введённых
чисел.
2. Вычислить квадратный корень из произвольного вещественного
числа, введенного с клавиатуры, используя метод Ньютона:
Здесь A – введенное число, Y0=A.
Если A – отрицательное число, то необходимо вывести сообщение об
ошибке.
Результат должен иметь относительную точность 1·10-6. Как только
получено значение Y0 оно используется для получения следующего
приближения Y0, по которому в свою очередь вычисляется Y0, и так до тех
пор, пока не будет достигнута требуемая точность, то есть, пока не станет
168
3. Вычисление числа e
Определить число e – основание натуральных логарифмов с помощью
ряда:
для всех значений n от 1 до 20. Для каждого случая напечатать n и
соответствующее приближение e.
4. Последовательности без повторений
Вывести на экран все числа из 3 цифр, причем внутри одного числа не
должно быть двух одинаковых цифр. Например, к таким числам относятся:
012, 271, 490. Число 6 такой последовательностью не является, так как
начинается с двух нулей.
5. Вывести на экран все трехзначные числа, состоящие из нечетных
цифр и их количество.
6. Вывести на экран все числа от 100 до 10000, которые делятся одновременно
на 7 и на 13 и их количество.
7. Вывести на экран все числа от 100 до 10000, сумма цифр которых равна
заданному числу.
8. Вывести на экран числа 1, 1, 2, 3, 5, 8, … (первые два числа – единицы,
каждое последующее равно сумме двух предыдущих). Не использовать массив.
9. Ввести с клавиатуры последовательность из n (вводится с клавиатуры)
целых чисел. Определить в процессе ввода количество положительных и
отрицательных чисел.
10. Ввести с клавиатуры последовательность из n (вводится с
клавиатуры) целых чисел. Определить в процессе ввода количество четных и
нечетных чисел.
169
11. Ввести с клавиатуры последовательность из n (вводится с
клавиатуры) целых чисел. Определить в процессе ввода среднее
арифметическое четных чисел последовательности.
12. Ввести с клавиатуры последовательность из n (вводится с
клавиатуры) целых чисел. Определить в процессе ввода, составляют ли числа
возрастающую последовательность.
13*. Совершенные числа
Совершенными называются числа, равные сумме всех своих делителей,
включая 1. Например, 6 – совершенное число, поскольку 6=1+2+3. Требуется
найти и вывести на экран все совершенные числа вместе с их делителями,
лежащие в диапазоне от 4 до 10000.
170
Лабораторная работа №4.
Одномерные массивы
Задание на лабораторную работу
Одной из классических задач является поиск простых чисел. Ниже
обсуждаются два алгоритма её решения. Напишите программу для алгоритма
«Решето Эратосфена». Самостоятельно продумайте алгоритм решения и
напишите программу для одного из вариантов индивидуальных заданий.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить раздел
«Структурированные типы. Массивы».
Варианты индивидуальных заданий
1. Ввести массив целых чисел из n элементов (задано константой).
Переместить все нулевые элементы в начало массива. Вывести массив на
экран.
2. Ввести с клавиатуры целочисленный массив из n элементов (задано
константой). Подсчитать количество чисел, значение которых (x) лежит в
заданных пределах L<x<M, где L и M вводятся с клавиатуры. Вывести на
экран полученное значение.
3. Ввести с клавиатуры целочисленный массив из n элементов (задано
константой). Подсчитать количество чисел, значение которых меньше
значений обоих соседних элементов. Вывести на экран полученное значение.
4. Ввести с клавиатуры вещественный массив из n элементов (задано
константой). Вычислить разность суммы положительных и абсолютного
значения суммы отрицательных элементов. Вывести на экран полученное
значение.
5. Ввести с клавиатуры вещественный массив из n элементов (задано
константой). Вычислить разность суммы элементов с четными индексами и
171
суммы элементов с нечетными индексами. Вывести на экран полученное
значение.
6. Ввести массив целых чисел из n элементов (задано константой).
Переместить все отрицательные элементы в конец массива. Вывести массив
на экран.
7. Ввести массив целых чисел из n элементов (задано константой).
Проверить, что элементы с нечетным индексом расположены в порядке
неубывания. Вывести на экран результат.
8. Ввести массив вещественных чисел из n элементов (задано константой).
Вычислить сумму элементов, расположенных до максимального элемента.
Вывести на экран полученное значение.
9. Ввести массив вещественных чисел из n элементов (задано константой).
Вычислить разность суммы первой половины элементов и произведения
второй половины элементов. Вывести на экран полученное значение.
10.Ввести массив целых чисел из n элементов (задано константой).
Проверить, что элементы с четным индексом расположены в порядке
убывания. Вывести на экран результат.
11.Ввести массив вещественных чисел из n элементов (задано константой).
Вычислить произведение элементов, расположенных после минимального
элемента. Вывести на экран полученное значение.
12.Ввести массив целых чисел из n элементов (задано константой).
Последовательно поменять местами элементы на четных и нечетных местах
(1-й и 2-й, 3-й и 4-й и тд.. Вывести на экран полученное значение.
13.*Ввести с клавиатуры целочисленный массив из n элементов (задано
константой). Найти второй по величине элемент. Вывести на экран
найденный элемент, его индекс.
14.*Даны действительные числа а0, а1, ..., а5. Получить (в виде массива
коэффициентов) многочлен шестой степени (х—а0)(х—а1) ... (х—а5).
172
Лабораторная работа №5.
Матрицы
Задание на лабораторную работу
Изучите материал приведённый ниже. Самостоятельно продумайте
алгоритм решения и напишите программу для одного из вариантов
индивидуальных заданий.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить раздел
«Структурированные типы. Массивы».
Варианты индивидуальных заданий
1. Ввести с клавиатуры вещественную матрицу размерности n´m (заданы
константами). Найти максимальный и минимальный элементы и их
индексы. Вывести на экран найденные элементы, их индексы.
2. Ввести с клавиатуры вещественную матрицу размерности n´m (заданы
константами). Заменить все отрицательные числа их абсолютным
значением. Вывести на экран полученную матрицу.
3. Ввести с клавиатуры вещественную матрицу размерности n´m (заданы
константами). Вычислить среднее арифметическое для каждого столбца.
Вывести исходную матрицу и полученные значения на экран.
4. Ввести с клавиатуры вещественную матрицу размерности n´m (заданы
константами). Заменить все положительные числа их квадратом. Вывести
на экран полученную матрицу.
5. Ввести с клавиатуры целочисленную матрицу размерности n´m (заданы
константами). Подсчитать количество положительных и отрицательных
чисел. Вывести на экран полученные значения.
173
6. Ввести с клавиатуры целочисленную матрицу размерности n´m (заданы
константами). Найти максимальный элемент среди элементов с нечетной
суммой индексов. Вывести на экран найденный элемент, его индексы.
7. Ввести с клавиатуры целочисленную матрицу размерности n´m (заданы
константами). Подсчитать количество четных и нечетных чисел. Вывести
на экран полученные значения.
8. Ввести с клавиатуры вещественную матрицу размерности n´m (заданы
константами). Вычислить среднее арифметическое для каждой строки.
Вывести исходную матрицу и полученные значения на экран.
9. Ввести с клавиатуры вещественную матрицу размерности n´n (задана
константой). Подсчитать количество нулевых элементов, расположенных
на главной и побочной диагоналях.
10.Ввести с клавиатуры целочисленную матрицу размерности n´m (заданы
константами). Найти элемент кратный заданному числу. Вывести на экран
найденный элемент, его индексы, степень кратности.
11.Поиск наибольшего числа в трехмерном массиве
Пусть массив Т имеет размерность 3х5х7. Найти наибольшее
содержащееся в нем число и вывести его и его индексы на экран.
12.*Поиск двух одинаковых чисел в двумерном массиве.
Массив А размерностью 30х7 содержит два (и только два) одинаковых
числа. Необходимо найти их и вывести на экран их индексы. Избегайте
лишних сравнений (особенно сравнений элемента с самим собой).
13.Суммирование элементов двумерного массива, сумма индексов которых
равна заданной константе
Массив X размерностью 10х30 содержит вещественные числа. Требуется
ввести целое число k и вычислить сумму элементов Xi,j, для которых i+j=k.
Естественно, следует убедиться, что значение позволяет найти решение,
иначе нужно вывести сообщение об ошибке.
Задачу можно решить не перебирая все элементы массива!
14.*Решение нижней треугольной системы
174
Требуется написать программу, которая решает треугольную систему
порядка n:
В программе необходимо ввести следующие данные:
A – квадратная матрица коэффициентов,
B – вектор свободных членов.
Порядок системы n задается в виде константы. Вектор X будет хранить
решение. Кроме того, понадобится индикатор ошибки k.
Если все кончилось благополучно, то индикатор будет иметь значение
false (ошибки не было), иначе – true (произошла ошибка). Это означает, что в
какой-то момент один из делителей оказался равным нулю. Алгоритм поиска
решения очень прост. Достаточно найти x1 из первого уравнения, подставить
его во второе, чтобы найти x2 и продолжать таким образом до тех пор, пока
не будет вычислено значение xn.
175
Лабораторная работа №6.
Процедуры и функции
Задание на лабораторную работу
Размерности массивов следует задать именованными константами. Все
необходимые данные должны передаваться подпрограммам в качестве
параметров; все величины, используемые только внутри подпрограмм,
должны быть описаны как локальные. Использование глобальных
переменных в подпрограммах не допускается. Вывод результатов работы
подпрограмм должен выполняться в главной программе.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить разделы
«Структурированные типы. Массивы», «Процедуры и функции».
Варианты индивидуальных заданий
1.
Дана целочисленная прямоугольная матрица. Определить:
1. Количество строк, не содержащих ни одного нулевого элемента
(оформить в виде функции).
2. Максимальное из чисел, встречающихся в заданной матрице более одного
раза (оформить в виде процедуры).
2.
Дана целочисленная прямоугольная матрица.
1. Определить количество столбцов, не содержащих ни одного нулевого
элемента (оформить в виде функции).
2. Характеристикой строки целочисленной матрицы назовем сумму ее
положительных четных элементов. Переставляя строки заданной
176
матрицы, расположить их в соответствии с ростом характеристик
(оформить в виде процедуры).
3.
Дана целочисленная прямоугольная матрица. Определить:
1. Количество столбцов, содержащих хотя бы один нулевой элемент
(оформить в виде функции),
2. Номер строки, в которой находится самая длинная серия одинаковых
элементов (оформить в виде процедуры).
4.
Дана целочисленная квадратная матрица. Определить:
1. Произведение элементов в тех строках, которые не содержат
отрицательных элементов (оформить в виде функции).
2. Максимум среди сумм элементов диагоналей, параллельных главной
диагонали матрицы (оформить в виде процедуры).
5.
Дана целочисленная квадратная матрица. Определить:
1. Сумму элементов в тех столбцах, которые не содержат отрицательных
элементов (оформить в виде функции).
2. Минимум среди сумм модулей элементов диагоналей, параллельных
побочной диагонали матрицы (оформить в виде процедуры).
6.
Дана целочисленная прямоугольная матрица. Определить:
1. Сумму элементов в тех строках, которые содержат хотя бы один
отрицательный элемент (оформить в виде функции).
2. Номера строк и столбцов всех седловых точек матрицы (оформить в виде
процедуры).
ПРИМЕЧАНИЕ
177
Матрица А имеет седловую точку Ау, если Ау является минимальным
элементом в i-й строке и максимальным в j-м столбце.
7.
1. Для заданной матрицы размером 8x8 найти такие kt что k-я строка
матрицы совпадает с k-u столбцом (оформить в виде процедуры).
2. Найти сумму элементов в тех строках, которые содержат хотя бы один
отрицательный элемент (оформить в виде функции).
8.
1. Характеристикой столбца целочисленной матрицы назовем сумму
модулей его отрицательных нечетных элементов. Переставляя столбцы
заданной матрицы, расположить их в соответствии с ростом
характеристик (оформить в виде процедуры).
2. Найти сумму элементов в тех столбцах, которые содержат хотя бы один
отрицательный элемент (оформить в виде функции).
9.
1. Соседями элемента Аij в матрице назовем элементы Аkl, где i-1ki+1, j-
1l j + 1, (k,l) (i,j). Операция сглаживания матрицы дает новую
матрицу того же размера, каждый элемент которой получается как
среднее арифметическое имеющихся соседей соответствующего элемента
исходной матрицы. Построить результат сглаживания заданной
вещественной матрицы размером 10х10 (оформить в виде процедуры).
2. В сглаженной матрице найти сумму модулей элементов, расположенных
ниже главной диагонали (оформить в виде функции).
10.
1. Соседями элемента Аij в матрице назовем элементы Аkl, где i-1ki+1, j-
1l j + 1, (k,l) (i,j). Элемент матрицы называется локальным
минимумом, если он строго меньше всех имеющихся у него соседей
(определение соседних элементов см. в варианте 9). Подсчитать
178
количество локальных минимумов заданной матрицы размером 10x10
(оформить в виде процедуры).
2. Найти сумму модулей элементов, расположенных выше главной
диагонали (оформить в виде функции).
11.
1. Коэффициенты системы линейных уравнений заданы в виде
прямоугольной матрицы. С помощью допустимых преобразований
привести систему к треугольному виду (оформить в виде процедуры).
2. Найти количество строк, среднее арифметическое элементов которых
меньше заданной величины (оформить в виде функции).
12.
1. Уплотнить заданную матрицу, удаляя из нее строки и столбцы,
заполненные нулями (оформить в виде процедуры).
2. Найти номер первой из строк, содержащих хотя бы один положительный
элемент (оформить в виде функции).
13.
Осуществить циклический сдвиг элементов прямоугольной матрицы на
п элементов вправо или вниз (в зависимости от введенного режима), п
может быть больше количества элементов в строке или столбце (оформить в
виде процедуры).
14.
Осуществить циклический сдвиг элементов квадратной матрицы
размером MxN вправо на k элементов таким образом: элементы первой
строки сдвигаются в последний столбец сверху вниз, из него — в
последнюю строку справа налево, из нее — в первый столбец снизу вверх, из
него — в первую строку; для остальных элементов — аналогично (оформить
в виде процедуры).
179
15.
1. Дана целочисленная прямоугольная матрица. Определить номер первого
из столбцов, содержащих хотя бы один нулевой элемент (оформить в виде
функции).
2. Характеристикой строки целочисленной матрицы назовем сумму ее
отрицательных четных элементов. Переставляя строки заданной матрицы,
расположить их в соответствии с убыванием характеристик (оформить в
виде процедуры).
16.
1. Упорядочить строки целочисленной прямоугольной матрицы по
возрастанию количества одинаковых элементов в каждой строке
(оформить в виде процедуры).
2. Найти номер первого из столбцов, не содержащих ни одного
отрицательного элемента (оформить в виде функции).
17.
1. Путем перестановки элементов квадратной вещественной матрицы
добиться того, чтобы ее максимальный элемент находился в левом
верхнем углу, следующий по величине — в позиции (2, 2), следующий по
величине — в позиции (3, 3) и т. д., заполнив таким образом всю главную
диагональ (оформить в виде процедуры).
2. Найти номер первой из строк, не содержащих ни одного положительного
элемента (оформить в виде функции).
18.
Дана целочисленная прямоугольная матрица. Определить:
1. Количество строк, содержащих хотя бы один нулевой элемент (оформить
в виде функции).
2. Номер столбца, в котором находится самая длинная серия одинаковых
элементов (оформить в виде процедуры).
180
19.
Дана целочисленная квадратная матрица. Определить:
1. Сумму элементов в тех строках, которые не содержат отрицательных
элементов (оформить в виде функции).
2. Минимум среди сумм элементов диагоналей, параллельных главной
диагонали матрицы (оформить в виде процедуры).
20.
Дани целочисленная прямоугольная матрица. Определить:
1. Количество отрицательных элементов в тех строках, которые содержат
хотя бы один нулевой элемент (Оформить в виде функции).
2. Номера строк и столбцов всех седловых точек матрицы (оформить в виде
процедуры).
ПРИМЕЧАНИЕ
Матрица А имеет седловую точку Аij, если Aij является минимальным
элементом в i-й строке и максимальным в j-м столбце.
181
Лабораторная работа №7.
Множества, строки и записи
Задание на лабораторную работу
Сравнить программы для поиска простых чисел, приведенные ниже (с
использованием множеств) и в лабораторной работе №3. Выполнить один из
вариантов индивидуальных заданий, пользуясь информацией из следующего
раздела.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить разделы
«Структурированный тип данных – записи», «Структурированный тип
данных – множества».
Варианты индивидуальных заданий
1. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО, дата рождения. Программа должна
выполнять следующие действия: ввод данных, поиск человека по
заданной дате рождения и вывод информации о нём.
2. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Название товара, цена. Программа должна
выполнять следующие действия: ввод данных, изменение цены заданного
товара.
3. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО сотрудника, подразделение, оклад.
Программа должна выполнять следующие действия: ввод данных,
просмотр списка сотрудников заданного подразделения по одному
человеку с командами «перейти к следующему» и «перейти к
предыдущему».
182
4. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО автора, название книги, общее количество
книг в библиотеке, количество выданных книг, номер шкафа, где хранится
книга. Программа должна выполнять следующие действия: ввод данных,
по заданным полям «Автор» и «Название» определить возможно ли
выдать книгу, если да – указать номер шкафа.
5. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Наименование товара, цена. Программа должна
выполнять следующие действия: ввод данных, удаления товара.
6. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО студента, массив оценок. Программа
должна выполнять следующие действия: ввод данных (фамилии
студентов, массивы оценок заполняются нулями), запись заданному
студенту очередной оценки.
7. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Наименование товара, цена, количество на
складе. Программа должна выполнять следующие действия: ввод данных,
вывод на экран «счета» (т.е. наименования товара, количества, стоимости
единицы товара и общая стоимость заказа) на заданное количество товара,
если он есть на складе.
8. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО сотрудника, почасовая оплата,
количество часов. Программа должна выполнять следующие действия:
ввод данных (изначально информация записывается в поля "ФИО
сотрудника" и " почасовая оплата", поле "количество часов" заполняется
нулями), запись количества часов заданному сотруднику, расчет и вывод
ведомости на зарплату сотрудникам (т.е. выводится таблица с колонками:
ФИО сотрудника, почасовая оплата, количество часов, зарплата за месяц).
183
Лабораторная работа №8.
Файлы
Задание на лабораторную работу
Добавить к программе из лабораторной работы №7 следующие
действия:
сохранение информации в текстовый файл
загрузка информации из текстового файла
сохранение информации в типизированный файл
загрузка информации из типизированного файла
Оформить все действия в виде процедур.
Необходимая информация
Для выполнения лабораторной работы необходимо изучить разделы
«Структурированный тип данных – записи», «Структурированный тип
данных – множества».
Варианты индивидуальных заданий
Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО, дата рождения. Программа должна
выполнять следующие действия: ввод данных, поиск человека по заданной
дате рождения и вывод информации о нём.
9. Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Название товара, цена. Программа должна
выполнять следующие действия: ввод данных, изменение цены заданного
товара.
10.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО сотрудника, подразделение, оклад.
Программа должна выполнять следующие действия: ввод данных, просмотр
184
списка сотрудников заданного подразделения по одному человеку с
командами «перейти к следующему» и «перейти к предыдущему».
11.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО автора, название книги, общее количество
книг в библиотеке, количество выданных книг, номер шкафа, где хранится
книга. Программа должна выполнять следующие действия: ввод данных, по
заданным полям «Автор» и «Название» определить возможно ли выдать
книгу, если да – указать номер шкафа.
12.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Наименование товара, цена. Программа должна
выполнять следующие действия: ввод данных, удаления товара.
13.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО студента, массив оценок. Программа должна
выполнять следующие действия: ввод данных (фамилии студентов, массивы
оценок заполняются нулями), запись заданному студенту очередной оценки.
14.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: Наименование товара, цена, количество на складе.
Программа должна выполнять следующие действия: ввод данных, вывод на
экран «счета» (т.е. наименования товара, количества, стоимости единицы
товара и общая стоимость заказа) на заданное количество товара, если он
есть на складе.
15.Написать программу, в которой описывается массив записей, хранящий
следующую информацию: ФИО сотрудника, почасовая оплата, количество
часов. Программа должна выполнять следующие действия: ввод данных
(изначально информация записывается в поля "ФИО сотрудника" и "
почасовая оплата", поле "количество часов" заполняется нулями), запись
количества часов заданному сотруднику, расчет и вывод ведомости на
зарплату сотрудникам (т.е. выводится таблица с колонками: ФИО
сотрудника, почасовая оплата, количество часов, зарплата за месяц).
185
Контрольные вопросы
16.Что такое алгоритм?
17.Какие есть алгоритмические структуры?
18.Как представляются алгоритмические структуры с помощью блок-схем?
19.Что такое язык программирования?
20.Как классифицируются языки программирования?
21.Какие есть способы трансляции программ?
22.Зачем нужно транслировать программы?
23.Алфавит языка Паскаль.
24.Идентификаторы языка Паскаль.
25.Что такое константа?
26.Что такое переменная?
27.Что такое тип данных?
28.Приведите пример простейшей программы на языке Паскаль.
29.Приведите структуру программы на языке Паскаль.
30.Перечислите простые типы данных. Дайте краткую характеристику
каждому типу.
31.Приведите пример использования целых чисел.
32.Приведите пример использования вещественных чисел.
33.Приведите пример использования символов.
34.Назначение функций ввода/вывода. Пример использования.
35.Процедуры write и writeln. Синтаксическая конструкция, логика работы.
36.Приведите пример использования процедуры write или writeln.
37.Процедуры read и readln. Синтаксическая конструкция, логика работы.
38.Приведите пример использования процедуры read или readln.
39.Оператор присваивания. Синтаксическая конструкция, логика работы.
40.Какие арифметические операции есть в языке Паскаль? Приведите пример
их использования.
186
41.Какие операции отношения есть в языке Паскаль? Приведите пример их
использования.
42.Какие логические операции есть в языке Паскаль? Приведите пример их
использования.
43.Приоритет операций.
44.Приведите пример выражения, напишите порядок вычисления выражения.
45.Алгоритмическая структура выбор вариантов.
46.Оператор if. Синтаксическая конструкция, логика работы.
47.Оператор case. Синтаксическая конструкция, логика работы.
48.Приведите пример использования оператора case.
49.Приведите пример использования оператора if.
50.Оператор цикла while. Опишите синтаксическую конструкцию, логику
работы.
51.Оператор цикла repeat…until. Опишите синтаксическую конструкцию,
логику работы.
52.Оператор цикла for. Опишите синтаксическую конструкцию, логику
работы.
53.Приведите пример использования оператора цикла while.
54.Приведите пример использования оператора цикла repeat…until.
55.Приведите пример использования оператора цикла for.
56.Приведите пример использования оператора цикла for с символьной
переменной цикла.
57.Оператор goto. Опишите синтаксическую конструкцию, логику работы.
58.Перечислите структурированные типы, дайте им характеристику.
59.Как объявить переменную типа массив? Опишите синтаксическую
конструкцию, приведите пример.
60.Как объявить переменную типа строка? Опишите синтаксическую
конструкцию, приведите пример.
61.Как объявить переменную типа запись? Опишите синтаксическую
конструкцию, приведите пример.
187
62.Как изменить значение элемента массива?
63.Что такое двумерный массив?
64.Как изменить значение элемента двумерного массива?
65.Как ввести все элементы массива?
66.Как ввести все поля записи?
67.Как вывести все элементы массива?
68.Как вывести все поля записи?
69.Функция length(). Назначение, описание, пример использования.
70.Какие операции можно выполнять с переменными типа строка?
71.Для чего и как используется оператор with? Приведите пример.
72.Объявление переменных типа массив записей.
73.Приведите пример добавления элемента в массив структур.
74.Приведите пример удаления элемента из массива структур.
75.Приведите пример поиска элемента в массиве структур.
76.Что такое подпрограмма?
77.Какие виды подпрограмм есть в языке Паскаль?
78.Как описать и использовать свою функцию на языке Паскаль? Приведите
пример.
79.Что такое параметры и возвращаемое значение функции. Привести
пример.
80.Как описать и использовать свою процедуру на языке Паскаль? Приведите
пример.
81.Что такое параметры-значения и как их использовать?
82.Что такое параметры-переменные и как их использовать?
83.Каковы правила определения видимости?
84.Что такое модуль?
85.Какие разделы содержит модуль?
86.Как модули подключаются к основной программе?
87.Какие функции и процедуры модуля можно вызывать извне модуля, а
какие нет?
188
88.Что такое файл?
89.Какие виды файлов есть в языке Паскаль?
90.Что такое текстовый файл?
91.Как объявить переменную типа текстовый файл?
92.Что такое типизированный файл?
93.Как объявить переменную типа типизированный файл?
94.Что такое нетипизированный файл?
95.Как объявить переменную типа нетипизированный файл?
96.Какие режимы можно использовать в Паскале при инициации файлов?
97.Как создать новый каталог?
98.Как изменить текущий каталог?
99.Как переименовать файл?
100. Как переместить файл?
101. Как скопировать файл?
102. Как удалить файл?
103. Как удалить каталог?
104. Как открыть файл?
105. Как закрыть файл?
106. Как используются процедуры записи и чтения информации при работе
с текстовыми файлами.
107. Как используются процедуры записи и чтения информации при работе
с типизированными файлами.
108. Как используются процедуры записи и чтения информации при работе
с нетипизированными файлами.
109. Операция @. Назначение, описание, пример использования.
110. Что такое указатель?
111. Как выделить память? Привести пример.
112. Как освободить динамически выделенную память? Привести пример.
113. Как задать цвет текста в графическом режиме работы монитора?
114. Как задать цвет фона в графическом режиме работы монитора?
189
115. Что такое графический драйвер?
116. Что такое режим драйвера?
117. Что такое графический примитив?
118. Как нарисовать линию?
119. Как нарисовать эллипс?
120. Как нарисовать закрашенный эллипс?
121. Как нарисовать прямоугольник?
122. Как нарисовать закрашенный прямоугольник?
123. Как нарисовать точку?
124. Как задать область вывода?
125. Как задать тип линии?
126. Как переместить картинку по экрану?
127. Как инициализировать графический режим работы монитора?
128. Как восстановить текстовый режим работы монитора?
190
Литература
а) Основная литература
1. Фаронов В.В. Turbo Pascal 7.0 Начальный курс. Учебное пособие. М.:
«ОМД Групп», 2003.- 616с.
2. Павловская Т.А. Паскаль. Программирование на языке высокого
уровня. Учебник для ВУЗов. СПб: «Питер», 2007. – 393с.
3. Павловская Т.А. Паскаль. Программирование на языке высокого
уровня. Практикум. СПб: «Питер», 2007. – 317с.
б) Дополнительная литература
1. Лавров С. Программирование. Математические основы, средства,
теория СПб.: BHV, 2002. – 304 с.
2. Культин Н. Turbo Pascal в задачах и примерах. Учебное пособие. СПб.:
BHV, 2007. – 384 с.
3. Немюгин С.А. Turbo Pascal. Программирование на языке высокого
уровня. Учебник для ВУЗов. 2 изд. СПб: «Питер», 2007. – 544 с.
4. Керниган Б. Язык программирования Си. изд.2 Киев: Диалектика, 2006.
– 352 с.
191