Текст
                    Часть I
КОМПЬЮТЕР
И ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ
Глава 1. КОМПЬЮТЕР—АЛГОРИТМ—ПРОГРАММА
Важнейшая роль в развитии современного общества в конце XX в. принадле-
жит информатизации. По характеру и значению для будущего человеческой циви-
лизации процесс информатизации можно сравнить с переходом человечества от
аграрного общества к индустриальному. Подобно тому, как изобретение механи-
ческого двигателя открыло эру комплексной механизации и автоматизации физи-
ческого труда, изобретение ЭВМ сделало то же самое в отношении труда умствен-
ного.
Процесс информатизации характеризуется использованием информации в ка-
честве общественного продукта, обеспечивающего интенсификацию всех сфер эко-
номики, ускорение научно-технического прогресса, интеллектуализацию основных
видов человеческой деятельности и демократизацию обшества. В результате вне-
дрения новых информационных технологий значительно изменяются общественно-
экономические структуры, формируется гибкое динамичное общество, способное
к активной самооценке и выбору целей развития, быстрой и адекватной реакции на
изменение внешней и внутренней среды.
1.1. СТРУКТУРА ПЕРСОНАЛЬНОГО КОМПЬЮТЕРА
Основой информатизации является использование электронно-вычислительной
техники (ЭВТ) для сбора, накопления, обработки и передачи информации. ЭВМ
(англ. — computer) — комплекс технических средств, предназначенных для автома-
тической обработки информации в процессе решения вычислительных и информа-
ционных задач.
Компьютер — это сложная вычислительная система, каждая часть которой
имеет свое функциональное назначение. Рассмотрим структуру персонального ком-
пьютера (ПЭВМ), предназначенного для индивидуального пользования. Общий
вид персонального компьютера показан на рис. 1.1.
Основой конструкции является системный блок, управляющий всеми осталь-
ными устройствами компьютера, выполняющий хранение и обработку информа-

6 Глава 1 ции. В этом блоке размещены: блок питания, который обеспечивает все части ком- пьютера электрическим питанием, системная плата с процессором и памятью, а также устройства записи-считывания информации на гибких магнитных дисках — накопители на гибких магнитных дисках. Рис. 1.1. Общий вид персо- нального компьютера: 1 - видеомонитор; 2 - системный блок; 3 - клавиатура. 1.1.1. Процессор В процессоре (в литературе часто его называют CPU — central processing unit — центральное процессорное устройство) выполняются арифметические и логиче- ские операции, а также формируются управляющие сигналы ко всем устройствам компьютера. Мировым лидером в производстве процессоров для персональных ЭВМ является американская корпорация Intel. Выпускаемые ею процессоры Intel 80286, Intel 80386, Intel 80486, Intel PENTIUM с огромной скоростью выполняют элементарные арифметические и логические операции, на которые разбиваются процессы обработки информации любой сложности. Важнейшей характеристикой процессора является тактовая частота — величи- на, показывающая, сколько элементарных операций — тактов микропроцессор вы- полняет за одну секунду. Тактовая частота измеряется в мегагерцах (МГц) (1 МГц = 1 млн. тактов в секунду) и для разных процессоров имеет следующие значения: 4,77 МГц (Intel 8088), 16—25 МГц (Intel 80286), 25^0 МГц (Intel 80386), 33—66 МГц (Intel 80486). Быстродействие процессора определяется отношением тактовой частоты к количеству тактов, требующихся процессору для выполнения самой про- стой команды. Процессоры Intel 80286 и Intel 80386 могут дополняться сопроцессорами типа Intel 80287, Intel 80387, специализирующимися на выполнении арифметических операций и увеличивающими производительность компьютера при выполнении
Компьютер - алгоритм - программа 7 трудоемких математических расчетов. В отличие от них процессор Intel 80486DX включает в себя арифметический сопроцессор. 1.1.2. Память Память (memory) предназначена для хранения данных и программ их обра- ботки. Различают следующие виды памяти компьютера: внутреннюю и внешнюю. Встроенная в компьютер память называется внутренней. Она разделяется на постоянное запоминающее устройство (ПЗУ, или ROM — Read Only Memory) и оперативную память (RAM — Random Access Memory). В ПЗУ компьютера хранит- ся базовая система ввода-вывода (BIOS), которая состоит из программы тестирова- ния памяти и периферийного оборудования компьютера, а также программы запус- ка операционной системы. Оперативной памятью называется программно-адре- суемая память, быстродействие которой соизмеримо с быстродействием процессо- ра. В ней хранятся исполняемые в данный момент программы и оперативно необ- ходимые для этого данные. Недостатком оперативной памяти является ее энергоза- висимость, т. е. при выключении компьютера все содержимое оперативной памяти стирается Объем оперативной памяти является одной из важнейших характери- стик компьютера. Типичные размеры оперативной памяти современных персо- нальных компьютеров от I до 16 Мбайт. Достаточно быстрые компьютеры (на основе процессоров с тактовой частотой 25 МГц и более), чтобы процессор не простаивал, оснащаются кэш-памятью, т. е. "сверхоперативной" памятью относительно небольшого объема (от 64 до 256 Кбайт). Кэш-память (cache memory — память немедленного доступа) является "посредником" между процессором и оперативной памятью. В ней хранятся наибо- лее часто используемые участки оперативной памяти. За счет того, что время дос- тупа процессора к кэш-памяти в несколько раз меньше, чем к обычной памяти, среднее время доступа к памяти значительно уменьшается. Внешней памятью называются средства памяти на сменных носителях (магнитные диски, магнитные ленты, перфоленты), предназначенные для хранения больших массивов данных. Для хранения архивов, переноса информации с одного компьютера на другой широко используются гибкие магнитные диски (FDD — Floppy Disk Drive). Для работы с ними в системный блок встроен накопитель ин- формации на гибких магнитных дисках (НГМД — дисковод) емкостью от 360 Кбайт до 2,88 Мбайт (1 Мбайт памяти позволяет запомнить около 700 страниц ма- шинописного текста). Гнбкие магнитные диски (дискеты) представляют собой тон- кие пластиковые диски, покрытые специальным магнитным материалом и запеча- танные в пластиковую обложку. Для считывания или записи информации дискеты вставляются в дисковод. Диск в работающем дисководе вращается, а вдоль его по- верхности перемещается магнитная головка. Процесс считывания или записи ин- формации основан на таких же принципах, как и в магнитофоне, только информа- ция на дискетах записывается концентрическими кольцами — дорожками, распола- гаясь небольшими порциями фиксированной длины — секторами. На персональ-
8 Глава I ных компьютерах широко распространены следующие типы дискет: диаметром 5,25 дюйма (объем памяти от 360 Кбайт до 1,2 Мбайта) и 3,5 дюйма (объем памяти от 720 Кбайт до 1,44 Мбайта). Примечание 1 дюйм — английская мера длины (примерно равен 2,54 см). Дискеты бывают с двойной плотностью записи (DS/DD — Double Sided/Double Density — двусторонний гибкий диск двойной плотности) или высокой плотностью записи (DS/HD — Double Sided/High Density — двусторонний гибкий диск высокой плотности). Для предупреждения случайной порчи информации на дискетах преду- смотрена защита от записи. На 5,25-дюймовых дискетах существует специальная прорезь, которая может быть заклеена для защиты от записи. На 3,5-дюймовых дисках имеется пластмассовый переключатель, разрешающий или запрещающий запись. t Для постоянного хранения информации, используемой при работе с компьюте- ром, предназначены накопители на жестких магнитных дисках (HDD—Hard Disk Drive). Часто их называют винчестерами. Они представляют собой малогабарит- ный пакет из нескольких жестких магнитных дисков, вращающихся с высокой ско- ростью на одной оси и размещенных в герметичном корпусе вместе с головками за- писи-чтения. Информация располагается дорожками и секторами, при этом группа дорожек на всех дисках винчестера, расположенных на одинаковом расстоянии от их общей оси, называется цилиндром. Считывание и запись информации осуществ- ляются сразу несколькими магнитными головками одновременно со всех дисков пакета. Название “винчестер’* возникло из-за того, что первоначально такие нако- пители изготавливались из 2 дисков по 30 Мбайт, составляющих единый блок; сум- марная емкость получаемого накопителя обозначалась цифрами 30/30, подобно ка- либру старинного охотничьего ружья "Винчестер". Емкость винчестеров значи- тельно больше, чем гибких магнитных дисков, и может иметь значение от 20 Мбайт до 1 Гбайта и более. В качестве устройств внешней памяти большой емкости все большее распро- странение получают оптические диски, или CD-ROM (Compact Disk Read Only Memory — компакт-диск, предназначенный только для чтения). Емкость CD-ROM дисков — сотни мегабайт. 1.1.3. Клавиатура Клавиатура компьютера является одним из основных устройств ввода в компь- ютер информации и команд на ее обработку. На рис. 1.2 показана клавиатура широко распространенного стандарта, состоя- щая из 101 клавиши. Алфавитно-цифровые клавиши. Зона алфавитно-цифровых клавиш анало- гична клавиатуре обычной пишущей машинки. Буквенные клавиши в верхнем ре- гистре печатают прописные буквы, а в нижнем регистре — строчные.
Компьютер - алгоритм - программа 9 Рис. 1.2. Клавиатура компьютера 1 — алфавитно-цифровые клавиши; 2 — стандартные управляющие клавиши; 3 — клавиши управле- ния перемещением курсора; 4 — малая цифровая клавиатура, 5 — универсальные управляющие кла- виши: 6 — световые индикаторы функций (NumLock, CapsLock, ScroIlLock). Цифровые клавиши в нижнем регистре печатают цифры, а в верхнем — знаки и символы. В зависимости от выполняемых программ клавиши могут изменять свое назначение. Каждой клавише может соответствовать любое значение по желанию програм- миста. Обычно вы работаете со значениями клавиш, которые определяет операци- онная система MS-DOS. Стандартные управляющие клавиши. Стандартные управляющие клавиши расположены в каждой части клавиатуры: слева и справа от буквенно-цифровой клавиатуры, на цифровой клавиатуре и справа от функциональных клавиш. Они имеют следующее назначение: Enter (на некоторых клавиатурах — Return) — используется для завершения ввода команды, а в системах подготовки текста служит для перевода курсора в на- чало следующей строки. Backspace — находится над клавишей Enter. При каждом нажатии курсор пе- редвигается на одну позицию влево, и при этом стирается символ слева от курсора. Shift — при прижатой клавише Shift все символы печатаются в верхнем реги- стре. Если же светится световой индикатор CapsLock, то при нажатии клавиши Shift все символы печатаются в нижнем регистре. CapsLock — фиксатор прописных букв. Эта клавиша переключает алфавитно- цифровую клавиатуру в верхний регистр и при этом включает световой индикатор
10 Глава 1 CapsLock. Если этот индикатор светится, то все символы печатаются в верхнем ре- гистре, а при нажатии клавиши Shift — в нижнем. Каждое нажатие CapsLock вы- зывает переключение регистра клавиатуры на противоположный режим. Действие этой клавиши распространяется только на алфавитные символы и не распространя- ется на цифровые клавиши и знаки препинания. Tab — передвигает курсор на определенное количество позиций вправо (в нижнем регистре) или влево (в верхнем регистре Shift+Tab). Эта клавиша также используется для перехода из одного поля ввода в другое при работе с диалоговы- ми окнами. Ctrl (Control — управление) — эта клавиша включает управляющий режим клавиш, при котором клавиши выполняют не ввод литер, а команды или функции. Alt (Alternate — изменение) — всегда используется вместе с другими клавиша- ми для выполнения команды или функции. Esc (Escape — спасаться, убегать) — находится в левом верхнем углу кла- виатуры и используется для прекращения выполнения команды. NumLock (Numeric Lock) — переключатель режимов малой цифровой клавиа- туры. При включенном световом индикаторе цифровая клавиатура будет печатать цифры и знаки. Если индикатор NumLock не светится, то клавиши цифровой кла- виатуры управляют движением курсора по экрану. ScrollLock (блокировка прокрутки) — переключает клавиши со стрелками в такой режим, при котором они перемешают не курсор, а сам текст. При этом вклю- чается (выключается) световой индикатор ScrollLock. Если он светится, то при на- жатии клавиш со стрелкой вверх или вниз экран прокручивается вверх или вниз. Обычно клавиша ScrollLock программируется для выполнения других функции и редко соответствует первоначальному назначению. Примечание. Курсор — указатель места на экране, где будет происходить действие. Он обычно имеет вид мерцающей черточки и располагается ниже знакоместа, в котором появится вводимый символ или произойдет что-нибудь другое. Клавиши управления пере- мещением курсора находятся между алфавитно-цифровой и цифровой клавиатурами. Они выполняют те же функции, что и клавиши управления курсором на цифровой клавиатуре. Это позволяет одновременно управлять курсором и пользоваться цифровой клавиатурой. Print Screen — выводит на принтер содержимое экрана. Pause — временно приостанавливает выполнение программы. Для продолже- ния следует нажать любую клавишу. Клавиши управления перемещением курсора. Insert — используется для переключения режима ввода символов: ввод с раздвиганием строки (вставка) или ввод с заменой ранее набранных символов (замена). Delete — при нажатии этой клавиши удаляется символ, находящийся над кур- сором, а весь текст в строке справа от курсора подтягивается на один символ влево. Ноте — переместить курсор в начало строки.
Компьютер - алгоритм - программа П End — переместить курсор в конец строки. PageUp — переместить курсор на страницу (25 строк) вверх. PageDown — переместить курсор на страницу (25 строк) вниз. Клавиши со стрелками вверх (UpArrow), вниз (DownArrow), влево (LeftArrow), вправо (RightArrow) служат для перемещения курсора на строку вверх, на строку вниз, на позицию влево, на позицию вправо. Малая цифровая клавиатура. Малая цифровая клавиатура расположена в правой части клавиатуры компьютера. Клавиша NumLock (блокировка цифр) предназначена для переключения режима использования клавиш малой цифровой клавиатуры. При нажатии на клавишу NumLock выполняется переключение режи- ма и изменяется состояние светового индикатора NumLock. Если светится индика- тор NumLock, тогда каждая клавиша печатает цифру, написанную на ней. Клави- ши " и "+" означают соответствующие арифметические операции. Клавиша Enter выполняет те же функции, что и клавише Enter на алфавитно-циф- ровой клавиатуре, а в режиме калькулятора нажимается для получения результата. Если индикатор NumLock не светится, то клавиши цифровой клавиатуры использу- ются для управления перемещением курсора, т. е. дублируют клавиши Insert, Delete, Home, End. PageUp. PageDown. Универсальные управляющие клавиши. Универсальные управляющие кла- виши расположены в верхнем ряду клавиатуры. Называются они так потому, что их назначение определяется текущей прикладной программой. Существуют опре- деленные неформальные соглашения, которых обычно придерживаются разработ- чики программного обеспечения, например: клавиша F1 почти всегда вызывает подсказку, F10 часто служит для активизации основного меню.- Световые индикаторы функций. Маленькие окошки в верхнем правом углу клавиатуры называются световыми индикаторами функций. Их назначение — ука- зывать, включены ли соответствующие функции. Если светится CapsLock, то бук- венная клавиатура работает в верхнем регистре. К цифрам и символам это не отно- сится. Если вы в это аремя будете нажимать Shift, то буквы будут печататься в нижнем регистре. Когда светится индикатор NumLock, цифровая клавиатура печа- тает цифры. Если NumLock выключен, то она используется для управления курсо- ром Если светится индикатор Scroll Lock, то при нажатии клавиш со стрелкой вверх или вниз экран прокручивается вверх или вниз. Стандартные комбинации клавиш. Некоторые функции можно вызвать, только одновременно нажимая несколько клавиш. Комбинация клавиш Ctrl+Alt+Delete — перезагружает систему без отключе- ния питания. Ctrl+Break —• прерывает программу и выходит в DOS. На экране печатается
12 Глава 1 Ctrl+NumLock — прерывает программу до тех пор, пока вы не нажмете лю- бую клавишу. Эта функция работает не с любым программным обеспечением. Ctrl+Home — очищает экран, переводит курсор в левый верхний угол экрана. Действует только в DOS. Переключение клавиатуры для ввода русских букв. Для переключения кла- виатуры в русский или латинский регистр используются различные комбинации клавиш, определяемые программами-русификаторами клавиатуры, например, RightShift, LeftShift+RightShift или RightCtrl и т.п. Как правило, программа-русифи- катор при запуске выводит на экран сообщение о способе переключения клавиату- ры в русский или латинский регистр. Ввод дополнительных символов. При работе с различными программами на экране часто можно видеть символы, изображение которых отсутствует на клавиа- туре. Для ввода таких символов нужно знать таблицу кодирования символов ком- пьютера. На персональных компьютерах широко используется таблица ASCII (American Standard Code for Information Interchange — американский стандартный код для обмена информацией), в которой насчитывается 256 символов. Она пред- ставлена в приложении А. Вводится символ следующим образом: при прижатой клавише Alt на малой цифровой клавиатуре набирается цифровой код. Например, чтобы ввести верти- кальную черту, надо прижать Alt и набрать код 179; чтобы задать стрелку вправо, надо прижать Alt и набрать код 26. О программном управлении клавиатурой рас- сказано в приложении В. 1.1.4. Видеомонитор Результаты обработки информации выводятся из компьютера на экран дисплея (видеомонитора). Видеомонитор (видео — информация, представленная в зритель- ных образах, мониторинг — наблюдение, непрерывное слежение) служит окном в компьютер. Через него пользователь наблюдает за работой компьютера и узнает о результатах решения задач. По устройству и принципу действия видеомонитор по- хож на цветной телевизор. Для генерации видеосигнала монитору в компьютере имеется специальное уст- ройство — видеоадаптер. Информация об изображении на экране хранится в видео- памяти — специальных микросхемах видеоадаптера. Видеомониторы бывают цветными и монохромными. Качество изображения информации на видеомониторе определяется разрешающей способностью — раз- мером минимального элемента изображения или количеством точек, изображае- мых на экране, а также воспроизводимой цветовой гаммой. Наиболее распространены видеомониторы следующих типов: • CGA (Color Graphics Adapter) имеет разрешающую способность 320x200 то- чек (320 точек по горизонтали и 200 по вертикали) при воспроизведении 4 цветов.
Компьютер - алгоритм - программа 13 • EGA (Enhanced Graphics Adapter) имеет разрешающую способность 640- х35О точек при воспроизведении 16 цветов. • VGA (Virtual Graphics Array) имеет разрешающую способность 640x480 точек (минимальный размер изображения 0,31— 0,39 мм) при воспроизведении 16 цветов. • SVGA (Super VGA) в зависимости от графического режима имеет разрешаю- щую способность: 320x200, 640x480, 800x600, 1024x768 точек при воспроизведе- нии от 16 до 16 млн цветов. Видеомонитор может работать в двух режимах: текстовом и графическом. В текстовом режиме на экране чаще всего располагается 80 столбцов и 25 строк, но возможны и другие режимы (80x43 и 40x25). В графическом режиме существует доступ к каждой точке экрана, что дает возможность сформировать любое изобра- жение. При работе в графическом режиме необходимо хранить информацию о цве- те каждой точки экрана, поэтому требуется значительно больший объем видеопа- мяти. Подробно об управлении выаодом информации на экран видеомонитора вы узнаете в гл. 14,15. 1.1.5. ДОПОЛНИТЕЛЬНЫЕ УСТРОЙСТВА КОМПЬЮТЕРА Принтер. С подключением к компьютеру различных устройств он приобретает дополнительные возможности. Например, если к компьютеру подключить принтер, то вы сможете напечатать информацию на бумаге. По принципу формирования изображения на бумаге различают принтеры следующих видов: матричные, струй- ные и лазерные. В наиболее распространенных матричных принтерах (рис. 1.3) элементы изо- бражения на бумаге формируются с помощью точечной матрицы. Печатающая го- ловка матричного принтера содержит ряд тонких металли- ческих стержней (иголок). Го- ловка движется вдоль печатае- мой строки, а стержни в нуж- ный момент ударяют по бума- ге через красящую ленту, и в этом месте на бумаге образу- ется черная точка. Из отдель- Р1,с- 13- Обший вид матричного принтера ных точек формируются буквы, символы и элементы графического изображения. В струйных принтерах изображение формируется микрокаплями специальных чернил. Этот способ обеспечивает более высокое качество печати, удобен для цветной печати. Самое высокое качество печати в настоящее время обеспечивают лазерные принтеры. В этих принтерах изображение переносится на бумагу со специального барабана, к которому электрически притягиваются частички краски. Барабан элек-
14 Глава 1 тризуется за счет облучения лазером. Оригинал-макет данной книги напечатан на лазерном принтере. Манипулятор “мышь”. Манипулятор “мышь” предназначен для ввода в ком- пьютер информации. Он представляет собой небольшую коробочку с двумя или тремя клавишами. Может быть, вид быстро перемещающейся коробочки, соеди- ненной с компьютером гибким кабелем, вызвал у создателей определенные ассо- циации, и поэтому это устройство назвали “мышь”. При движении мыши по плоской поверхности одновременно поворачивается встроенный в нижнюю часть ее корпуса небольшой резиновый шарик; Движение шарика фиксируется датчиками, передается в компьютер и отображается на его эк- ране специальным указателем (курсором мыши). Управление компьютером с помощью мыши сводится к указанию курсором на объект на экране, нажатию, удержанию и отпусканию клавиш. Например, чтобы переместить объект по экрану компьютера, нужно установить курсор мыши на объ- ект, прижать левую кнопку и, перемещая мышь по столу, задать новое положение объекта на экране, после чего отпустить кнопку. Есть мыши, в которых перемеще- ' ние по поверхности стола копируется не шариком, а лазерным лучом. Модем. Для обмена информацией с другими компьютерами используется мо- дем. Модем служит для преобразования цифрового сигнала, выдаваемого компью- тером в аналоговый электромагнитный сигнал, пригодный для передачи по теле- фонным линиям связи (модуляция), а также наоборот, — для преобразования элек- тромагнитного сигнала аналогового характера, поступившего в компьютер по теле- фонным линиям связи, в цифровой сигнал, пригодный для обработки компьютером (демодуляция). Одной из основных характеристик модема является максимальная скорость передачи данных. Она измеряется в бодах (1 бод = 1бит/с). Используя мо- дем, вы можете получить доступ к российским и международным компьютерным сетям, быстро и надежно передавать и получать интересующую вас информацию. Устройство, сочетающее возможности модема и средства для обмена факсимиль- ными изображениями, называется факс-модемом. Факс-модемы являются одним из непременных атрибутов офисов современных фирм. 1.2. ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПЕРСОНАЛЬНОГО КОМПЬЮТЕРА Компьютер сам по себе ("Hardware" -— "жесткий товар" — аппаратные средст- ва, аппаратура) без разработанных человеком для него программ ("Software" — "мягкий товар" — программные средства, программное обеспечение) не может вы- полнить какой-либо работы. Программным обеспечением (ПО) называют органи- зованную совокупность программ постоянного употребления, ориентирующую ЭВМ на тот или иной класс применений.
Компьютер - алгоритм - программа 15 Различают три основные группы программ для компьютера: системное, при- кладное программное обеспечение и инструментальные средства, обеспечивающие создание новых программ для ЭВМ.. 1.2.1. Системное программное обеспечение t Системное программное обеспечение — программы, предназначенные для управления работой компьютера, объединения различных устройств вычислитель- ной техники в единую вычислительную систему, организации диалога пользова- тель — ЭВМ и выполнения работ, связанных с обслуживанием вычислительных машин. К системным программам относят: операционные системы, всевозможные сервисные программы, облегчающие пользователю взаимодействие с компьютером (программы-драйверы обслуживания различных периферийных устройств компью- тера, программы-оболочки, диагностические программы др.), программы, обеспе- чивающие работу компьютеров в сети. Например, для уточнения информации о со- ставе вычислительной системы, характеристиках ее аппаратного и программного обеспечения широко используется программа sysinfo. 1.2.2. Прикладное программное обеспечение Прикладное программное обеспечение — программы для решения класса за- дач в определенной области применения систем обработки данных. Примеры при- кладных программ: программа для обработки экспериментальных данных, про- грамма бухгалтерского учета, игровые, обучающие программы и т.п. Текст данной книги создан с помощью прикладной программы — редактора текстов Microsoft Word, иллюстрации подготовлены с помощью прикладной программы — графиче- ского редактора. 1.2.3. Инструментальные средства Инструментальные средства (системы программирования) — это пакеты про- грамм для создания или изменения программ для ЭВМ. Современные системы про- граммирования предоставляют программисту мощные и удобные средства для раз- работки программ. В главе 2 вы познакомитесь с этими возможностями на примере системы программирования Турбо Паскаль версии 7.0. 1.3. ОСНОВНЫЕ ЭТАПЫ РЕШЕНИЯ ЗАДАЧ НА КОМПЬЮТЕРЕ Процесс решения задач на компьютере — это совместная деятельность челове- ка и ЭВМ. Этот процесс можно представить в виде нескольких последовательных этапов. На долю человека приходятся этапы, связанные с творческой деятельно- стью — постановкой, алгоритмизацией, программированием задач и анализом ре- зультатов, а на долю компьютера — этапы обработки информации в соответствии с разработанным алгоритмом. Рассмотрим эти этапы на следующем примере: пусть требуется вычислить сум- му двух целых чисел и вывести на экран видеомонитора результат.
16 Глава 1 Первый этап — постановка задачи. На этом этапе участвует человек, хо- рошо представляющий предметную область задачи. Он должен четко определить цель задачи, дать словесное описание содержания задачи и предложить общий подход к ее решению. Для задачи вычисления суммы двух целых чисел человек, знающий, как складываются числа, может описать задачу следующим образом: ввести два целых числа, сложить их и вывести сумму в качестве результата реше- ния задачи. Второй этап — математическое или информационное моделирование. Цель этого этапа — создать такую математическую модель решаемой задачи, ко- торая может быть реализована в компьютере. Существует целый ряд задач, где математическая постановка сводится к простому перечислению формул и логи- ческих условий. Этот этап тесно связан с первым этцром, и его можно отдельно не рассматривать, однако возможно, что для полученной модели известны несколько методов решения, и тогда предстоит выбрать лучший. Для вышеописанной задачи данный этап сведется к следующему: введенные в компьютер числа запомним в па- мяти под именами А и В, затем вычислим значение суммы этих чисел по формуле А+В, и результат запомним в памяти под именем Summa. Третий этап — алгоритмизация задачи. На основе математического описа- ния необходимо разработать алгоритм решения. Алгоритмом называется точное предписание, определяющее последователь- ность действий исполнителя, направленных на решение поставленной задачи. В роли исполнителей алгоритмов могут выступать люди, Summa= А+В Рис. 1.4. Блок-схема алгоритма роботы, компьютеры. Используются различные способы записи алгорит- мов. Широко распространен словесный способ записи: это записи рецептов приготовления различных блюд в кулинарной книге, инструкции по использованию тех- нических устройств, правила правописания и многие другие. Наглядно представляется алгоритм языком блок-схем. Например, алгоритм решения задачи вычисления суммы двух целых чисел на языке блок-схем будет за- писан, как показано на рис. 1.4. Свойства алгоритма. При составлении и записи алгоритма необходимо обеспечить, чтобы он обладал рядом свойств. Однозначность алгоритма, под которой понима- ется единственность толкования исполнителем правил выполнения действий и порядка их выполнения. Чтобы алгоритм обладал этим свойством, он должен быть за- писан командами из системы команд исполнителя.
Компьютер - алгоритм - программа 17 Для нашего примера исполнитель алгоритма должен понимать такую запись действий, как сложить числа А и В. Конечность алгоритма — обязательность завершения каждого из действий, составляющих алгоритм, и завершим ость выполнения алгоритма в целом. Записан- ный на рис. 1.4 алгоритм обладает этим свойством, так как запись действий испол- нителя завершается записью об окончании алгоритма. Результативность алгоритма, предполагающая, что выполнение алгоритма должно завершиться получением определенных результатов. Алгоритм в нашем примере обладает этим свойством, так как для целых чисел А и В всегда будет вы- числена сумма. Массовость, т. е. возможность применения данного алгоритма для решения целого класса задач, отвечающих общей постановке задачи. Так как алгоритм, по- казанный на рис. 1.4, позволяет правильно подсчитать сумму не только чисел 2 и 3, но любой другой пары целых чисел, он обладает свойством массовости. Для того чтобы алгоритм обладал свойством массовости, следует составлять алгоритм, ис- пользуя обозначения величин и избегая конкретных значений. Правильность алгоритма, под которой понимается способность алгоритма давать правильные результаты решения поставленных задач. Представленный в примере алгоритм обладает свойством правильности, так как в нем использована правильная формула сложения целых чисел, и для любой пары целых чисел резуль- тат выполнения алгоритма будет равен их сумме. Четвертый этап — программирование. Программой называется план дейст- вий, подлежащих выполнению некоторым исполнителем, в качестве которого мо- жет выступать компьютер. Составление программы обеспечивает возможность выполнения алгоритма и соответственно поставленной задачи исполнителем-ком- пьютером. Во многих задачах при программировании на алгоритмическом языке часто пользуются заменой блока алгоритма на один или несколько операторов, введением новых блоков, заменой одних блоков другими. В главе 2 мы подробно рассмотрим пример программы решения поставленной выше задачи для компьюте- ра, записанной на языке Паскаль. Пятый этап — ввод программы и исходных данных в ЭВМ. Программа и исходные данные вводятся в ЭВМ с клавиатуры с помощью редактора текстов, и для постоянного хранения осуществляется их запись на гибкий или жесткий маг- нитный диск. Шестой этап — тестирование и отладка программы. На этом этапе проис- ходят исполнение алгоритма с помощью ЭВМ, поиск и исключение ошибок. При этом программисту приходится выполнять рутинную работу по проверке работы программы, поиску и исключению ошибок, и поэтому для сложных программ этот этап часто требует гораздо больше времени и сил, чем написание первоначального текста программы.
18 Глава 1 Отладка программы — сложный и нестандартный процесс. Исходный план отладки заключается в том, чтобы оттестировать программу на контрольных при- мерах. Контрольные примеры стремятся выбрать так, чтобы при работе с ними про- грамма прошла все основные пути блок-схемы алгоритма, поскольку на каждом из путей могут быть свои ошибки, а детализация плана зависит от того, как поведет себя программа на этих примерах: на одном она может зациклиться (т. е. бесконеч- но повторять одно и то же действие); на другом — дать явно неверный или бес- смысленный результат и т. д. Сложные программы отлаживают отдельными фраг- ментами. Для повышения качества выполнения этого этапа используются специальные программы —- отладчики, которые позволяют исполнить программу !'по шагам" с наблюдением за изменением значений переменных, выражений и других объектов программы, с отслеживанием выполняемых операторов. Седьмой этап — исполнение отлаженной программы и анализ результа- тов. На этом этапе программист запускает программу и задает исходные данные, требуемые по условию задачи. Полученные в результате решения выходные данные анализируются поста- новщиком задачи, и на основании этого анализа вырабатываются соответствующие решения, рекомендации, выводы. Например, если при решении задачи на компью- тере результат сложения двух чисел 2 и 3 будет 4, то следует сделать вывод о том, что надо изменить алгоритм и программу. Возможно, что по итогам анализа результатов потребуются пересмотр самого подхода к решению задачи и возврат к первому этапу для повторного выполнения всех этапов с учетом приобретенного опыта. Таким образом, в процессе создания программы некоторые этапы будут повторяться до тех пор, пока мы получим алго- ритм и программу, удовлетворяющие показанным выше свойствам. 1.4. КАК ВЫЗВАТЬ ПРОГРАММУ? Программы хранятся на дисках в виде программных файлов — совокупности команд для решения на компьютере определенной задачи. Файлы распознают по именам. Для запуска программы в самом простом случае, бывает достаточно задать в командной строке имя программного файла. Например, для загрузки среды про- граммирования Турбо Паскаль нужно задать команду turbo.exe или просто turbo. Контрольные вопросы и задания 1, Как вы понимаете процесс информатизации современного общества, насколько ве- лико его значение? 2. Что такое компьютер, из каких частей он состоит?
Компьютер - алгоритм - программа 19 3. Каковы назначение и основные характеристики процессора? 4. Зачем компьютеру нужна память? Ее виды, их назначение. Единицы измерения объе- ма информации, хранимой в памяти. 5. Как представлена внешняя память в используемом вами компьютере? 6. Что такое жесткий магнитный диск, чем он отличается от гибких магнитных дисков? 7. Назначение клавиатуры компьютера. Опишите назначение различных групп клавиш. 8. Опишите назначение стандартных управляющих клавиш. 9. Зачем нужна русификация клавиатуры, как переключить клавиатуру в русский (ла- тинский) регистр? ( 10. Опишите назначение видеомонитора и охарактеризуйте видеомонитор исполь- зуемого вами компьютера. 11. Каково назначение принтера? Охарактеризуйте различные типы принтеров. 12. Опишите внешний вид мыши, к чему сводится управление компьютером с помощью мыши? 13. Зачем нужен компьютеру модем? Какие возможности открываются пользователю компьютера, в котором установлен модем? 14. Какие еще устройства, расширяющие возможности компьютера, вы знаете? 15. Каково значение программ для компьютеров? 16. На какие группы подразделяется программное обеспечение компьютеров по выпол- няемым им функциям? 17. Опишите назначение системного программного обеспечения. Перечислите програм- мы, входящие в эту группу. 18. Опишите как вызвать программу? 19. Каково назначение прикладных программ. Приведите примеры прикладных про- грамм. 20. Какие программы называются инструментальными средствами? Назовите извест- ные вам инструментальные средства. 21. Перечислите основные этапы решения задач на ЭВМ. Поясните, какие действия вы- полняет разработчик программы на каждом этапе. 22. Что такое алгоритм? Каким свойствам должен он удовлетворять? Приведите пример словесного описания алгоритма и покажите, что он обладает указанными свойствами. 23. Какие вы знаете способы записи алгоритма? Приведите примеры алгоритмов, запи- санные разными способами: словесным,-формулами, блок-схемами. 24. Составьте и запишите алгоритмы: а) перехода улицы через переход, оборудованный светофором; б) деления отрезка пополам с помощью циркуля и линейки; в) вычисления скорости равномерного движения тела, если известны перемещение и время, за которое это перемещение совершилось.
20 Глада 1 25. Используя программу sysinfo (или аналогичную), выясните следующие характери- стики вашего компьютера: тип процессора и тактовую частоту; наличие сопроцессора; тип и версикЬ операционной системы; тип видеоадаптера, видеомонитора и текущий видеорежим; объем видеопамяти и размер видеобуфера для размещения одного экрана; какие внешние устройства включены в состав вычислительной системы, имена соответствующих драйверов и адреса размещения их в памяти; объем и распределение оперативной памяти компьютера. Примечание. Для запуска программы sysinfo задайте команду sysinfo.exe, после чего на экране раскрывается окно кратких сведений о вычислительной системе, а в верхней части экрана выводится главное меню, как показано на рис. 1.5. Для получения подробной справки нажатием клавиши F10 перейдите в главное меню и выберите нужный пункт. Для отказа от просмотра нажмите Esc. Для выхода из программы нажмите Alt+X. Рис. 1.5. Окно кратких сведений о вычислительной системе программы sysinfo г
Глава 2. ИНТЕГРИРОВАННАЯ СРЕДА ПРОГРАММИРОВАНИЯ ТУРБО ПАСКАЛЬ 7.0 2.1. ЯЗЫКИ ПРОГРАММИРОВАНИЯ Чтобы компьютер выполнил решение какой-либо задачи, ему необходимо по- лучить от человека инструкции, как ее решать. Набор таких инструкций для компь- ютера, направленный на решение конкретной задачи, называется компьютерной программой. Современные компьютеры не настолько совершенны, чтобы понимать про- граммы, записанные на каком-либо употребляемом человеком языке — русском, английском, японском ... Команды, предназначенные для ЭВМ, необходимо запи- сывать в понятной ей форме. С этой целью применяются языки программирования — искусственные языки, алфавит, словарный запас и структура которых удобны человеку и понятны компьютеру. В самом общем смысле языком программирования называется фиксированная система обозначений и правил для описания алгоритмов и структур данных. Языки программирования имеют как бы два лица. Одно из них обращено к человеку, ис- пользующему язык для записи своих программ, а другое адресовано ЭВМ, которая должна понимать команды. Исходя из этого все языки программирования делятся на языки низкого, высо- кого и сверхвысокого уровня. Язык низкого уровня — это средство записи инструкций компьютеру, просты- ми приказами-командами на аппаратном уровне. Такой язык отражает структуру данного класса ЭВМ и поэтому иногда называется машинно-ориентированным языком. Пользуясь системой команд, понятной компьютеру, можно описать алго- ритм любой сложности. Правда, такая запись для сложных задач будет настолько громоздкой, что у человека будет мало шансов сделать ее безошибочной, так как этот язык мало приспособлен для использования человеком, ведь запись програм- мы на этом языке представляет собой последовательность нулей и единиц.
22 Глава 2 Существенной особенностью языков программирования низкого уровня явля- ется жесткая ориентация на определенный тип аппаратуры (систему команд про- цессора). В стремлении приспособить язык программирования низкого уровня к че- ловеку разработан язык символического кодирования (автокод или язык ассембле- ра), структура команд которого определяется форматами команд и данными ма- шинного языка. Программа на таком языке ближе человеку, потому что операторы этого языка — те же команды, но они имеют мнемонические названия, а в качестве операндов используются не конкретные адреса в оперативной памяти, а их симво- лические имена. Более многочисленную группу составляют языки программирования высокого уровня, средства которых допускают описание задачи в наглядном, легко восприг нимаемом виде. Отличительной особенностью этих ^ы'ков является их ориентация не на систему команд той или иной ЭВМ, а на систему операторов, характерных для записи определенного класса алгоритмов. К языкам программирования этого типа относятся: Бейсик, Фортран, Алгол, Паскаль, Си. Программа на языках высо- кого уровня записывается системой обозначений, близкой человеку (например, фиксированным набором слов английского языка, имеющих строго определенное назначение). Программу на языке высокого уровня проще понять и значительно легче отладить. К языкам программирования сверхвысокого уровня можно отнести Алгол-68, при разработке которого сделана попытка формализовать описание языка, привед- шая к появлению абстрактной и конкретной программ. Абстрактная программа создается программистом, конкретная — выводится из первой. Предполагается, что при таком подходе принципиально невозможно породить неверную синтакси- чески (а в идеале и семантически) конкретную программу. Язык APL относят к языкам сверхвысокого/уровня за счет введения сверхмощных операций и операто- ров. Запись программ на таком языке получается компактной. Все вышеперечисленные языки — вычислительные. Более молодые — декла- ративные (непроцедурные) языки, отличительная черта которых — задание связей и отношений между объектами и величинами и отсутствие определения последова- тельности выполнения действий (Пролог). Такие языки сыграли важную роль в программировании, так как они дали толчок к разработке специализированных языков искусственного интеллекта и языков представления знаний. 2.1.1. Язык программирования Паскаль Язык программирования Паскаль (назван в честь выдающегося французского математика и философа Блеза Паскаля (1623 — 1662)), разработан в 1968 — 1971 гг. Никлаусом Виртом, профессором, директором Института информатики Швей- царской высшей политехнической школы. Язык Паскаль, созданный первоначаль- но для обучения программированию как систематической дисциплине, скоро стал
Интегрированная среда программирования Турбо Паскаль 7.0 23 широко использоваться для разработки программных средств в профессиональном программировании. Широкой популярности Паскаля среди программистов способствовали сле- дующие причины: • Благодаря своей компактности, удачному первоначальному описанию Пас- каль оказался достаточно легким для изучения. • Язык программирования Паскаль отражает фундаментальные и наиболее важные концепции (идеи) алгоритмов в очевидной и легко воспринимаемой фор- ме, что предоставляет программисту средства, помогающие проектировать про- граммы. • Язык Паскаль позволяет четко реализовать идеи структурного программиро- вания и структурной организации данных. * • Язык Паскаль сыграл большую роль в развитии методов аналитического до- казательства правильности программ и позволил реально перейти от методов от- ладки программ к системам автоматической проверки правильности программ. • Применение языка Паскаль значительно подняло "планку" надежности разра- батываемых программ за счет требований Паскаля к описанию используемых в программе переменных, проверки согласованности программы при компиляции без ее выполнения. • Использование в Паскале простых и гибких структур управления: ветвлений, циклов. 2.1.2. Трансляторы Так как текст записанной на Паскале программы не понятен компьютеру, то требуется перевести его на машинный язык. Такой перевод программы с языка программирования на язык машинных кодов называется трансляцией (translation — перевод), а выполняется он специальными программами — трансляторами. Существует три вида трансляторов: интерпретаторы, компиляторы и ассембле- ры. Интерпретатором называется транслятор, производящий пооператорную (покомандную) обработку и выполнение исходной программы. Компилятор преобразует (транслирует) всю программу в модуль на машин- ном языке, после этого программа записывается в память компьютера и лишь по- том исполняется. Ассемблеры переводят программу, записанную на языке ассемблера (автоко- да), в программу на машинном языке. Любой транслятор решает следующие основные задачи: • анализирует транслируемую программу, в частности определяет, содержит ли она синтаксические ошибки;
24 Глава 2 • генерирует выходную программу (ее часто называют объектной или рабочей) на языке команд ЭВМ (в некоторых случаях транслятор генерирует выходную про- грамму на промежуточном языке, например, на языке ассемблера); • распределяет память для выходной программы (в простейшем случае это за- ключается в назначении каждому фрагменту программы, переменным, константам, массивам и другим объектам своих адресов участков памяти). 2.2. ИСПОЛЬЗОВАНИЕ СРЕДЫ ПРОГРАММИРОВАНИЯ ТУРБО ПАСКАЛЬ Разработка программ на Паскале вкдючает в себя следующие действия (этапы разработки программы): ввод и редактирование текста программы на языке про- граммирования Паскаль, ее трансляцию, отладку. Для выполнения каждого этапа применяются специальные средства: для ввода и редактирования текста используется редактор текстов, для трансляции програм- мы — компилятор, для построения исполняемого компьютером программного мо- • дуля с объединением разрозненных откомпилированных модулей и библиотекой стандартных процедур Паскаля — компоновщик (linker), для отладки программ с анализом ее поведения, поиском ошибок, просмотром и изменением содержимого ячеек памяти компьютера — отладчик (debugger). Для повышения качества и скорости разработки программ в середине 80-х гг. была создана система программирования Турбо Паскаль. Слово Турбо в названии системы программирования — это отражение торговой марки фирмы-разработчика Borland International, Inc. (США). Систему программирования Турбо Паскаль называют интегрированной (integration — объединение отдельных элементов в единое целое) средой програм- мирования, так как она объединяет в себе возможности ранее разрозненных средств, используемых при разработке программ: редактора текстов, компилятора, компоновщика, отладчика, и при этом обеспечивает программисту великолепные сервисные возможности. Часто ее кратко называют IDE (Integrated Development Environment — интегрированная среда разработки). Интегрированная среда программирования Турбо Паскаль версий 6.0 и 7.0 имеет следующие возможности: • множество накладывающихся окон; • поддержка мыши, меню, диалоговых окон; • многофайловый редактор, который может редактировать файлы до 1 Мбайта; • расширенные возможности отладки; • полное сохранение и восстановление среды разработки.
Интегрированная среда программирования Турбо Паскаль 7.0 25 К ее существенным отличиям от среды программирования Турбо Паскаль бо- лее ранних версий относятся: • объектно-ориентированная среда разработки прикладных программ Turbo Vision; • полные возможности встроенного ассемблера; • личные поля и методы в объявлении объектов; • директива расширенного синтаксиса $Х, которая позволяет вам интерпрети- ровать функции как процедуры (и игнорировать результаты функций); • генерация кода 286; • адресные ссылки в типизированных константах; • директивы ближних и дальних процедур; * • редактирование инициализированных данных из объектных файлов; • более быстрый монитор кучи, сокращающий фрагментацию (FreeMin и FreeList удалены); • расширенные возможности встроенной справочной системы с использовани- ем вырезки и вставки кода примеров для каждой библиотечной процедуры и функ- ции. Примечание. Кучей в Турбо Паскале называется сплошной массив байтов в памяти. Все примеры, приведенные в данном пособии, подготовлены, откомпилированы н отлажены в интегрированной среде программирования Турбо Паскаль версии 7.0. 2 .2.1. Основные файлы пакета Турбо Паскаль Если допустим, что система программирования Турбо Паскаль установлена на диске D: в каталоге D:\BORLAND\BP, то в каталоге ..\ВР находятся следующие ос- новные файлы Турбо Паскаля: TURBO.EXE — интегрированная среда программирования; TURBO.HLP — файл, содержащий данные для оперативной подсказки; TURBO.TP — файл конфигурации системы; TURBO.TPL — библиотека стандартных модулей Турбо Паскаля. В каталоге D:\BORLAND\BP\BGI находятся файлы, необходимые для работы в графическом режиме: GRAPH.TPU — модуль с графическими процедурами и функциями Турбо Паскаля, несколько файлов с расширением .BGI — драйверы различных типов видеосистем компьютеров, несколько файлов с расширением .CHR, содержащих векторные шрифты.
26 Глава 2 2 .2.2. Запуск интегрированной среды программирования Турбо Паскаль Для запуска интегрированной среды программирования нужно установить те- кущим каталог с Турбо Паскалем и (или) ввести команду: turbo.exe. После запуска программы экран компьютера будет иметь вид, показанный на рис. 2.1. FileEditSearch RunConpileDehugTools Options Uindou Help [Q]-- - —===-=_..----- HONRMEOO.PfiS =-_^ I Полоса заголовка Закрывающая кнопка Номер окна Имя редактируемого файла / Полоса меню Кнопка ' масштабирования Двойная рамка— признак активного окна Полосы скролинга Номер редактируемой \оки Строка статуса / Номер позиции / в строке Fl Help F2 Уголок для изменения размеров окна F3 Open fllt+F9 Compile F9 М<эке fllt+FlO Local menu Рис.2.1. Элементы окна редактирования интегрированной среды программирования Турбо Паскаль 7.0 На экране отображаются три видимых компонента интегрированной среды программирования: полоса меню в верхней части, область окна в центре и строка статуса внизу. Рассмотрим каждый компонент. Полоса меню и подменю. Полоса меню является основным доступом ко всем командам меню. Она становится невидимой только в то время, когда вы просмат- риваете вывод своей программы. Если полоса меню активна, то заголовок меню будет высвечен; это текущее выбранное меню. Если за командой меню следует знак многоточия (..)., выбор команды приведет к выводу диалогового окна. Если за командой следует стрелка (>), то команда ведет в другое меню. Команда без знака многоточия или без стрелки указывает, что как только вы ее выберете, произойдет какое-то действие. Строка статуса. Строка статуса отображается в нижней строке экрана и вы- полняет следующие функции: • напоминает основные строки ключей и сокращений (или горячих клавиш), допустимых в этот момент в активном окне; • предоставляет самый быстрый вариант выполнения действий, отмечая горя- чие клавиши в строке статуса мышью вместо выбора команд из меню или нажатия последовательности клавишей;
Интегрированная среда программирования Турбо Паскаль 7.0 27 • содержит информацию о том, какая функция выполняется. Например, когда сохраняется редактируемый файл, в строке статуса выводится сообщение: "Saving filename..."; • предлагает краткие советы по выбранной команде меню и элементам диало- гового окна. При смене окна или изменении характера деятельности информация в строке статуса сразу же меняется. Одна из наиболее характерных строк статуса — та, ко- торую вы видите во время написания и редактирования программ в окне редактора. 2 .2.3. Использование клавиатуры для выбора команды меню Для выбора команды меню с помощью клавиатуры необходимо сделать сле- • дующее: 1. Нажать клавишу F10. Это делает полосу меню активной. 2. Чтобы выбрать меню, которое вы хотите посмотреть, используйте клавиши со стрелками. Затем нажмите Enter. Будет быстрее, если вы просто нажмете высве- ченную букву заголовка меню. Например, из полосы меню нажмите Е, чтобы быст- ро показать Edit-меню. Из любого места нажмите клавишу Alt и высвеченную бук- ву для просмотра требуемого меню. Чтобы прервать действие, нажмите клавишу Esc. 3. Опять используйте клавиши со стрелками для выбора требуемых команд. За- тем нажмите Enter. Для быстроты можно нажать высвеченную букву команды для ' того, чтобы выбрать ее, как только появилось меню. В этот момент Турбо Паскаль выполняет команду или показывает диалоговое окно, или показывает другое меню. 2 .2.4. Использование “мыши” для выбора команд меню Для выбора команды меню с помощью мыши выполните следующие операции: 1. Отметьте заголовок требуемого меню для его просмотра. 2. Отметьте требуемую команду. Вы можете также тащить за заголовок меню к команде меню. Освободите кнопку мыши на команде, которая нужна. (Если вы изменили свое решение, выйдите из меню; ни одна команда не будет выбрана). Заметим, что некоторые команды меню являются недоступными, когда нет смысла их выбирать. Вы можете, однако, выбрать (высветить) недоступную ко- манду, чтобы получить по ней подсказку. Примечание. Турбо Паскаль использует только левую кнопку мыши. Однако вы може- те настроить правую кнопку и также сделать другие настройки для мыши. 2 .2.5. Быстрые способы выбора команд меню Турбо Паскаль предлагает несколько быстрых способов для выбора команд ме- ню. Например, пользователи мыши могут преобразовать двухшаговый процесс в
28'Глава 2 одношаговый, проведя мышью от заголовка меню вниз к командам меню с освобо- ждением кнопки мыши при выборе требуемой команды. Для клавиатуры можно использовать несколько быстрых методов (или “горя- чих клавиш”) для доступа к полосе меню и выбора команд. Быстрые методы для диалоговых окон работают так, как они сделаны в меню. (При перемещений от ок- на ввода к группе кнопок или окон вам нужно держать нажатой клавишу Alt при нажатии высвеченной буквы). Многие элементы меню имеют соответствующие “горячие клавиши”; одно - или двухключевые сокращения^ которые немедленно активизируют эту команду или диалоговое окно. Можно также отметить мышью сокращения в строке статуса. В табл. 2.1 перечислены наиболее используемые Турбо Паскалем “горячие кла- виши”. * Таблица 2.1 Общие “горячие клавиши” Клавиша(и) Элемент меню Функция F1 Help Выводит на экран окно подсказки F2 File/Save Сохраняет файл, находящийся в активном окне редактора F3 File/Open Появляются диалоговое окно и возможность открыть файл F4 Run/Go to Cursor Запускает программу до строки, на которой стоит курсор F5 Window/Zoom Масштабирует активное окно F6 Window/Next Переходит к следующему открытому окну F7 Run/Trace Into Запускает вашу программу в режиме отладки с заходом внутрь процедур F8 Run/Step Over Запускает вашу программу в режиме отладки, минуя вызовы процедур F9 Compile/Make Запускает Маке текущего окна F10 (none) * Возвращает вас в полосу меню 2 .2.6. Окна Турбо Паскаля Почти все, что вы видите и делаете в среде Турбо Паскаль, происходит в окнах. Окно — это область экрана, которую можно перемещать, изменять в размере, перекрывать, закрывать и открывать. Интегрированная среда программирования Турбо Паскаль 6.0,7.0 позволяет иметь любое количество открытых окон (если позволяют память и размер кучи), но в любой момент времени может быть активным только одно окно. Активное окно — это окно, с которым вы в настоящий момент времени работаете. Любая команда, которую вы выбрали, или текст, который вы набрали, относится только к активному окну. (Если один и тот же файл открыт в нескольких окнах, действие будет применяться к файлу везде).
Интегрированная среда программирования Турбо Паскаль 7:0 29 Примечание. Можно увеличить число окон, которые потенциально могут быть откры- ты, путем увеличения размера кучи, используя опцию Startup (Options/Environ-ment). Опция — выбор. Существует несколько типов окон, но большинство из них имеют следующие элементы: • полоса заголовка; • закрывающая кнопка; • полосы скролинга; • уголок для изменения размеров окна; • кнопка масштабирования; , • номер окна. Расположение этих элементов показано на примере окна редактирования тек- ста программ на рис. 2.1. Быстрый выбор какого-либо пункта меню выполняется клавишами, показанны- ми в табл. 2.2. Таблица 2.2 “Горячие клавиши” меню Клавиша(и) Элемент меню Функция Alt+ПРОБЕЛ — меню Переносит вас в (System) меню Alt+C Compile меню Переносит вас в Compile меню Alt+D Debug меню Переносит вас в Debug меню Alt+E Edit меню Переносит вас в Edit меню Alt+F File меню Переносит вас в File меню Alt+H Help меню Переносит вас в Help меню Alt+O Options меню Переносит вас в Options меню Alt+R Run меню Переносит вас в Run меню Alt+S Search меню Переносит вас в Search меню Alt+W Window меню Переносит вас в Window меню Alt+X File/Exit Завершает Турбо Паскаль с выходом в DOS Активное окно можно различить по двойной рамочке. Оно всегда имеет за- крывающую кнопку, кнопку масштабирования, кнопки перемещения и уголок изменения размеров. Если окна перекрываются, то активное окно всегда находится над остальными (на переднем плане). Примечание. Если вы изменили файл в окне редактирования, то слева от номеров строки и столбца появится знак звездочки (*). Закрывающая кнопка окна — это кнопка в левом верхнем углу. Отметив эту кнопку, вы можете быстро закрыть окно. (Можно также выбрать Window/Close или
30 Глава 2 нажать клавишу AJt+F3). Окно справочной информации рассматривается как вре- менное и может быть закрыто посредством нажатия клавиши Esc. Полоса заголовка — находящаяся выше всех горизонтальная строка окна, со- держащая имя окна и номер окна. Вы можете дважды отметить кнопку, находясь на полосе заголовка, для того, чтобы масштабировать окно. Вы можете также та- щить за строку заголовка для перемещения окна. Каждое открытое окно в Турбо Паскале имеет номер окна в верхнем правом углу. Клавиша Alt+О выдает список всех открытых окон. Можно сделать окно активным (самым верхним) посредст- вом нажатия клавиши Alt в комбинации с номером окна. Например, если окно справочной информации имеет №3, но оно скрыто под другими окнами, то для бы- строго вынесения его на передний план можно нажать одновременно клавиши Alt и 3. * Кнопка масштабирования окна находится в верхнем правом углу окна. Если значок в этом углу изображает стрелку вверх, можно отметить эту стрелку для уве- личения окна до максимально возможного размера. Если значок представляет со- бой двуглавую стрелку, то окно уже Имеет свой максимальный размер. Если вы отметите двуглавую стрелку, то окно вернется к своему предыдущему размеру. Чтобы промасштабировать окно с помощью клавиатуры, выберите Window/Zoom или нажмите клавишу F5. Примечание. Дважды отметьте заголовок окна для его масштабирования или восста- новления. Полосы скролинга позволяют пользователям и мыши, и клавиатуры видеть, в каком месте файла они находятся, а также используются для перемещения содер- жания окна с помощью мыши. Для смещения содержания окна отметьте соответ- ствующую стрелку. (Для непрерывного перемещения держите кнопку мыши на- жатой). Отмечая затененную область с любой стороны полосы скролинга, вы мо- жете перескочить на страницу. Наконец, можно тащить полосу скролинга "за лю- бое место" для быстрого перемещения в окне в место, соответствующее позиции полосы скролинга. Уголок изменения размеров находится в нижнем правом углу окна. Можно та- щить за любой уголок, чтобы сделать окно больше или меньше. Вы можете отли- чить уголок изменения размеров по рамке из одной линии вместо рамки, из двой- ной линии, используемой в остальных местах окна. Для того чтобы изменить раз- меры с помощью клавиатуры, выберите Size/Move из Window-меню или нажмите одновременно клавиши Ctrl и F5. Управление окнами в интегрированной среде программирования Турбо Пас- каль выполняется следующим образом (табл. 2.3).
Интегрированная среда программирования Турбо Паскаль 7.0 31 Таблица 2.3 Управление окнам^ в интегрированной среде программирования Турбо Паскаль Операция над окном Способы выполнения Открыть окно Edit Выберите File/Open для открытия файла и покажите его в окне или нажми- те клавишу F3 Открыть другие окна Закрыть окно Выберите требуемое окно из Window-меню Выберите Close из Window-меню (или нажмите клавиши Alt+F3) или от- метьте закрывающую кнопку окна Активизировать окно Отметьте в любом месте окна или нажмите клавишу Alt плюс номер окна (в верхнем правом углу окна), или выберите Window/List, или нажмите Alt+О и выберите окно из списка, или выберите Window/Next или F6, что- бы сделать активным следующее окно (следующее в порядке, в каком они были открыты). Или нажмите клавиши Alt+F6 для активизации предыду- щего окна Передвинуть активное окно Тащите его заголовок или нажмите клавиши Ctrl+F5 Window/Size/ Move и используйте клавиши со стрелками, чтобы поместить окно там, где вам хо- чется, и нажмите Enter Изменить размер ак- тивного окна Тащите уголок для изменения размера (или любой угол). Или выберите Window/Size/Move и нажимайте Shift одновременно с клавишами со стрел- ками, затем нажмите Enter. Более быстрый способ состоит в нажатии Ctrl+F5 и затем в одновременном использовании Shift и клавиш со стрел- ками Масштабировать актив- ное окно Отметьте кнопку масштабирования в верхнем правом углу окна, или дваж- ды отметьте заголовок окна, или выберите Window/Zoom, или нажмите клавишу F5 Быстрый способ управления окнами выполняется клавишами, показанными в табл. 2.4. “Горячие клавиши” управления окнами Таблица 2.4 Клавиша(и). Элемент меню Функция Alt+# (none) Показывает окно, где # — номер окна, которое вы хотите по- смотреть Alt+0 Window/List Показывает список открытых окон Alt+F3 Window/Close Закрывает активное окно Alt+F5 Window/User Screen Показывает экран пользователя Shift+F6 Window/Previous Проходит назад через все открытые окна F5 Window/Zoom Увеличивает (уменьшает) активное окно F6 Window/Next Проходит вперед через все активные окна Ctrl+F5 Window/Size/Move Изменяет размер или позицию активного окна
32 Глава 2 2.2.7. Справочная система Турбо Паскаль Как было показано выше, интегрированная среда программирования Турбо Паскаль 6.0, 7.0 отличается расширенными возможностями встроенной справочной системы, которая позволяет программисту не только получить контекстно-ориенти- рованную справочную информацию, но и делать вырезки и вставки кода примеров для каждой библиотечной процедуры и функции в текст своей программы, возврв- титься назад к другим экранам подсказки (клавиши Alt+Fl), воспользоваться под- сказкой по справочной информации (клавиша FI, если вы уже находитесь в систе- ме справочной информации). Примечание. Название контекстно-ориентированная справочная система Турбо Пас- каль получила за возможность получения справочной информации, связанной с текущим со- стоянием среды программирования, по указанному элементу языка программирования. На- пример, для получения справочной информации о люОЬм пункте меню интегрированной среды программирования активизируйте этот пункт и нажмите клавишу F1; для получения справки по элементу языка в окне редактирования (оператору, функции и т.п) установите курсор на нужном элементе и нажмите клавиши Ctrl+Fl. Для получения справочной информации (за исключением случаев, когда управление переходит к вашей программе) нужно нажать клавишу F1 или отметить мышью нужный пункт меню Help. Меню Help (клавиша Alt+H) обеспечивает вас таблицей содержания системы справочной информации, подробным оглавлением, способностями поиска (Ctrl+Fl). Любой экран справочной информации может со- держать одно ключевое слово или несколько ключевых слов (высвеченных эле- ментов), по которым можно получить дополнительную справочную информацию. Использование клавиш для получения справочной информации отражает табл. 2.5. Таблица 2.5 “Горячие клавиши” получения встроеииой справочной информации Клавиша(и) Элемент меню Функция F1 Help/Contents Открывает контекстно — ориентированный экран справочной информации F1.F1 Help/Help on Help Вызывает справочную информацию по справочной информа- ции (нужно нажать только клавишу F1, если вы уже находи- тесь в системе справочной информации) Shift+Fl Help/Index Вызывает оглавление справочной информации Alt+Fl Help/Previous Topic Показывает предыдущий экран справочной информации Ctrl+Fl Help/Topic Search Вызывает специфическую информацию по языку только в ре- дакторе 2.2.8. Редактор интегрированной среды Составной частью интегрированной среды разработки программ является ре- дактор Турбо Паскаль, который имеет следующие возможности: • поддержку мыши;
Интегрированная среда программирования Турбо Паскаль 7.0 33 • поддержку больших файлов (до 1 Мбайта; ограничение в 2 Мбайта для всех комбинаций редактора); • Shift + клавиши со стрелками — для выбора текста; • окна редактора, которые можно передвигать, перекрывать и изменять в раз- мере; • мультифайловые возможности, что позволяет открывать несколько файлов одновременно; । • многочисленные окна, позволяющие иметь несколько представлений одного и того же файла или разных файлов; • разумный макроязык, позволяющий создавать свои собственнее команды ре- дактирования; • брать текст или примеры из окна справочной информации;. • редактируемый карман, допускающий вырезание, копирование и его переда- чу между окнами. Для управления редактором используются клавиши, описанные в табл. 2.6. Таблица 2.6 “Горячие клавиши” редактирования Клавиша(и) Элемент меню Функция Shift+стрелкн Ctrl+Del Ctrl+Ins Shift+Del Shift+Ins Ctrl+L F2 F3 (none) Edit/Clear Edit/Сору Edit/Cut Edit/Paste Search/Search Again File/Save File/Open Помечает фрагмент текста в активном окне редактирования Удаляет выбранный текст из окна и не помещает его в карман Копирует выбранный текст в карман Помещает выбранный текст в карман и удаляет его Помещает текст из кармана в активное окно Повторяет последнюю команду Find или Replace Сохраняет файл в активном окне редактора Позволяет вам открыть файл Интегрированная система программирования Турбо Паскаль включает в себя средства для трансляции программ и их отладки (компилятор, компоновщик, от- ладчик). Быстрое управление этими средствами с помощью “горячих клавиш” по- казано в табл. 2.7. Таблица 2.7 “Горячие клавиши” компиляции-запуска-отладки программ Клавиша(и) Элемент меню Функция AH+F9 Compile/Compile Компилирует последний файл в окне редактора Ctrl+F2 Run/Program Reset Переустанавливает выполняемую программу Ctrl+F4 Debug/Evahiate/Modify Вычисляет выражение Ctrl+F7 Debug/Add Watch Добавляет выражение для просмотра 2 116
34 Глава 2 Продолжение Клавиша(и) Элемент меню Функция Ctrl+F9 F4 F7 F8 F9 Run/Run Run/Go То Cursor Run/Trace Into Run/Step Over Compile/Make Запускает программу Запускает программу до позиции курсора Выполняет прослеживание внутри процедур Осуществляет перескакивание через вызовы процедур Выполняет Маке (компилирует /редактирует связи) программы Остальные возможности интегрированной системы программирования Турбо Паскаль подробно описаны в приложении С. Упражнение 1. Создадим нашу первую программу вычисления суммы двух целых чи- сел. * Сценарий взаимодействия человека и компьютера при решении данной задачи можно предложить следующий. Компьютер запрашивает у человека значение первого целого числа, считывает его и за- писывает в память под именем А, затем запрашивает значение второго целого числа, считы- вает его и записывает в память под именем В. После этого компьютер выполняет сложение . чисел А, В, записывает результат в память под именем Summa, выводит на экран сообще- ние "Сумма чисел = " и печатает значение величины Summa. Запись данного алгоритма на Паскале может быть представлена в виде следующей про- граммы: {Учебная программа Вычисление суммы двух целых чисел} program Tutorl; {Заголовок программы} var {Описание раздела переменных} А,В, Summa : integer; {Переменные А,В,Summa — целые} begin {Начало программы} Write('Введите значение целого числа А >'); {Вывод запроса на экран} Readln(A); {Ввод значения А с клавиатуры} Write('Введите значение целого числа В >'); Readin(В); Summa := А + В; {Вычисление переменной Summa} Write('Сумма чисел ',А, ' и ',В,' = ',Summa); {Вывод ответа} end. {Конец программы} Прочтите текст программы, обратите внимание на структуру программы. Примечания. 1. В данной программе использованы следующие резервированные слова языка Паскаль (слова, за которыми закреплено строго определенное значение): program — заголовок программы (определяет ее название и список параметров). Он полностью декоративный и не оказывает влияния на саму программу; var — начало объявления переменных (связывает идентификатор — имя переменной и ее тип с расположением в памяти, где хранится значение); integer — указание, что переменные А, В, Summa — целые числа, т. е. они могут принимать целочисленные значения, такие, как 2,3,0,287,21,0,32,287 и другие, на ин- тервале [ — 32768,32767];
Интегрированная среда программирования Турбо Паскаль 7.0 -j 5 begin — начало тела программы; end. —конец тела программы; Write (' Текст ’) — инструкция компьютеру о выводе на экран сообщения 'Текст' (обратите внимание на то, что текст справа и слева ограничен символом ‘ — апостроф); Readln (А) — инструкция компьютеру о считывании значения переменной А с кла- виатуры. 2 . Для вычисления суммы чисел А и В в программе использована запись инструкции выполнения вычислений присваивания суммы чисел А и В переменной Summa (присваива- ние записывается как Summa:=A + B. 3 . Каждая строка программы завершается знаком в конце программы ставится Пояснения к программе, не влияющие на исполнения, записываются в фигурных скобках {комментарий} или в круглых скобках со звездочкой (* пояснение *). 2.2.9. Ввод текста программы в окне редактора Для запуска среды программирования Турбо Паскаль введите команду TURBO и нажмите Enter. После запуска программы на экране раскроется окно редактирова- ния. Введите текст программы. Для удаления неверно введенных символов исполь- зуйте Backspace и Delete, а для перемещения внутри окна редактора используйте клавиши сО стрелками. Для завершения ввода нажимайте Enter в конце каждой строки. Если вы забыли команды редактора, посмотрите табл. 2.6. Для, использова- ния дополнительных возможностей нажатием клавиш Alt+F10 вызывайте локаль- ное меню. Текст программы в окне редактирования может выглядеть, как показано на рис. 2.2. j=t IJ -...... progran Tutor!; == NONAMEOO.PAS j...........= (заголЪвок программы) {описании раздела переменных) d.=[t] А.В. Sunna : integer; (переменные A.B.Sunma— целые) begin (начало программы) WriteC*Введите значение целого числа Й >*>; (вывод запроса на экран) ReadlnCfl); (ввод значения Й с клавиатуры) WriteC*Введите значение целого числа В >*>; ReadlnСВ); Sunna й * В; (вычисление переменной Summa) WriteС'Сумма чисел '.Й.* и '.В.' - '.Summa); (вывод ответа) end._ (конец программы) Рис.2.2. Окно редактирования с текстом программы первого упражнения
36 Глава! 2.2.10. Компиляция программы Выполните компиляцию программы, для чего нажмите Alt+F9 (см. табл. 2.7). Если вы ввели текст правильно, то на экран будет выведено сообщение об успеш- ности компиляции, как показано на рис. 2.3. Рнс.2.3. Окно сообщения о результате компиляции 2.2.11. Создание .ехе-файла В ответ на сообщение "Compile successful" (компиляция успешна) нажмите лю- бую клавишу. Если вам требуется записать программу как исполняемый файл (с расширени- ем .ехе) на магнитный диск, то выберите в главном меню пункт Compile, в котором выберите опцию Destination (назначение), и если справа от нее стоит слово Memory (память), указывающее, что выполняемый код будет храниться в памяти, нажмите клавишу Enter или щелкните левую кнопку мыши (при этом установка на- значения изменится и станет Disk (диск), как показано на рис. 2.4). Если опция Destination установлена в Disk, что указывает на запись выпол- няемого кода на магнитный диск в виде файла с расширением .ехе, то перейдите к опции Маке этого пункта меню. После установки назначения для создания .ехе-файла на магнитном диске вы- берите в меню Compile опцию Маке (сборка) или нажмите клавишу F9. При этом выполняется создание .ехе-файла на диске. Подробнее об использовании пункта Compile главного меню читайте в прило- жении С.
Интегрированная среда программирования Турбо Паскаль 7.0 Рис.2.4. Окно с установкой Destination в положение Disk 2.2.12. Исполнение программы В ответ на сообщение "Compile successful" (компиляция успешна) нажмите лю- бую клавишу. Запустите программу на исполнение клавишами Ctrl+F9 (см. табл. 2.7). После этого раскроется экран пользователя, и на нем появится сообщение: Введите значение целого числа А > На этот запрос введите целое число (например: 3) и нажмите Enter. Появится следующее сообщение: Введите значение целого числа В > На этот запрос введите целое число (например: 4) и нажмите Enter. После этого будет выполнен расчет суммы, результат выведен на экран и среда программирования активизирует окно редактирования так быстро, что вы не успее- те увидеть результат. 2.2.13. Просмотр выполнения программы на экране пользователя Чтобы посмотреть результат выполнения программы на экране пользователя, выберите Window/User Screen (или нажмите Alt+F5). Для значений переменных А=3, В=4 экран компьютера может выглядеть, как показано на рис. 2.5. Введите значение целого числа А > 3 Введите значение целого числа В > 4 Сумма чисел 3 и 4 = 7 Рис.2.5. Экран пользователя при выполнении программы первого упражнения
38 Глава 2 Изучите информацию, выведенную программой на экран пользователя, сопос- тавьте ее с ожидаемой и оцените правильность выполнения программы. Для воз- врата в среду Турбо Паскаль снова нажмите клавиши Alt+F5. 2.2.14. Сохранение программы на диске Пока файл текста первой программы имеет имя NONAMEOO, т. е. ему не при- своено конкретного имени. Сохраните текст программы на диске. Имя программ- ного файла должно отражать назначение программы и быть уникальным. 4 Примечание. Имя программы задается в соответствии с правилами DOS (не более 8 символов латинского регистра). Запишите программу на диск под именем Tutorl, для чего клавишами Alt+F пе- рейдите в File-меню (см. табл. 2.2), выберите пункт "Save as ..." (записать под но- вым именем), как показано на рис. 2.6. Lilile Edit Run Compile Debug Tools Options Hindoo Neu Open . . . Saue F3 F2 ЛI Saue as. - aue all Change dir... Print Printer setup.. DOS shell Exit 1ММЫ> i переменных) _______________iunna- целые) Дуо программы) £ I числа A >'>; (вывод запроса на экран) „Н значения А с клавиатуры) Б|Н числа В )' >; Alt+X сление переменной Summa) .' = '.Summa); (вывод ответа) ц программы) 1. 2. 3 . 4. 5. \BORLAND\BP\TUTOR1.PAS TUTORl.PAS X...\BP\TUTORxTUTORl.PAS \...\TUT0R\DEM_GRA4.PAS \...\EXAMPLE\EXAM15_4.PAS FlHelp|Saue the current file under a different name, directory Рис.2.6. Выбор пункта меню File "Save as..." для записи файла под новым именем После этого на экран выводится диалоговое окно Save File As, как показано на рис. 2.7. В окне ввода задайте имя программы Tutorl, нажимая Tab или Shift+Tab для перехода от одного элемента к другому (каждый элемент, когда он становится ак- тивным, высвечивается), перейдите к окну списка Files и установите текущим ди- ректорий D:\BORLAND\BP\TUTOR, в который нужно записать файл текста про- граммы. После того как вы задали имя файла и выбрали директорий, в который он бу- дет записан, нажатием Tab выберите кнопку [ ОК ]. Если вы передумали, то выбе- рите кнопку [ Cancel ] или нажмите Esc. Примечание. Выбрать кнопку означает активизировать ее и затем нажать клавишу Return или левую кнопку мыши.
Интегрированная среда программирования Турбо Паскаль 7.0 39 Рис.2.7. Диалоговое окно Save File As В случае затруднений, нажимая Tab, выберите кнопку [ Help ], и на экран будет выведена подсказка о диалоговом окне Save File As, как показано на рис. 2.8. Рис.2.8. Окно справочной информации о диалоговом окне Save File As Для использования дополнительных возможностей локального меню нажмите Alt+F10. Отмените подсказку, нажав клавишу Esc. Если вы еще не записали файл на диск, нажатием Tab выберите кнопку [ОК]. Файл записан. Теперь в строке заголовка окна редактирования данного файла запи- саны имя файла и каталог, в котором он размещен: D:\BORLAND\BP\TUTOR\TUTOR1 .PAS, как показано на рис. 2.9.
40 Глава 2 File Ed i t Search Run Conpile Debug r=[|]= --- = D:\BORLAND\BP\TUTOR1. PBS 2=[ t ] program Tutorl; {заголовок программы} - uar (описание раздела переменных} А,В. Summa : integer; (переменные A.B.Summa- целые} begin (начало программы} WriteC* Введите значение целого числа А }'}; (вывод запроса на экран} Beadin(А); (ввод значения А с клавиатуры} Write(*Введите значение целого числа В }'}; Beadln(B); Summa := А * В; (вычисление переменной Summa} Write('CyMMa чисел 'и *.В»* = '.Summa); (вывод ответа} end. (конец программы} Fl Help F2 Saue F3 Open A It+F9 Conpile F9 Make Alt+FlO Local menu Рнс.2.9. Вид окна редактирования после записи файла TUTOR 1 на диск 2.2.15. Завершение работы в интегрированной среде программирования Завершите работу интегрированной среды программирования Турбо Паскаль, для чего клавишами Alt+F перейдите в File-меню, в этом меню выберите пункт "Exit" или нажмите Alt+X (см. табл. 2.2). Упражнение 2. Измените вашу первую программу так, чтобы она вычисляла не только сумму, но и разность двух целых чисел (А — В). Сценарий взаимодействия человека и ком- пьютера при решении данной задачи аналогичен сценарию в первой задаче (упражнение 1), а алгоритм дополнен вычислением разности двух целых чисел и выводом результата ее вы- числения на экран. 2.2.16. Открытие файла текста программы Запустите интегрированную среду программирования Турбо Паскаль и считай- те файл с текстом первой программы Tutorl, для чего клавишами Alt+F перейдите в File-меню, выберите пункт "Open" (открыть файл) или нажмите клавишу F3. На экран компьютера будет выведено окно выбора открываемого файла из списка, как показано на рис. 2.10. Нажимая клавиши Tab или Shift+Tab для перехода от одного элемента к друго- му (каждый элемент, когда он становится активным, высвечивается), перейдите к окну списка Files и установите текущим директорий D:\BORLAND\ BP\TUTOR, из которого нужно считать файл текста программы. Установите курсор (подсветите) файл TUTOR1.PAS, после этого нажатием клавиши Tab выберите кнопку [ Open ]. Если вы передумали, то выберите кнопку [ Cancel ] или нажмите кла- вишу Esc.
Интегрированная среда программирования Турбо Паскаль 7.0 41 File Edit Search Run Compile Pehn- Fl HelpГ Enter directory path and file mask Рис.2.10. Окно редактирования с панелью выбора открываемого файла из списка В случае затруднений, нажимая клавишу Tab, выберите кнопку [ Help ] и про- смотрите подсказку. Для отказа от подсказки нажмите клавишу Esc. Нажатием кла- виши Tab выберите кнопку [ Open ]. Файл TUTOR 1.PAS открыт в окне редактиро- вания. Отредактируйте текст программы, изменив его следующим образом: {Учебная программа Вычисление суммы и разности двух целых чисел} program Tutor2; {Заголовок программы} var {Описание раздела переменных} А,В, Summa,Raznost : Integer; ' {Переменные А, В, Summa, Raznost — целые числа} begin {Начало программы} Write(1Введите значение целого числа А >1); {Вывод запроса на экран} Readln(А); {Ввод значения А с клавиатуры} Write('Введите значение целого числа В >'); Readln(В); Summa := А + В; {Вычисление переменной Summa} Raznost := А — В; {Вычисление разности чисел} Writein('Сумма чисел ' ,А, ' и ',В,' = Summa); {Вывод ответа с переводом курсора на следующую строку} Write('Разность чисел * ,А,' и ',В,' = ',Raznost); end. {Конец программы} 2.2.17. Получение справочной информации по редактору Для получения справочной информации по операциям редактирования посмот- рите табл. 2.6 или клавишей F1 вызовите экран подсказки; нажимая клавишу PageDown, перейдите к перечню подсказок о функциях редактирования, как пока- зано на рис. 2.11.
42 Глава 2 , File Edit Search Run Conpile Debug Tools Opt ions Uindow Help | TUIOKI.FAS 1 3=[Т1 (заголовок программы} ===== Help .... = program Tutorl; var A,B, Sum begin WriteC' Been Readln(A>; WriteC Ввод ReadIn(B>; Summa :“ A Write('Сумм end. ^^Тог^ЕпУоггёДЯо? экран) Choose one of these for more information: Using Turbo Pascal windows Using the editor Find dialog box (text search) Replace dialog box Edit menu FileISave command File|Save Rs command "Block read" (^KR> dialog box "Block write" (^KW> dialog box Fl Help on help ftlt+Fl Preu ions topic Shift+F1 Help index Esc Close help Рис.2.11. Окно справочной системы с перечнем подсказок о функциях редактирования Нажимая клавишу Tab, выберите пункт Using the editor, затем выбирая: Cursor Movement Commands — перемещение курсора, Insert & Delete Commands — команды вставки и удаления, Block Commands — операции с блоками текста, Miscellaneous Commands — управляющие команды и нажимая Alt+Fl для возврата к предыдущему экрану помощи, найдите нужную справочную информацию. Чтобы закрыть окно подсказки, нажмите Esc. После завершения редактирования текста программы выполните ее компиля- цию, для чего нажмите Alt+F9. Если программа отредактирована без ошибок, то за- пустите ее на выполнение клавишами Ctrl+F9. 2.2.18. Ошибки, обнаруженные при компиляции Часто по результатам компиляции на экран компьютера выводится сообщение об ошибке. Это объясняется тем, что язык программирования Паскаль имеет грам- матические правила, которые нужно выполнять. Однако в отличие от английского или русского языка в программе на Паскале недопустимо использование сленга или плохого синтаксиса — компилятор должен всегда однозначно понимать, ЧТО вы хотите сказать. Наиболее частыми ошибками начинающих программистов на языке Паскаль бывают: Unknown identifier (неизвестный идентификатор) или expected (ожидается и другие. Паскаль требует, чтобы вы объявили все переменные, типы данных, константы и подпрограммы, точнее, все идентификаторы перед их использованием. Если вы обратитесь к необъявленному идентификатору или пропустите его, то получите ошибку. Другой частой ошибкой является несоответствие пар begin..end, присваи- вание несовместимым типам данных (например, присваивание действительного
Интегрированная среда программирования Турбо Паскаль 7.0 43 значения целой переменной), несоответствие числа и типа параметров в вызовах процедур и функций и т. д. Допустим, что компиляция прекращена, а на экран выведено сообщение об ошибке, как, например, показано на рис. 2.12. File h R u T no Is Options Uindow Help ’ггог 5» Syntax error var описание раздела переменных) Й.В» Summa,Raznost : Integer; (переменные A, В. Summa. Raznost - целые) begin (начало программы) Write('Введите значение целого числа Й >*>; (вывод запроса на экран) Readln(A); (ввод значения Й с клавиатуры) Write('Введите значение целого числа В >'); ReadIn(В); Summa := А * В; (вычисление переменной Summa) разность :* Й - В; (вычисление разности чисел) WriteIn('Сумма чисел *»А»* и '.В,' " '.Summa); (вывод ответа с переводом курсора на следутщуп строку) Write('Разность чисел *.Й.* и *.В.' " '.Raznost); end. (конец программы) 12:4 е 1р aue F3 Open A It+F9 Compile F9 Make Alt*F10 Local menu Рис.2.12. Окно редактирования с выводом сообщения об ошибке при компиляции В данном случае сообщение об ошибке "Error 5: Syntax error." вызвано синтак- сической ошибкой — записью имени переменной (идентификатора) "Разность" на русском языке. Место ошибки в тексте программы показано курсором. Примечание. Первое нажатие клавиши очистит это сообщение, а нажатие комбинации клавиш Ctrl+Q W будет показывать его снова до тех пор, пока вы не измените файл или не перекомпилируете его. Для получения подсказки по данной ошибке нажмите клавишу F1. После озна- комления со справочной информацией отмените окно подсказки клавишей Esc, ис- правьте ошибку и заново выполните компиляцию программы. Если по результатам компиляции будет выведено сообщение об отсутствии ошибок в программе, то за- пустите ее на исполнение клавишами Ctrl+F9. В ответ на запрос компьютера введи- те значения целых чисел А, В, после чего будет выполнен расчет суммы и разности чисел А и В и результаты выведены на экран. Чтобы посмотреть результат выпол- нения программы на экране пользователя, нажмите клавиши Alt+F5. Для значений переменных А=12, В=5 экран компьютера будет выглядеть как показано на рис. 2.13. Введите значение целого числа А > 12 Введите значение целого числа В > 5 Сумма чисел 12 и 5 = 17 Разность чисел 12 и 5 — 7 Рис.2.13. Экран пользователя при выполнении программы второго упражнения
44 Глава! Запишите программу на диск под именем Tutor2, для чего клавишами Alt+F пе- рейдите в File-меню (см. табл. 2.2), выберите пункт "Save as ...", как показано на рис.2.5. В окне ввода "Save file as" отредактируйте имя программы Tutor2, нажимая клавиши Tab или Shift+Tab, перейдите к окну списка Files и установите текущим директорий D:\BORLAND\BP\TUTOR, в который нужно записать файл текста про- граммы. После этого выберите кнопку [ ОК ]. Файл записан. Контрольные вопросы и задания Вопросы. 1. Что такое языки программирования? Их классификация. 2. Язык программирования Паскаль, его особенности. 3. Зачем нужны трансляторы? Что такое компилятор, чем он отличается от интерпрета- тора? 4. Каковы возможности и в чем преимущества интегрированной среды программирова- ния? 5. Перечислите основные файлы среды программирования Турбо Паскаль и их назначе- ние. Как запустить среду программирования Турбо Паскаль? 6. Перечислите основные компоненты окна редактирования программ среды програм- мирования Турбо Паскаль. В чем их назначение? 7. В чем заключается назначение пунктов File, Edit, Run, Compile главного меню среды программирования Турбо Паскаль? 8. В чем заключается назначение следующих опций пункта меню File: Open, Save As, DOS shell ? 9. В чем заключается назначение следующих опций пункта меню File: New, Save, Exit? 10. Опишите значение информации в строке статуса окна редактирования интегриро- ванной среды программирования: Fl Help F2 Save F3 Open Alt+F9 Compile F9 Make Alt+FlO Local menu 11. Что такое локальное меню, какие локальные меню имеются в интегрированной сре- де программирования? Как их вызывать? 12. Как откомпилировать файл программы? 13. В чем отличие пункта Run от пункта Compile главного меню интегрированной сре- ды программирования? 14. Как посмотреть результаты выполнения программы в окне пользователя? 15. Каково назначение информационно-справочной системы среды программирования Турбо Паскаль? Почему ее называют контекстно-ориентированной? Как осуществляется управление системой помощи? Задания. 1. Пользуясь интегрированной средой программирования Турбо Паскаль, считать с дис- ка программу Tutor 1, измените ее так, чтобы она вместо суммы двух целых чисел вычисля- ла их произведение и записывала его значение в память под именем Proizved. Откомпилиро- вать программу, проверить ее действие дця значений переменных: А = 6, В = — 3. Записать измененную программу на диск под именем Tutor3.pas. 2. Изменить составленную в задании 1 программу так, чтобы она вычисляла площадь прямоугольника по двум введенным сторонам и выводила на экран сообщение о результате вычислений.
Часть II НАЧАЛА ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ ТУРБО ПАСКАЛЬ • * Глава 3. ОСНОВНЫЕ ЭЛЕМЕНТЫ ЯЗЫКА ПАСКАЛЬ 3.1. ОСНОВНЫЕ ЭЛЕМЕНТЫ ПРОГРАММИРОВАНИЯ Большинство программ создаются для решения какой-либо задачи. В процессе решения задачи на компьютере пользователю нужно ввести обрабатываемые дан- ные, указать, как их обрабатывать, задать способ вывода полученных результатов. Поэтому как программист вы должны знать: • как ввести информацию в память (ввод); • как хранить информацию в памяти (данные); • как указать правильные команды для обработки данных (операции); • как передать обратно данные из программы пользователю (вывод). Вы должны упорядочить команды таким образом, чтобы: • некоторые из них выполнялись только в том случае, если соблюдается неко- торое условие или ряд условий (условное выполнение); • другие выполнялись повторно некоторое число раз (циклы); • третьи выделялись в отдельные части, которые могут быть неоднократно вы- полнены в разных местах программы (подпрограммы). Таким образом, как программист вы должны уметь использовать семь основ- ных элементов программирования: ввод, данные, операции, вывод, условное вы- полнение, циклы и подпрограммы и на их основе строить программы. Этот список не является исчерпывающим, однако он содержит те элементы, которые обычно присущи всем программам (й процедурным языкам программиро- вания). Многие языки программирования, в том числе и Паскаль, имеют еще до- полнительные средства, которые вы изучите далее. Ниже дается краткое описание каждого элемента.
46 Глава 3 Ввод означает считывание значений, поступающих с клавиатуры, с диска или из порта ввода-вывода. Данные — это константы, переменные и структуры, содержащие числа (целые и вещественные), текст (символы и строки) или адреса (переменных и структур). Операции осуществляют присваивание значений, их комбинирование (сложе- ние, деление и т. д. ) и сравнение значений (равные, неравные и т. д.). Вывод означает запись информации на экран, на диск или в порт ввода-выво- да. Условное выполнение предполагает выполнение набора команд в случае, если удовлетворяется (является истинным) некоторое условие (если это условие не удовлетворяется, то эти команды пропускаются или же выполняется другой на- бор команд) или если некоторый элемент данных имеет некоторое специальное значение или значение из некоторого спектра. Благодаря циклам некоторый набор команд выполняется повторно или фикси- рованное число раз, или пока является истинным некоторое условие, или пока не- которое условие не стало истинным. Подпрограмма представляет собой набор команд, который имеет имя и может быть неоднократно вызван из любого места программы по его имени. Теперь рассмотрим, как эти элементы используются в Паскале. 3.2. АЛФАВИТ И СЛОВАРЬ ЯЗЫКА ПАСКАЛЬ Языком называется совокупность символов, соглашений и правил, используе- мых для общения. При записи алгоритма решения задачи на языке программирова- ния необходимо четко знать правила написания и использования элементарных ин- формационных и языковых единиц. Основой Паскаля, как и любого языка, являет- ся алфавит — конечный набор знаков, состоящий из букв, десятичных и шестна- дцатеричных цифр, специальных символов. В качестве букв в Паскале используются прописные и строчные буквы латин- ского алфавита: ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz и знак подчеркивания (_); в качестве десятичных цифр: 012345678 9. Шестнадцатеричные цифры строятся из десятичных цифр и букв от А до F (или от а до f).
Основные элементы языка Паскаль 47 При написании программ применяются следующие специальные символы: + плюс 9 запятая — минус • точка * звездочка двоеточие / дробная черта и квадратные скобки > больше {} фигурные скобки < меньше $ знак денежной единицы - равно о круглые скобки точка с запятой Л тильда номер @ коммерческое а • апостроф нет обозначения пробел Комбинации специальных символов могут образовывать составные символы: присваивание не равно диапазон значений альтернатива { } меньше или равно больше или равно альтернатива[ ] (••) (* *) В программе эти пары символов нельзя разделять пробелами, если они исполь- зуются как знаки операций отношения или ограничители комментария. Примечание. Русские буквы в программе должны заключаться в апострофы, например ‘Пример текста на русском языке’. Примеры: W — прописная латинская буква; R — прописная латинская буква; w — строчная латинская буква; 9— цифра; $— специальный символ; о — составной символ . 3.2.1. Слова в Паскале Неделимые последовательности знаков алфавита образуют слова, отделенные друг от друга разделителями и несущие определенный смысл в программе. Разде- лителем могут служить пробел, символ конца строки, комментарий. Набор слов, используемый в Паскале, можно разделить на три группы: зарезервированные сло- ва, стандартные идентификаторы и идентификаторы пользователя. Зарезервированные слова являются составной частью языка, имеют фиксиро- ванное начертание и раз и навсегда определенный смысл. Они не могут изменяться программистом. Зарезервированные слова версии языка Паскаль для персональных ЭВМ приведены ниже.
Основные элементы языка Паскаль 49 3.2.2. Формальные методы описания синтаксических конструкций языка программирования Для описания синтаксических конструкций языка программирования в настоя- щее время наиболее распространены два формальных метода. Первый метод ис- пользует форму записи, предложенную Джоном Бэкусом и Питером Науром, когда они описывали синтаксис языка Алгол-60. С тех пор эта форма называется Backus Naur Form или сокращенно BNF. Другой формальный метод, наглядно представляющий синтаксические конст- рукции языка в графическом виде, использует синтаксические диаграммы. Попу- ляризировал синтаксические диаграммы создатель языка Паскаль Н.Вирт, и поэто- му их часто называют синтаксическими диаграммами Вирта. На синтаксических диаграммах использованы два вида четырехугольников — с прямыми и округленными углами (иногда их заменяют кружком или овалом). В прямоугольники заключаются элементы языка, значение которых должно быть определено (так называемые "нетерминальные" символы). В четырехугольниках с округленными углами (или кружках, эллипсах) размещаются так называемые тер- минальные (базовые) символы, или иероглифы языка, значение которых в опреде- лении не нуждается. Направление движения по диаграмме при раскрытии структу- ры понятия, записанного при входе в диаграмму, указывают стрелки. Например: . ♦(базовый символ )-----» нетерминальный символ ------♦ Чтобы получить правильные грамматические конструкции языка, используя синтаксические диаграммы, нужно идти по путям, указанным стрелками, от одного четырехугольника к другому до тех пор, пока не встретится выход. Там, где преду- смотрено более одного направления движения, можно выбрать любое. Если по пу- ти встретилась ссылка к другой синтаксической диаграмме, то надо войти в эту но- вую диаграмму, пройти по ней, выйти из нее и возвратиться на старое место в пер- воначальной диаграмме. Если по пути движения встретилась точка, то это означа- ет, что данный путь характерен только для Турбо Паскаля и является расширением стандарта языка. Представление синтаксических конструкций языка программирования методом BNF или синтаксических диаграмм тождественно. 3.2.3. Идентификаторы Для того чтобы программа решения задачи обладала свойством массовости, следует не употреблять конкретные значения величин, а использовать их обозначе-
50 ГлайаЗ у -а ния для возможности изменения по ходу выполнения программы их значений. Для обозначения программ, а в программе переменных и постоянных величин, различ- ных процедур, функций, объектов используются имена — идентификаторы (identification — установление соответствия объекта некоторому набору симво- лов). Для обозначения заранее определенных разработчиками языка типов данных, констант, процедур и функций служат стандартные идентификаторы, напри- мер: integer, Sin, Cos, Ln, Sqr, Sqrt, Read, Readln, Write, Writein. В этом приме- ре стандартный идентификатор Sin вызывает функцию, вычисляющую, синус за- данного угла, Read, Readln вызывают процедуру, организующую ввод данных, Write, Writein вызывают процедуру, организующую вывод данных. Любой из стан- дартных идентификаторов в отличие от зарезервированных слов можно переопре- делить, но это чаще всего приводит к ошибкам. Поэтому на практике стандартные идентификаторы лучше использовать без каких-либо изменений. Для обозначения меток, констант, переменных, процедур и функций, опреде- ленных самим программистом, применяются идентификаторы пользователя. При этом идентификаторы в программе должны быть уникальными, т. е. в данном блоке программы не может использоваться один идентификатор для обозначения более чем одной переменной или постоянной величины и т.д. Компилятор Турбо Паскаля строго следит за этим, и если это требование не со- блюдается, компиляция прерывается, а на экран выводится сообщение об ошибке "Error 4: Duplicate identifier" и указывается дубликат идентификатора. В идентификатор не могут входить пробелы, специальные символы алфавита. Обратите внимание, что буквы русского алфавита не могут входить в идентифика- тор Турбо Паскаля. Синтаксическая диаграмма понятия "идентификатор" выгля- дит следующим образом: При записи программ следует соблюдать общие правила написания иденти- фикаторов: 1. Идентификатор начинается только с буквы или знака подчеркивания (исклю- чение составляют метки, которые могут начинаться и цифрой, и буквой).
Основные элементы языка Паскаль 51 2. Идентификатор может состоять из букв, цифр и знака подчеркивания (пробег лы, точки и другие специальные символы при написании идентификаторов недо- пустимы). 3. Между двумя идентификаторами должен быть по крайней мере один пробел. 4. Максимальная длина идентификатора 127 символов, но значимы только пер-. вые 63 символа. - . 5. При написании идентификаторов можно использовать как прописные, так и строчные буквы. Компилятор не делает различий между ними, хотя они и имеют различные номера в стандартном коде обмена информацией. На практике рекомен- дуется применять эту особенность для более простого чтения и понимания значе- ний идентификаторов. Так, вместо идентификатора nomerotdela лучше написать NomerOtdela, выделив прописными буквами каждую из двух смысловых частей. Правильно выбранные идентификаторы значительно облегчают чтение и по- нимание программы, а также уменьшают вероятность появления ошибок при моди- фикации программ. Например, значение даты удобнее обозначить идентификато- ром Data, чем просто буквой D или любым другим символом. Примеры: Metkal2 2graph — ошибка, идентификатор начинается с цифры В1оск_56 Nomer.Dorna — ошибка, идентификатор содержит точку Сумма — ошибка, идентификатор содержит символы русского язы- ка. 3.2.4. Константы и переменные Решение задачи на компьютере — это процесс сбора, обработки и передачи информации. Поэтому любая программа имеет смысл, если она обрабатывает ка- кие-либо данные. Как и в других языках программирования, в Паскале данные раз- деляются на константы и переменные. В программе константы и переменные опре- деляются идентификаторами (именами), по которым к ним можно обращаться для получения текущих значений. Константами называются элементы данных, значения которых установлены в описательной части программы и в процессе выполнения программы не изменя- ются. Константы задаются идентификаторами пользователя. Например, если вы ис- пользуете в программе ваше имя, то его лучше всего задать константой, так как имя дается раз и навсегда, и не меняет своего значения.
52 Глава 3 Синтаксическая диаграмма определения константы выглядит следующим обра- зом: Определение константы: Все константы должны быть описаны в специальном разделе, который начина- ется зарезервированным словом const (constant — константа). Формат: const <идентификатор> = Оначение константы>; Например: const MyName = * Петя Иванов'; MyBirthDay = '27 августа 1950 г’; Мах = 1000; Min = 0; Center “ ( Max -j- Min) /2; Num_School = 86; В Паскале имеется ряд констант, к значениям которых можно обращаться без предварительного определения. Их называют зарезервированными константами. Наиболее употребительные из них приведены в табл. 3.1. Таблица 3.1 Зарезервированные константы Идентификатор Тип Значение Описание True boolean True "Истина" False boolean False "Ложь" Maxint integer 32767 Максимальное целое Переменными называют величины, которые могут менять свои значения в процессе выполнения программы. Каждые переменная и константа принадлежат к определенному типу данных. Тип констант автоматически распознается компиля- тором без предварительного описания. Тип переменных должен быть описан перед тем, как с переменными будут вы- полняться какие-либо действия. Этим мы как бы объявляем компьютеру, какие
Основные элементы языка Паскаль 53 ячейки памяти мы собираемся использовать для хранения данных в своей програм- ме. Само название "переменная" подразумевает, что содержимое объявленной об- । ласти памяти будет изменяться в ходе выполнения программы. Переменные описы- ваются в специальном разделе, который начинается зарезервированным словом var (variable — переменная). Формат: var <идентификатор> : <тип>; Пример. var А, В : integer; Summa : real; Имя переменной подобно ящичку, который можно заполнить различными зна- чениями, что нельзя сделать с константой. Синтаксическая диаграмма определения переменных выглядит следующим образом: Кроме констант и переменных существуют так называемые типизированные константы, которые являются как бы промежуточным звеном между переменны- ми и константами. Слово "константа" означает, что данные этого типа описывают- ся в разделе const, а слово "типизированные" указывает, что для них должен указы- ваться и тип, как у переменных. Формат: cons t <идентификатор>:<тип>=<Вначение>; Пример. const VideoSeg : word = $В800; Ocenka : byte=4; Predmet : string='Информатика'; Синтаксическая диаграмма определения типизированных констант записывает- ся таким образом:
54 Глава 3 В прикладном аспекте типизированная константа равнозначна переменной с заранее инициализированным значением, и в программе действия над ней могут производиться так же, как над переменной. Имена констант и переменных должны соответствовать их назначению, в этом случае ваша программа будет абсолютно прозрачна для понимания. 3.3. СТРУКТУРА ПРОГРАММЫ Программа реализует алгоритм решения задачи. В ней программист записыва- ет последовательность действий, выполняемых над определенными данными с по- мощью определенных операций для реализации заданной цели. Основные характе- ристики программы: точность полученного результата, время выполнения и объем требуемой памяти. О соответствии этих показателей решаемой задаче и возможно- стям компьютера должен позаботиться сам программист. В большинстве случаев определяющим требованием является точность. Ограничения по объему памяти и времени выполнения носят менее жесткий характер. Программа на языке Паскаль состоит из строк. Набор текста программы осу- ществляется с помощью встроенного редактора текстов системы программирова- ния Турбо Паскаль или любого другого редактора формата DOS. В первом случае программа может после выхода из редактора (при нажатии клавиши F10) в главное меню компилироваться и выполняться; во втором случае программа должна быть записана в файл на диск и вызываться для компиляции и выполнения в интегриро- ванной среде программирования Турбо Паскаль. Программист, набирая текст программы, имеет право произвольно располагать строки на экране. Строка может начинаться с любой колонки, т. е. величина отсту- па от левой границы экрана для каждой строки устанавливается самим программи- стом с целью получить наиболее удобный для чтения, по его мнению, текст про-
Основные элементы языка Паскаль 55 граммы. Количество операторов в строке произвольно, но если в строке записыва- ется один оператор, такая программа легче читается. Существуют различные схемы написания программ на языке Паскаль, все они отличаются количеством отступов слева в каждой строке и различным ис- пользованием прописных букв. Схема, которая применяется в данном пособии, имеет следующие черты: • зарезервированные слова program, procedure, function пишутся строчны- ми буквами; • имена констант, переменных, процедур, функций начинаются с прописных букв; • операторы записываются только строчными буквами; • логически подчиненные структуры записываются на 1 строку ниже и на 1 — 2 позиции правее по отношению к более старшим. Такая схема, возможно, не является лучшей, но если вы будете придерживать- ся ее, ваши программы будут значительно понятнее вам и вашим коллегам, в них значительно быстрее будут "вылавливаться" ошибки. Максимальный размер программы ограничен. Компилятор позволяет обраба- тывать программы и библиотечные модули, в которых объем данных и генерируе- мый машинный код не превышают 64 Кбайт каждый. Если программа требует большего количества памяти, следует использовать библиотечные модули (.TPU- файлы) или оверлейные структуры. Примечание. Оверлеи — части машинной программы, которые совместно используют общую область оперативной памяти. В один и тот же момент времени в памяти может нахо- диться тот или иной оверлей в зависимости от выполняемой функции. В процессе выполне- ния программы эти части могут замещать друг друга в памяти. Синтаксически программа состоит из необязательного заголовка и блока. 1 заголовок программы Блок может содержать в себе другие блоки. Блок состоит из двух частей: опи- сательной и исполнительной. Первая часть может отсутствовать, без второй блок не имеет смысла. Блок, который не входит ни в какой другой блок, называется гло- бальным. Если в глобальном блоке находятся другие блоки, они называются ло- кальными. Глобальный блок — это основная программа, он должен присутство- вать в любом случае. Локальные блоки — это процедуры и функции, их присутст- вие необязательно. Объекты программы (типы, переменные, константы и т.д.) со- ответственно называются глобальными и локальными. Область действия объек- тов — блок, где они описаны, и все вложенные в него блоки.
56 Глава 3 Блочная структура обеспечивает структуризацию программ на уровне исход- ных текстов. В идеальном случае программа на языке Паскаль состоит из проце- дур и функций» которые вызываются для выполнения из раздела операторов основ- ной программы. Синтаксическая диаграмма блока: Исходя из этого можно записать структуру программы следующим образом: program <имя> (Input,Output); uses <имя1, имя2,...>; label ...; const ...; type ...; var ...; procedure <имя>; Стало процедуры> function <имя>; <тело функции> begin <операторы> end. Рассмотрим структуру программы на примере программы решения задачи вы- числения произведения двух целых чисел, составленной при выполнении упраж- нения во второй главе. program Tutor3; {Заголовок программы) var {Описание раздела переменных) А,В, Proizved : integer; {Переменные А, В,Proizved — целые) begin {Начало программы) Write('Введите значение целого числа А >'); {Вывод запроса на экран) Readln(А); {Ввод значения А с клавиатуры)
Основные элементы языка Паскаль 57 Write('Введите значение целого числа В >’); Readin(В); Proizved := А * В; {Вычисление переменной Proizved} Write('Произведение чисел ’,А,' и ’,В,' = Proizved); {Вывод ответа} end. {Конец программы} В начале программы находится заголовок, состоящий в общем случае из заре- зервированного слова program, имени программы Tutor3 и параметров, с помощью которых программа взаимодействует с операционной системой. Заголовок про- граммы несет чисто смысловую нагрузку и может отсутствовать, однако рекомен- дуется всегда его записывать для быстрого распознавания нужной программы сре- ди листингов других программ. Параметрами программы обычно являются стан- дартные идентификаторы стандартных файлов ввода-вывода Input и Output (в про- граммах на Турбо Паскале их можно не указывать). После заголовка следует программный блок, состоящий в общем случае из семи разделов: • списка имен подключаемых библиотечных модулей (он определяется заре- зервированным словом uses)', • описания меток; • описания констант; • определения типов данных; • описания переменных; • описания процедур и функций; • операторов. Любой раздел, кроме раздела операторов, может отсутствовать. Разделы описа- ний (кроме uses, который всегда расположен после заголовка программы) могут встречаться в программе любое количество раз и следовать в произвольном поряд- ке. Главное, чтобы все описания объектов программы были сделаны до того, как они будут использовайы. 3.3.1. Раздел uses Этот раздел состоит из зарезервированного слова uses и списка имен под- ключаемых стандартных и пользовательских библиотечных модулей. Формат: uses <имя1>,<имя2>,...; Пример. uses Crt, Dos, MyLib;
58 Глава 3 Подробно о структуре и организации библиотечных модулей будет рассказано ниже, 3.3.2. Раздел описания меток Перед любым оператором языка Паскаль можно поставить метку, что позволя- ет выполнить прямой переход на этот оператор с помощью оператора перехода go to из любого места программы. Примечание. Нельзя выполнять переход на оператор в теле цикла, внутрь составного оператора. Метка состоит из имени и следующего за ним двоеточия. Именем может слу- жить идентификатор или цифра. Максимальная длина имени метки ограничена 127 .символами. Перед употреблением метка дЛгжна быть описана. Раздел описа- ния меток начинается зарезервированным словом label (метка), за которым следу- ют имена меток, разделенные запятыми. За последним именем ставится точка с за- пятой. Формат: label <имя,...>; Пример. label Metkal, Metka2, 111, BloklO; После записи метки в разделе операторов следует двоеточие, показывающее компилятору, что идентификатор используется как метка: label Ml, М2; {Описание меток} begin Ml: <оператор> {Использование Ml в разделе операторов} М2: <оператор> {Использование М2 в разделе операторов} end. Если метка описана, но в разделе операторов не используется, то ошибки при этом не возникает, т.е. метки можно описывать и применять по мере расширения программы. 3.3.3. Раздел описания констант В разделе описания констант производится присваивание идентификаторам констант постоянных значений. Раздел начинается зарезервированным словом const, за которым следует ряд выражений, присваивающих идентификаторам по-
Основные элементы языка Паскаль 59 стоянные числовые или строковые значения. Выражения присваивания отделяются друг от друга точкой с запятой. Формат: const <идентификатор> — <вначение>; Пример. const Maxlnd: word = 100; {Типизированная константа} Name = 'Петя'; {Строковая константа} Code = $124; {Константа — шестнадцатеричное значение} Удачное относительно мнемоники именование констант пользователя делает ; программу более читаемой и позволяет быстро вносить корректировку в програм- му при изменении алгоритма. В Турбо Паскале большое число констант определено'стандартно, к ним мож- ' но обращаться без предварительного описания. 3.3.4. Раздел описания типов данных Тип данных может быть либо описан непосредственно в разделе описания не- пременных, либо определяться идентификатором типа. Стандартные типы не требу- ют описания в отличие от типов, образованных пользователем. Строго говоря, син- 1 таксис языка Паскаль не требует обязательного определения идентификатора типа и в последнем случае, так как тип можно задать перечислением в разделе описания , переменных. Выбор описания типа зависит, таким образом, только от программи- . ста и специфики программы. Раздел описания типов данных начинается зарезервированным словом type, за ’ которым следуют одно или несколько определений типов, разделенных точкой с запятой. Формат: type <имя типа> = Оначения типа>; Пример. type LatLetter = ( ' А' . . ' z' ) ; Days = 1..31; Matr = array[1..10] of. integer; Каждое описание задает множество значений и связывает с этим множеством некоторое имя типа. Например, в данном описании тип LatLetter определяет мно- , жество букв латинского алфавита, Days — множество целых чисел от 1 до 31, Matr— массив из 10 целых чисел. Типы данных, разрешенные в языке Паскаль, и их описание рассмотрены в гл. i 4 “Типы данных”. 3.3.5. Раздел описания переменных Каждая встречающаяся в программе переменная должна быть описана. Описа- ’* ние обязательно предшествует использованию переменной. Раздел описания пере-
60 Глава 3 менных начинается зарезервированным словом var (variable — переменная), затем через запятую перечисляются имена переменных и через двоеточие следуют их тип и точка с запятой. Формат: var «^идентификатор, . . . > : <тип>; В рассматриваемом примере программы три переменных А, В и Proizved могут принимать целочисленные значения, описаны следующим образом: var А,В, Proizved : integer; 3.3.6. Раздел описания процедур и функций В этом разделе размещаются тела подпрограмм. Подпрограммой называется программная единица, имеющая имя, по которому она может быть вызвана из дру- гих частей программы. В языке Паскаль роль подпрограмм выполняют процедуры и функции. В общем случае подпрограмма имеет ту же структуру, что и программа. Для описания подпрограмм используются зарезервированные слова procedure и function, которые записываются в начале подпрограммы. Формат процедуры: procedure <имя процедуры> {<параметры>}; <равделы описаний> <равдел операторов> end; Формат функции: function <имя функции> {<параметры>} : <тип результата:*; <разделы описаний> <раздел операторов> end; Процедуры и функции подразделяются на стандартные и определенные поль- зователем. Стандартные процедуры и функции являются частью языка и могут вы- зываться без предварительного описания. Описание процедур и функций пользова- теля обязательно. Более подробно процедуры и функции рассмотрены в гл. 7 "Про- цедуры и функции". 3.3.7. Раздел операторов В программе на языке Паскаль раздел операторов является основным, так как именно в дем с предварительно описанными переменными, константами, зна- чениями функций выполняются действия, позволяющие получить результат, ради которого создавалась программа.
Основные элементы языка Паскаль 61 Раздел операторов начинается зарезервированным словом begin (начало), да- лее следуют операторы языка, отделенные друг от друга точкой с запятой. Завер- шает раздел зарезервированное слово епё.(конец) с точкой. Например: begin {Начало программы} Write('Введите значение целого числа А >'); {Вывод запроса на экран} Readin(А); {Ввод значения А с клавиатуры} Write('Введите значение целого числа В >'); Readln(B); Proizved := А * В; {Вычисление переменной Proizved} Write('Произведение чисел ' ,А,' и ',В,' = ',Proizved); {Вывод ответа} * end. {Конец программы} Операторы выполняются строго последовательно в том порядке, в котором они записаны в тексте программы в соответствии с синтаксисом и правилами пунктуа- ции. Слова begin и end являются аналогом открывающей и закрывающей скобки в обычных арифметических выражениях. Подробно операторы и правила пунктуа- ции, которые необходимо знать для их правильного написания, рассматриваются в гл. 6 “Операторы”. 3.3.8. Комментарии Для лучшего понимания программы в ней записывается пояснительный текст — комментарий. Комментарий можно записать в любом месте программы, где разрешен пробел. Текст комментария ограничен символами {} или (* *) и может содержать любые комбинации латинских и русских букв, цифр и других символов алфавита языка Паскаль. Ограничений на длину комментария нет, он может зани- мать несколько строк. Примеры. {Начало программы} или (*Начало программы*) {Вывод запроса на экран} {Ввод значения А с клавиатуры} {Вычисление произведения двух целых чисел Proizved} {Пример комментария, занимающего несколько строк} В ограничителях (* *) пробелы между скобкой и звездочкой запрещены. В тек- сте не должны находиться знаки ограничителей, с которых комментарий начинает- ся. Например, текст комментария {Пример {1} задания {4}} вызовет ошибку при компиляции. Однако ограничители { } можно вложить в (* *) и наоборот: (*При- мер{ 1 {задания {4} *) или { Пример (* 1 *) задания (* 4 *) }.
62 Глава 3 Комментарий игнорируется компилятором и поэтому никакого влияния на про- грамму не оказывает. По месту положения в программе комментарии можно под- разделить на четыре класса: объясняющие назначение программы, поясняющие смысл идентификаторов переменных и констант, описывающие логически обособ- ленные части программы, объясняющие трудно понимаемые элементы алгоритма. В удачно прокомментированной программе легко найти ошибку, проанализировав различие между замыслом автора (в комментариях) и реализацией (в тексте про- граммы). Ограничители { } и (* *) удобно использовать при отладке программ. В про- цессе отладки часто требуется временно исключить выполнение какой-либо части программы. Конечно, этого можно добиться, уничтожив временно ненужные опе- раторы или обойдя их с помощью оператора go Ц. Однако оба этих способа непри- емлемы по ряду совершенно понятных причин: повторный набор вновь понадо- бившихся операторов, путаница с операторами go to и т.д. Гораздо удобнее просто заключить временно ненужную часть программы в {} или (* *), которая будет вос- приниматься компилятором как комментарий. Например: begin {Начало программы} Write('Введите значение целого числа А >'); {Вывод запроса на экран} Readln(А); {Ввод значения А с клавиатуры} Write('Введите значение целого числа В >'); Readln(В); Proizved := А * В; {Вычисление переменной Proizved} {Временно невыполняемая часть программы Write('Произведение чисел ',А,' и ',В,' = Proizved); Вывод ответа} end. {Конец программы} При необходимости { } или (* *) можно убрать, и программа будет выполнять- ся в полном объеме. 3.3.9. Директивы компилятора и управляющие символы Текст программы может содержать директивы компилятора, которые исполь- зуются программистом для управления режимами компиляции. Примечание. Директива — сообщение в повелительной форме (команда), вводимая оператором и содержащая указание о том, какие необходимо выполнить действия. Дирек- тива компилятора — компонент программы, управляющий последующей компиляцией программы. Директивы, как и комментарии, заключаются в фигурные скобки, но они име- ют отличительный признак $, позволяющий компилятору интерпретировать их со- ответствующим образом.
Основные элементы языка Паскаль 63 Пример. {$ R-J {$ V+, К—, R—} В рассматриваемой версии языка Паскаль директивы компилятора имеют боль- . шое значение как на стадии отладки, так и при выполнении программы. По умолча- . нию, директивы находятся в состоянии, гарантирующем минимальный объем объ- ектного модуля и минимум времени компиляции. В программе могут встречаться также управляющие символы и "А". Знак . и следующее за ним целочисленное значение в диапазоне 0..255 обозначают символ кодовой таблицы ПЭВМ, имеющий соответствующее десятичное значение. Пример. , #23 — символ, имеющий десятичный код 23 * #$08 — символ, имеющий шестнадцатеричный код $08 Знак "л" и следующий за ним какой-либо другой символ трактуются компиля- тором как управляющий символ, т. е. "Л" указывает, что далее следует один управ- ляющий символ. Управляющие символы могут группироваться в строке без разделителей между ними. Допустимо использование управляющих символов вместе со строковыми данными. Пример. AI#25AYAD — группа управляющих символов Writein(’Обнаружена ошибка в тексте!',AG,AG); Write(#234, #235, #236); 3.3.10. Библиотечные модули пользователя Понятие библиотечного модуля является одним из основных в идеологии про- граммных систем на языке Турбо Паскаль. Именно они служат средством создания библиотек подпрограмм (процедур и функций). Библиотечный модуль — это ре- зультат компиляции в режиме Compile с установленной директивой Destination = Disk одной или нескольких процедур и функций. Модуль имеет имя, при упомина- нии которого в разделе uses любой программы можно получить доступ к каждой из находящихся в нем процедур или функций. Создание библиотечного модуля требует определенной организации с приме- нением зарезервированных слов unit, interface, implementation, begin, end. Система сама определяет структуру компилируемого файла и создает соответственно .TPU- файл (при обнаружении unit и т.д.) или .ЕХЕ-файл (при отсутствии unit, implementation и т.д.). В первом случае формируется библиотечный модуль, во вто- ром — готовый к выполнению по вызову из, DOS загрузочный модуль. Подробно структуру библиотечного модуля мы рассмотрим позднее.
64 Глава 3 3.4. РЕКОМЕНДАЦИИ ПО СТИЛЮ ПРОГРАММИРОВАНИЯ Накопленный опыт программирования привел к формированию следующих ре- комендаций по составлению наглядных и легко читаемых программ. 1. Стандартизация стиля программирования заключается в том, что необходи- мо всегда придерживаться одного способа программирования, записи программы. 2. С целью рационального размещения текста не следует операторы програм- мы писать сплошным текстом. Для четкого выявления вложенности управляющих структур требуется особым образом располагать операторы в тексте, так что служебные слова, которыми на- чинается и заканчивается тот или иной оператор, записываются на одной вертика- ли, а все вложенные в него операторы записываются с некоторым отступом вправо. При записи конструкций языка более глубоких уровней вложенности следует сдви- гать их от начала строки вправо. Каждое описание и каждый оператор следует пи- сать с новой строки. Продолжение описаний и операторов на новые строки надо сдвигать вправо. Следует избегать длинных строк. 3. Рекомендуется любую программу сопровождать комментариями, поясняю- щими назначение всей программы и отдельных ее блоков, процедур, функций. 4. Имена для объектов программы надо выбирать так, чтобы они наилучшим образом соответствовали этим объектам, отражали их назначение. 5. Списки идентификаторов в блоках описания следует упорядочивать —это облегчает поиск в них нужных элементов. 6. Программирование сверху вниз. В процессе разработки алгоритма и про- граммы следует начинать с самой общей модели решения, постепенно уточняя ее до уровня отдельного блока и затем детально прорабатывая каждый блок. Примечание. Иллюстрацией соблюдения данных правил могут послужить примеры программ в директории EXAMPLE, поставляемые фирмой Borland. Упражнение 1. Загрузите интегрированную среду программирования, считайте с диска программу ТгйогЗ. Измените ее таким образом, чтобы она выполняла целочисленное деле- ние числа А на В и вычисляла остаток от деления А на В. Для этого в разделе описания пе- ременных нужно ввести переменные Rezult и Ostatok целого типа: var А, В, Rezult, Ostatok : Integer; {Переменные А, В, Rezult-, Ostatok — целые} Операцию вычисления значений величин Rezult и Ostatok можно записать следующи- ми операторами: Rezult := A div В; Ostatok := A mod В; Вывод результатов работы программы на экран можно запрограммировать следую- щим образом:
Основные элементы языка Паскаль 65 Write('Результат целочисленного деления чисел ' ,А,' и ',В,’ =', Rezult); Write('Остаток от деления чисел ' ,А,’ и ',В,* = ',Ostatok); Откомпилируйте программу и проверьте ее действие, затем запишите отлаженный ва- риант на диск под именем Tutor4. Контрольные вопросы и задания 1. Укажите буквы, символы, составные символы: Л, Y, о, +, ♦, R, к, $, !, ы 2. Что в списке можно рассматривать как идентификаторы: FIO, ФИО, 22222, X, У, >=, &, $, Summa, _Rezult 3. Укажите идентификаторы, которые проще воспринимак^ся при чтении, объясните причину: klassl; Klassl; summadoxoda; SummaDoxoda; nomerdoma; Nomer_Doma. 4. Сколько в следующем списке зарезервированных слов: X, Program, Y, Summa, МуМопеу, Произведение, Vova, begin, end, if, repeat, Read? 5. В каких случаях надо использовать переменные: 1) если в программе используется какое-то число? 2) если в вычислениях какой-то операнд постоянно меняет свое значение? 3) если операнд в выражении хотя бы один раз меняет значение? 6. Какие заголовки программ правильны: 1) program Zarplata? 2) program Сумма? 3) program Summa Nalogov? 4) программа TeachKurs? 5) program 12Kurs2? 6) program Summa_Elem6ntov? 7. Какая структура программы правильна: l)program МуProgram; 2)program MyFirst; begin begin Writein('Привет'); X:=Y+100; end. end. 8. Какой из перечисленных разделов обязателен в программе: 1) раздел var? 2) раздел const? 3—116
66 Глава 3 3) раздел type? 4) раздел begin.. end.? 5) раздел label? 9. Какие из комментариев неправильны: 1) { Программа вычисляет логарифм введенного числа }; 2) (* Это тоже комментарий *); 3) {{ Комментарий в комментарии }}; 4) (* { И это комментарий в комментарии} *); 5) { (* Еще один вариант *) }; 6) (*(* Самый последний вариант *)*)Л 10. Есть ли причины к невыполнению следующей программы: program Test; * begin end. 11. Для чего используется слово uses ? 1) такого слова нет в языке Турбо Паскаль; 2) это какой-то пользовательский идентификатор; 3) с его помощью подключают стандартные библиотеки; 4) это стандартная константа, равная 3,14; 5) это логическая операция. 12. В разделе процедур и функций описываются только стандартные процедуры ? 1) да, только стандартные; 2) нет, только пользовательские; 3) и стандартные, и пользовательские; 4) такого раздела вообще не может быть в программе. 13. Наберите и откомпилируйте простейшую программу: program Tutor; begin Writein(5*6) ; Writein(* Привет'); end. Каков будет результат ее выполнения? Измените программу, чтобы результат равнялся 35. 14. Где ошибки (их три) в следующей программе? program Ошибки1; begin Summa:“6+8; end; Исправьте программу и добейтесь компиляции без ошибок. 15. Где ошибки (их три) в следующей программе?
Основные элементы языка Паскаль 67 program Ошибки2; {{ Программа с ошибками }} begin var X: integer; Х:=5 * 3; Write(X) ; end. Исправьте программу и добейтесь компиляции без ошибок. 16. Где ошибка в следующей программе? program MyError; uses; {(* .Программа с ошибкой *) } * begin end. Исправьте программу и добейтесь компиляции без ошибок. 17. Напишите самую короткую программу. 18. Где ошибка в следующей программе? program Kvadr; begin Writein('Введите значение X'); Writein('X в квадрате=*,Х*Х);- end. var X:integer; Исправьте программу и добейтесь компиляции без ошибок. 19. Где ошибка в следующей программе? program Summa; var X,Y,Сумма:integer; begin Writein('Введите Значение X’); Readln (X) ; Writein('Введите Значение Y'); Readln(Y); Сумма:=X+Y; Writein(Сумма); end. Исправьте программу и добейтесь компиляции без ошибок. 20. Составьте программу вычисления площади прямоугольника по введенным в диало-. ге двум сторонам. Запишите текст программы на диск под именем tutorS.pas, откомпили- руйте и проверьте ее действие.
68 Глава 3 21. Составьте программу вычисления длины окружности и площади круга по указанно- му радиусу. 22. Составьте программу вычисления длин высот треугольника, у которого длины сто- рон А,В,С. 23. Составьте программу вычисления длин медиан треугольника, у которого длины сто- рон А,В,С. 24. Составьте программу вычисления величины работы, совершенной при равномерном подъеме груза массой М кг нк высоту Н м. Ускорение свободного падения описать как кон- станту G=9,81. / 25. Составьте программу вычисления давления столба жидкости плотностью R высотой Н на дно сосуда. 26. Составьте программу вычисления силы давления,^действующей на пол со стороны стола массой М, если суммарная площадь опоры ножек стола 100 см2. 27. Составьте программу вычисления выталкивающей силы, действующей на тело объ- емом V, наполовину погруженное в жидкость плотностью R. 28. Составьте программу вычисления количества теплоты, полученного при сгорании М г керосина, если его теплота сгорания q. 29. Составьте программу вычисления количества теплоты, затраченного на нагревание тела плотностью R, объемом V на Т градусов Цельсия, если его удельная теплоемкость С. 30. Составьте программу вычисления величины силы тока на участке электрической цепи сопротивлением R Ом при напряжении U В. 31. Составьте программу вычисления напряжения на каждом из последовательно со- единенных участков электрической цепи сопротивлением Rl, R2, R3 Ом, если сила тока при напряжении U B составляет 1 А. 32. Составьте программу вычисления значения силы тока I на участке, состоящем из двух параллельно соединенных резисторов сопротивлением R1 и R2, если напряжение на концах этого участка равно U. 33. Составьте программу, определяющую плотность тела, объем которого равен V, а масса М.. 34. Составьте программу, определяющую количество теплоты Q, требуемое для нагре- вания V л жидкости, взятой при температуре Т1, до температуры кипения Т2, если извест- на удельная теплоемкость жидкости q.
Глава 4. ТИПЫ ДАННЫХ 4.1. ОБЩИЕ СВЕДЕНИЯ * При решении задач выполняется обработка информации различного характера. Это могут быть целые и дробные величины, строки и др. Соответственно констан- ты и переменные должны быть описаны какцелые, дробные, строковые и т. д. Для описания множества допустимых значений величины и совокупности опе- раций, в которых может участвовать данная величина, используется указание ее ти- па данных. Тип данных (data type) — множество величин, объединенных опреде- ленной совокупностью допустимых операций. Каждый тип данных имеет свой диапазон значений и специальное зарезервиро- ванное слово для описания. Например, значения 1 и 2 относятся к целочисленному типу, их можно складывать, умножать и выполнять над ними другие арифметиче- ские операции. В языке Паскаль для описания типа в общем случае используется зарезервиро- ванное слово type. Формат: 1 type <Имя типа> = Оначения типа>; , Синтаксическая диаграмма описания типов может быть представлена таким 'образом: Определение типа -------> идентификатор -----И = )-----• тип ---------► Все типы данных можно разделить на две группы: скалярные и структуриро- ванные (составные). Скалярные типы, в свою очередь, делятся на стандартные и пользовательские.
70 Глава 4 Стандартные типы предлагаются пользователям разработчиками системы Тур- бо Паскаль. К ним относятся целочисленные, вещественные, литерные, булевские типы данных и указатели. Пользовательские типы разрабатываются пользователями системы программи- рования Турбо Паскаль. 4.2. ПЕРЕЧЕНЬ ТИПОВ ДАННЫХ В ТУРБО ПАСКАЛЕ Перечень типов данных в языке Турбо Паскаль можно представить в виде сле- дующей схемы: 1. Простые типы (скалярные типы). * Порядковые типы. Целые типы: byte, shortint, integer, word, longint. Логический тип boolean. Символьный тип char. Перечисляемый тип. Интервальный тип (диапазон). Вещественные типы: real, single, double, extended, comp. Ссылочный тип. 2. Структурированные типы. Строковый (string). Регулярный (array). Комбинированный (record). Множественный (set). Файловый (file). 3. Процедурные типы.
Типы данных 71 Данные целочисленных типов могут быть представлены как в десятичной, так и в шестнадцатеричной системе. Если число представлено в шестнадцатеричной системе, перед ним без пробела записывается знак $. Диапазон изменений шестна- дцатеричных чисел от $0000 до $FFFF. В десятичной системе числа могут записываться двумя способами: с фиксиро- ванной и с плавающей точкой. Вещественные десятичные числа с фиксированной точкой записываются по обычным правилам арифметики. Целая часть от дробной отделяется десятичной точкой. Если десятичная то^ка отсутствует, число считается целым. Перед числом может находиться знак "+" или "—". Если знак отсутствует, по умолчанию число считается положительным. * Примеры: 125 . — целое десятичное число S1FF — шестнадцатеричное число 124.674 — вещественное число — 12.9 — отрицательное вещественное число Вещественные десятичные числа в форме с плавающей точкой представлены в экспоненциальном виде: шЕ+р, где m — мантисса (целое или дробное число с деся- тичной точкой), Е означает десять в степени, р — порядок (целое число). Пример. 5.18Е+02 = 5.18 * 102= 518 10Е — 03 = 10* Ю 3 = 0.01 Пользовательские типы — перечисляемый и интервальный — разрабатывают- ся самим программистом. Структурированные типы в своей основе имеют один или несколько скалярных типов данных. К структурированным типам относятся строки, массивы, множества, записи, файлы и данные совершенно новой природы: процедурного типа и типа object. Двум последним типам трудно поставить в соответствие данные в обычном , понимании этого слова. Разработчики фирмы Borland назвали их процедурными типами и объектами, что точно соответствует их базовым признакам. Понимание работы с этими типами требует наличия определенного опыта и навыков програм- мирования. 4.3. СКАЛЯРНЫЕ ТИПЫ ДАННЫХ К скалярным (scalar — простые) типам данных относят типы данных таких величин, значения которых не содержат составных частей.
72 Глава 4 4.3.1. Целочисленные типы данных Целочисленные типы данных представляют собой значения, которые могут ис- пользоваться в арифметических выражениях и занимать в памяти от 1 до 4 байт (табл. 4.1). Таблица 4.1 Целочисленные типы данных Тип Диапазон Требуемая память (байт) byte 0..255 1 shorint — 128.. 127 1 integer — 32768.. 32767 2 1 word 0..65535 2 longint — 2147483648..2147483647 4 Значения целых типов могут изображаться в программе двумя способами: в де- сятичном виде (традиционно, в виде последовательности цифр) и в шестнадцате- ричном виде (в этом случае вначале числа ставится знак $, а цифры старше 9 обо- значаются латинскими буквами от А до F). Синтаксическая диаграмма для целых чисел выглядит так: Пример, var XI, Х2: byte; Yl: word; Над данными целого типа определены следующие арифметические операции: +, —, *, /, div, mod. Результат выполнения этих операций над целыми операндами получается также целого типа. Над данными целого типа определены следующие операции отношения: =, <>, <, >, <=, =>, вырабатывающие результат логического типа. Для целых чисел определены следующие стандартные функции: odd(x) — возвращает результат логического типа: для четного аргумента — false; для нечетного — true; succ(x) — возвращает следующее целое число (х+1); pred(x) — возвращает предьщущее целое число (х — 1); ord(x) — возвращает аргумент х;
Типы данных 73 Продолжение abs(x) — возвращает модуль х; chr(x) — возвращает символ, ASCII-код которого равен х; sqr(x) — возвращает квадрат числа х; sqrt(x) — возвращает значение корня квадратного из х; ехр(х) — возвращает е в степени х (экспоненту), результат вещественного типа; sin(x) — возвращает синус х, результат вещественного типа; cos(x) — возвращает косинус х, результат вещественного типа; 1п(х) — возвращает натуральный логарифм х, результат вещественного типа; arctan(x) — возвращает арктангенс х, результат вещественного типа. Для целых чисел определены следующие стандартные процедуры: dec(x,i) — уменьшает значение х на i, если i не задано, то на 1; inc(x,i) — увеличивает значение х на i, если i не задано, то на 1. 4.3.2. Вещественные типы данных Вещественные типы данных представляют собой вещественные значения, ко- торые используются в арифметических выражениях и занимают в памяти от 4 до 6 байт. Паскаль допускает представление вещественных значений и с плавающей, и с фиксированной точкой (табл. 4.2). Таблица 4.2 Вещественные типы данных Тип Диапазон Мантисса Требуемая память (байт) real 2.9*10E — 39..1.7*10E38 11 — 12 6 single 1.5*10E — 45..3.4*10E38 7 — 8 4 double 5.0* 10E — 324.. 1.7*10E308 15—16 8 extended 1.9*10E —4951..1.1*10E4932 19 — 20 10 comp — 2E+63+1..2E+63 —1 10 — 20 8 Примечание. Все вещественные типы, кроме real, могут использоваться в программе, если в компьютере имеется математический сопроцессор Intel 8087/80287 или программно эмулируются (эмуляция — имитация функционирования математического сопроцессора в его отсутствие программными средствами) аппаратные операции с вещественными типами. Вещественные значения могут изображаться в форме с фиксированной точкой, например 7.32,456.721 или 0.015, а также в форме с плавающей точкой, т. е. парой чисел вида <мантисса>Е<порядок>. Числа из предыдущего примера в форме с плавающей точкой будут записаны так: 7.32Е+00, 4.56721Е+02, 1.5Е —02. Синтаксические диаграммы для записи вещественного числа будут выглядеть так: i 1 § fl jl
74 Глава 4 Над данными вещественного типа определены следующие арифметические операции: +, —, *, /. Результат выполнения этих операций получается также веще- ственного типа. Над данными вещественного типа определены следующие операции отноше- ния: =, о, <, >, <=, =>, вырабатывающие результат логического типа. Для вещественных чисел определены следующие стандартные функции: abs(x) — возвращает модуль х, результат вещественного типа; chr(x) — возвращает символ, ASCII-код которого равен х; sqr(x) — возвращает квадрат числа х, результат вещественного типа; sqrt(x) — возвращает значение корня квадратного из х, результат вещественного типа; ехр(х) — возвращает е в степени х (экспоненту), результат вещественного типа; sin(x) — возвращает синус х, результат вещественного типа; cos(x) — возвращает косинус х, результат вещественного типа; 1п(х) — возвращает натуральный логарифм х, результат вещественного типа; arctan(x) — возвращает арктангенс х, результат вещественного типа; trunc(x) — преобразует вещественный аргумент х в целое число путем отбрасывания дробной части; round(x) — преобразует вещественный аргумент х в целое число путем округления до ближайше- го целого. Выражение, составленное из переменного целого и вещественного типа, имеет вещественный тип. Допускается присваивание переменной вещественного типа значения выражения целого типа, но не наоборот. Примечание. Эффективное использование типов single, double, extended, comp возмож- но только при наличии сопроцессора 8087 при включенной директиве {SN+}. По умолча- нию, она находится в выключенном состоянии {$N —}. Пример. var Summa: single; Rootl, Root2: double;
Типы данных 75 Упражнение 1. Изменить программу Tutor3 так, чтобы в результате ее выполнения вы- числялось и выводилось на экран значение частного двух целых чисел. Так как результат де- ления будет вещественного типа, то в разделе описания переменных данной программы должно быть добавлено его описание. Например, если идентификатор результата деления будет Ratio, то раздел описания переменных в программе будет выглядеть следующим об- разом: var А,В : Integer; Ratio : real; Определение частного чисел А и В запишется операцией вещественного деления: Ratio := А / В; а вывод на экран видеомонитора результата можно задать следующим образом: Writein{'Частное двух чисел равно Ratio); * Загрузите интегрированную среду программирования, считайте программу Tutor3 с диска, отредактируйте ее й проверьте действие. При проверке действия программы попробуйте задать следующие значения переменным: А=33000, В=33. Обратите внимание на то, что в результате вычислений получается не 1000, как вы ожидали, а — 9.8593939394Е+02, т. е. программа неправильно вычисляет ре- зультат арифметической операции. Причина ошибки в том, что мы, указав в разде- ле описания переменных для величин А и В тип integer, зарезервировали в памяти место только для хранения целых чисел, принимающих значения в интервале [ — 32768 .. 32767 ], а задали величине А значение 33000. Внесите изменения в программу, указав в разделе описания для величин А, В тип word или longint, проверьте действие программы на примере больших значе- ний чисел А, В. Запишите программу на диск под именем Tutor4. - Этот пример наглядно демонстрирует необходимость правильного описания типов величин, обрабатываемых в программе. 4.3.3. Литерный (символьный) тип Литерный (символьный) тип char определяется множеством значений кодовой таблицы ПЭВМ. Каждому символу приписывается целое число в диапазоне от 0 до 255. Для кодировки используется код ASCH. Таблица кодов ASCII приведена в приложении А. Для размещения в памяти переменной.литерного типа требуется один байт. Пример, var Ch: char; Letter, Symbol: char;
76 Глава 4 В программе значения переменных и констант типа char должны быть заклю- чены в апострофы. Например, 'А' обозначает букву А,'' — пробел, — точку с за- пятой. Над данными символьного типа определены следующие операции отношения: =, о, <, >, <=, =>, вырабатывающие результат логического типа. Для данных символьного типа определены следующие стандартные функции: chr(x) — преобразует выражение х типа byte в символ и возвращает значение символа; ord(ch) — преобразует символ ch в его код типа byte и возвращает значение кода; pred(ch) — возвращает предыдущий символ; succ(ch) — возвращает следующий символ. Примеры: ord(':•) = 58 ord('А’) = 65 chr(128) = Б pred( ' Б' ) = А зиссГГ') = Д 4.3.4. Булевский тип Булевским типом называют тип данных, представляемый двумя значениями: True (истина) и False (ложь). Он широко применяется в логических выражениях и выражениях отношения. При описании величин этого типа указывают слово boolean. Для размещения в памяти переменной булевского типа требуется 1 байт. Пример. var Flag, Rezult: boolean; 4.3.5. Пользовательские типы Кроме стандартных типов данных Паскаль поддерживает скалярные типы, оп- ределенные самим пользователем. К ним относятся перечисляемый и интерваль- ный типы. Данные этих типов занимают в памяти один байт, поэтому скалярные пользо- вательские типы не могут содержать более 256 элементов. Их применение обеспе- чивает семантический контроль вводимых данных, значительно улучшает нагляд- ность программы, делает более легким поиск ошибок и экономит память. 4.3.6. Перечисляемый тип Перечисляемый тип (enumerated type) — тип данных, заданных списком при- надлежащих ему значений.
Типы данных 77 Объявление перечисляемого типа описывает множество идентификаторов, ко- , торые являются возможными значениями перечисляемого типа. Идентификаторы в описании типа представляют собой константы. Отдельные значения указываются через запятую, а весь список заключается в круглые скобки. Первая константа имеет порядковый номер нуль, вторая — 1 и т.д. Формат: type <имя типа> = (<3начение1, значение2,...,значениеп>); var Идентификатор, . . .> : <имя типа>; Синтаксическая диаграмма для перечисляемых типов имеет следующий вид: Пример. type Gaz = (Ge, С, О, N); Metall = (Na, К, Li, Си, Zn); var Gl, G2, G3 : Gaz; Metl, Met2 : Metall; Season: (Winter, Spring, Summer, Autumn); В данном примере приведены два явно описанных типа данных пользователя — Gaz и Metall. Определены их значения — обозначения некоторых газов и метал- лов периодической таблицы Д. И. Менделеева. Переменные Gl, G2, G3 и Metl, Met2 могут принимать только одно из перечисленных значений. Попытка присво- ить им любое другое значение вызовет программное прерывание. Третий тип пере- числения анонимный (не имеет имени) и задается перечислением значений в разде- ле var. Season является переменной этого типа и может принимать значения Winter, Spring, Summer и Autumn. Таким образом может быть задан любой тип, но это не всегда приемлемо, так как первый способ более понятен и больше соответствует характеру языка Паскаль. При этом имена внутри круглых скобок являются кон- стантами соответствующего типа перечисления и подчиняются обычным правилам для констант. Выражения и константы перечисляемого типа допустимы для ис- пользования в операторе CASE. Операции отношения и логические операции до- пустимы для значений перечисления одного и того же типа. Упорядочение осуще- ствляется по номеру элемента в описании типа. Например, будет истинно выраже- ние Winter < Spring, так как Spring имеет больший номер по порядку в описании типа, чем Winter. <
78 Глава 4 В отличие от данных других типов Паскаль не поддерживает операции ввода- вывода значений пользовательского перечисляемого типа. При необходимости про- граммист сам должен организовать ввод-вывод таких данных. Для работы с данными перечисляемого типа в языке Паскаль предназначены стандартные подпрограммы Succ, Pred, Ord. Описанные ранее переменные булевского типа можно представить и как пере- числяемый тип, объявленный следующим образом: type Boolean — (False, True); Поэтому для значений False и True справедливы результаты вычисления выра-. жений: False < True Ord(False) = О Ord (True) = 1 Succ (False)* = True Pred(True) = False 4.3.7. Интервальный тип (диапазон) - Интервальный тип позволяет задавать две константы, определяющие грани- цы диапазона значений для данной переменной. Компилятор при каждой операции с переменной интервального типа генерирует подпрограммы проверки, определяю- щие, остается ли значение переменной внутри установленного для нее диапазона. Обе константы должны принадлежать одному из стандартных типов (напом- ним, что тип real здесь недопустим). Значение первой константы должно быть обя- зательно меньше значения второй. Формат: type <имя типа> — <константа1> . . <константа2>; var Сидентификатор,...> : <имя типа>; Синтаксическая диаграмма для интервальных типов выглядит следующим об- разом: Ф константа константа Пример. type Days =1 .. 31; var RabDay, BolnDay : Days;
Типы данных 79 В этом примере переменные RabDay и BolnDay имеют тип Days и могут прини- мать любые значения из диапазона 1..31. Выход из диапазона вызывает программ- ное прерывание. Рациональнее определить интервальный тип более универсальным способом, задав границы диапазона не значениями констант, а их именами: const Min = 1; Max = 31; type Days = Min .. Max; var RabDay, BolnDay : Days; * 4.4. СТРУКТУРИРОВАННЫЕ ТИПЫ ДАННЫХ Структурированные типы данных определяют упорядоченную совокупность скалярных переменных и характеризуются типом своих компонентов. В языке Пас- каль допускаются следующие структурированные типы данных: строки, массивы, множества, записи, файлы, указатели, процедурные типы и объекты. Все они тре- буют отдельного рассмотрения и подробно 'изложены в третьей части данной кни- ги. 4.5. ТОЖДЕСТВЕННОСТЬ И СОВМЕСТИМОСТЬ ТИПОВ Для того чтобы в результате выполнения программы не получилось путаницы с величинами различного типа' например, когда цена корзины с продуктами равна расстоянию от дома до магазина, умноженному на количество этажей дома, в кото- ром вы живете, от вас как от программиста требуется знать и правильно применять понятия тождественности и совместимости типов величин, участвующих в опера- циях — операндов. Два типа являются-тождественными, если они описаны вместе или если их определения используют один и тот же идентификатор типа. Пример. type Ml, М2 = array[1..10] of byte; {Ml, М2 — тождественные типы) S = set of byte; F = set of integer; {S, F — нетождественные типы) или var A, В, Proizved : integer; Тождественность типов требуется только для переменных фактических и фор- мальных параметров при вызове процедур и функций. Совместимость типов игра- ет важнейшую роль в выражениях и операциях сравнения и в операторах присваи- вания.
80 Глава 4 В операциях сравнения два типа являются совместимыми, если соблюдается хотя бы одно из следующих условий: • оба типа являются одинаковыми; • оба типа являются вещественными типами; • оба типа являются целочисленными; • один тип является поддиапазоном другого; • оба типа являются поддиапазонами одного и того же основного типа; • оба типа являются множественными типами с совместимыми базовыми типа- ми; • оба типа являются строковыми типами с одинаковым числом компонентов; • один тип является строковым, а другой тип — строковым или символьным типом; • один тип является указателем, а другой — любым типом указателей. Пример. 'а' > Ъ' {Допустимо, так как оба значения относятся к типу char}; 'а' > 5 {Ошибка, так как сравниваемые значения имеют разные типы}. В операциях присваивания два типа являются совместимыми, если соблюдает- ся хотя бы одно из следующих условий: • оба типа тождественны, и ни один из них не является файловым или струк- турным типом, содержащим компоненты с файловым типом на одном из своих уровней; • оба типа являются совместимыми скалярными типами, и значения второго типа попадают в Диапазон возможных значений первого; • оба типа относятся к вещественным типам, и значения второго типа попада- ют в диапазон возможных значений первого; • первый тип является вещественным, а второй — целочисленным; • оба типа являются строковыми; • первый тип является строковым, а второй — литерным; • оба типа относятся к совместимым множественным типам, и все члены значе- ния второго типа попадают в диапазон возможных значений первого типа; • оба типа относятся к совместимым типам "указатель". Пример. var А, В: integer; С: real;
Типы данных g 1 I А: = В; {Правильно} К С:= В; {Правильно} Е А:= С; {Ошибка} | 4.6. ВЫРАЖЕНИЯ, ОПЕРАЦИИ, ОПЕРАНДЫ I Конструкция языка, задающая порядок выполнения действий над элементами I данных, называется выражением. Выражение состоит из операндов (operand — I элемент данных, участвующий в операции), — величин и выражений, над которы- । ми производится операция (константы и переменные всех типов, обращения к I функциям); круглых скобок и знаков операций. Операции определяют действия, Е которые надо выполнить над операндами. Например, в выражении (X + Y — 10) I X, Y и 10 — операнды; а "+", " — " — знаки операций сложения и вычитания. I В простейшем случае выражение может состоять из одной переменной или | константы. Круглые скобки ставятся так же, как и в обычных арифметических вы- | ражениях для управления ассоциативностью и порядком выполнения операций. I Операции в языке Паскаль делятся на арифметические, отношения, логические I (булевские), операцию @, строковые и др. Выражения соответственно называются I арифметическими, отношения, булевскими, строковыми и т.д. в зависимости от то- [ го, какого типа операнды и операции в них используются. Три первые группы опе- [ раций описаны в данном подразделе, остальные будут рассмотрены при изучении | соответствующих типов данных. [ Тип значения, вычисляемого с помощью выражения, определяется типом его । операндов и знаками выполняемых над ними операций. Операции могут быть унарными и бинарными. В первом случае операция от- [ носится к одному операнду и всегда записывается перед ним, во втором — опера- Е ция выражает отношение между двумя операндами и записывается между ними. [ Например, — А — унарная операция, X+Y — бинарная. 4.6.1. Арифметические выражения и операции Арифметическим называется выражение, составленное из операндов арифме- тического типа и использующее только знаки арифметических операций и круглые скобки. Порядок вычисления выражения определяется скобками и старшинством операций. Арифметическое выражение порождает целое или действительное (веществен- ное) значение. Наиболее простыми формами арифметических выражений являют- ся: целая или действительная константа без знака;
82 Глава 4 • целая или действительная переменная; • элемент массива целого или действительного типа; • функция, принимающая целое или действительное значение. Значение переменной или элемента массива должно быть определено до их появления в арифметическом выражении. Другие арифметические выражения со- ставляются из вышеперечисленных простых форм путем применения круглых ско- бок и арифметических операций. Арифметические операции выполняют арифметические действия в выражени- ях над значениями операндов целочисленных и вещественных типов. Арифметиче- ские операции языка Паскаль представлены в табл. 4.3. * Таблица 4.3 Арифметические операции Операция Действие Типы операндов Тип результата Бинарные + Сложение Целый Целый Вещественный Вещественный — Вычитание Целый Целый Вещественный Вещественный * Умножение Целый Целый Вещественный Вещественный / Деление Целый Вещественный Вещественный Вещественный DIV Целочисленное деление Целый Целый MOD Остаток от деления Целый Целый AND Арифметическое И Целый Целый SHL Сдвиг влево Целый Целый SHR Сдвиг вправо Целый Целый OR Арифметическое ИЛИ Целый Целый XOR Исключающая дизъюнкция Целый Целый Унарные + Сохранение знака Целый Целый Вещественный Вещественный — Отрицание знака Целый Целый Вещественный Вещественный NOT Арифметическое отрицание Целый Целый Операции сложения (+), вычитания (—), умножения (*) и деления (/) вы- полняются так же, как и в обычных арифметических выражениях.
Типы данных 83 Целочисленное деление (DIV) отличается от обычной операции деления тем, . что вычисляет целую часть частного, дробная часть отбрасывается. Перед выполне- : нием операции оба операнда округляются до целых значений. Результат целочис- ленного деления всегда равен нулю, если делимое меньше делителя. : Например: Выражение Результат 11 DIV 5 2 10 DIV 3 3 2 DIV 3 0 Деление по модулю (MOD) вычисляет остаток, полученный при выполнении ; целочисленного деления. Например: * Выражение Результат 10 MOD 5 0 11 MOD 5 1 10 MOD 3 1 14 MOD 5 4 Арифметическое И (AND) производит логическое умножение операндов в соответствии со следующей таблицей истинности: 1 AND 1=1 1 AND 0=0 0 AND 1=0 0 AND 0=0 Операнды записываются в десятичной форме, но во время выполнения перево- дятся в двоичную форму. Результат представлен в десятичной форме. Пример. Вычислить результат выражения A AND В, если А = 12 и В = 22. А и В занимают в па- мяти 2 байта и в двоичной форме имеют вид: 000000000001100 и 00000000010110. В ре- ", зультате выполнения операции 000000000001100 AND 00000000010110 в соответствии с таблицей истинности получим результат 0000000000000100, или 4 в десятичной форме. Следовательно, 12 AND 22 = 4. Сдвиг влево (К SHL N) восстанавливает в качестве результата значение, полу- ченное путем сдвига на N позиций влево представленного в двоичной форме числа К. Пример. Вычислить результат выполнения выражения 2 SHL 7. Число 2 занимает в памяти 2 байта и в двоичной форме имеет вид 0000000000000010. Сдвигаем каждый бит на 7 позиций влево, получаем 0000000100000000, что соответствует числу 256 в десятичной форме. Следовательно, 2 SHL 7 = 256.
84 Глава 4 Сдвиг вправо (SHR) выполняется аналогично с той лишь разницей, что сдвиг производится вправо. Например: Выражение 160 SHR 2 90 SHR 2 256 SHR 7 Результат 40 22 2 Логическое сложение (OR) выполняет сложение операндов в двоичной форме в соответствии с таблицей истинности: 1 OR 1=1 1 OR 0=1 0 OR 1=1 0 OR 0=0 Результат представлен в десятичной форме счисления. Пример. Вычислить результат выполнения выражения 12 OR 22. 12 и 22 занимают в памяти по 2 байта и в двоичной форме имеют вид 0000000000001100 и 0000000000010110 соответст- венно. Выполнив сложение по таблице истинности, получим двоичное значение суммы 0000000000011110, или 30 в десятичной форме. Следовательно, 12 OR 22 = 30. Исключающая дизъюнкция (исключающее ИЛИ) (XOR) производит сложе- ние операндов в соответствии р таблицей истинности: 1 XOR 1=0 1 XOR 0=1 0 XOR 1=1 0 XOR 0=0 Результат преобразовывается в десятичную форму счисления. Пример. Вычислить результат выполнения выражения 12 XOR 22. 12 и 22 занимают в памяти 2 байта и в двоичной форме имеют вид 0000000000001100 и 0000000000010110 соответствен- но. Выполнив сложение по таблице истинности, получим двоичное значение суммы: 0000000000011108, или 26 в десятичной форме. Следовательно, 12XOR22 = 26. Унарная операция сохранения знака (+) оставляет текущий знак числа без из- менения. Например: Выражение Результат + (-777) +(422) -777 422 Унарная операция отрицания знака (—) восстанавливает значение операнда с противоположным знаком. Например: Выражение -(-256) -(+39) Результат 256 -39
Типы данных 85 Применение операции NOT к данным целочисленных типов вызывает побит- ную инверсию (inversion — отрицание) — получение обратного значения, соот- ветствующего данному числу двоичного кода. Например: Выражение Результат NOT 0 —1 NOT 78 -79 4.6.2. Выражения и операции отношения Выражением отношения называется словосочетание языка, в котором два выражения связаны знаком операции отношения. Выражение отношения определя- ет истинность или ложность результата. Операции отношения выполняют срав- нение двух операндов и определяют, истинно значение выражения или ложно. В языке Паскаль операции отношения и рассмотренные ниже булевские опера- ции более важны при написании программ, чем в других языках, так как они интен- сивно используются для реализации разветвляющихся и циклических алгорит- мов. В табл. 4.4 приведены операции отношения, допустимые в версии языка Пас- каль для ПЭВМ. Сравниваемые величины могут принадлежать к любому скалярному или пере- числяемому типу данных. Результат всегда имеет булевский тип и принимает одно из двух значений: True (истина) или False (ложь). Таблица 4.4 Операции отношения Операция Название Выражение Результат в равно А=В True, если А равно В о не равно АоВ True, если А не равно В > больше А>В True, если А больше В < меньше А<В True; если А меньше В >== больше или равно А>=В True, если А больше или равно В <= меньше или равно А<=В True, если А меньше или равно В in принадлежность AinM True, если А находится в списке М 4.6.3. Логические выражения и операции Результатом выполнения логического (булевского) выражения является логи- ческое значение True или False. Операндами служат данные только булевского ти- па. Простейшими видами логических выражений являются следующие: • логическая константа; • логическая переменная;
86 Глава 4 • элемент массива логического типа; • логическая функция; • выражение отношения. Другие логические выражения строятся из вышеперечисленных путем приме- нения логических операций и круглых скобок. Список логических операций приве- ден в табл. 4.5. Таблица 4.5 Логические операции Операция Действие Выражение А В Результат . not Логическое отрицание not А True * False False True and Логическое И A and В True True True True False False False True False False False False or Логическое ИЛИ A and В True True True True False True False True True Г False False False xor Исключающее ИЛИ АхогВ True True False True False True False True True False False False Пример программы, которая сравнивает два вводимых целых числа и печатает значение логического заключения. program Sravnenie; var А,В : integer; Test : boolean; {Описание переменной логического типа Test} begin Write('Введите два числа: '); Readln(А,В); Test := А > В; {Присвоить Test значение результата сравнения} Writeln(*A больше В — ', Test) ; end. Упражнение 2. Введите текст программы, откомпилируйте ее и проверьте действие для следующих пар значений А и В: 2 и 3,4 и 2,5 и 5. Пример программы, которая вводит два целых числа и выводит на экран результат при- менения к ним арифметических и логических операций.
Типы данных 87 program Demo_Operас; var ; N,M : integer; {Описание двух переменных целого типа integer} ‘begin i Write(’Введите два целых числа'); f- Readin(N,M) ; {Считывание с клавиатуры значений переменных N,M} ‘ {Выполнение различных логических операций} Writein(N,' + ' ,M, ' = ' ,N + M) ; {Арифметическое сложение} Writein(N,' — ' ,M, ' = = ' ,N - - M) ; {Арифметическое вычитание} Writein(N,' * ' ,M, ' = ' ,N * M) ; {Арифметическое умножение} Writein(N,' / ' ,M, ' = ' ,N / M) ; {Вещественное деление} Writein(N,' div ' ,M, ' = ' ,N div М) ; {Целочисленное деление} Writeln(N,' mod • »M, ' = ’ .N mod М) ; {Остаток от деления} ? Writein('not N =', not N,' not M =', not M); {Отрицание} . j Writein(N,' and ' ,M,' = ' ,N and M) ; {Арифметическое И} ' Writein (N, ' or ' ,M, ' = = ' ,N or M) ; {Арифметическое ИЛИ} Writein(N,' xor ',M, ' = ',N xor M); {Исключающая дизъюнкция} Writein(N,' shl • ,M,' = ' ,N shl M) ; {Сдвиг влево} [ Writein(N,' shr ',M,' = ' ,N shr M) ; {Сдвиг вправо} end. Упражнение 3. Введите текст программы [, запишите на диск под именем ‘Demo Operac, откомпилируйте ее и проверьте действие для любых пар целых значений N и М Операция (а). С помощью операции @ можно создать указатель на перемен- ную. В табл. 4.6 показаны операнд и типы результата. Таблица 4.6 Операция создания указателя Операция Действие Тип операнда Тип результата @ Получение указателя Ссылка на переменную, процедуру, или идентификатор функции Указатель (совместимый с nil) Операция @ является унарной. В качестве операнда может использоваться ссылка на переменную, процедуру или идентификатор функции. После выполнения операнду возвращается соответствующий указатель, тип которого является таким же, как тип указателя nil, и, следовательно, его можно присвоить любому указате- лю переменной. 4.6.4. Приоритет операций Приоритетом называется очередность выполнения операций в выражении. Выполнение каждой операции происходит с учетом ее приоритета. Значения при- оритетов указаны в табл. 4.7.
88 Глава 4 Таблица 4.7 Порядок выполнения операций Операция Приоритет Вид операции @, NOT *,/, DIV, MOD, AND +, —, OR, XOR =, o, <,>, <=, >=, IN Первый (высший) Второй Третий Четвертый (низший) Унарная операция Операции типа умножения Операции типа сложения Операции отношения Для определения старшинства операций имеются четыре основных правила: 1. Операнд, находящийся между двумя операциями с различными .приоритета- ми, связывается с операцией, имеющей более высокий приоритет. 2. Операция, находящаяся между двумя операциями с равными приоритетами, связывается с той операцией, которая находится слева. 3. Выражение, заключенное в скобки, перед выполнением вычисляется как от- дельный операнд. 4. Операции с равным приоритетом производятся слева направо с возможным регулированием порядка выполнения скобками. Контрольные вопросы и задания Вопросы. 1. Для чего используется указание типа данных величины? 2. Как описывается тип величины в языке Паскаль? 3. Приведите полный перечень типов данных в Турбо Паскале с примерами величин ка- ждого типа. 4. Какие типы данных относят к скалярным типам данных? 5. Охарактеризуйте целочисленные типы данных: какие они могут принимать значения, в каких операциях могут принимать участие, сколько места занимают в памяти. 6. Какие типы отношений определены над данными целого типа? Какие стандартные функции определены для целых чисел? 7. Чем отличаются вещественные числа от целых? 8. Какие функции преобразуют вещественный аргумент в целое число? Чем они отли- чаются? 9. Охарактеризуйте символьный тип данных. 10. Где применяется булевский тип данных, какие он принимает значения, сколько мес- та требуется для его размещения в памяти? 11. Что такое пользовательские типы данных, чем они отличаются от стандартных ти- пов данных? Приведите примеры данных перечисляемого и интервального типов. 12. Что такое структурированные типы данных? 13. Почему от программиста требуется знание и правильное применение понятия тож- дественности и совместимости типов величин? Каковы признаки тождественности и усло- вия совместимости типов?
Типы данных 89 14. Что такое выражение, операция, операнд? Какие операции в языке Паскаль вы знае- те? 15. Охарактеризуйте каждую арифметическую операцию. 16. Какие операции называются операциями отношения? В чем заключаются особенно- сти результата операций отношения? 17. Охарактеризуйте каждую логическую операцию. 18. Каковы основные правила для определения старшинства операций? Задания. 1. Какие утверждения неправильны: 1) 144 — целое число? 2) 125 — шестнадцатеричное целое число? 3) 124.98 — вещественное число? 4)$1FF — шестнадцатеричное число? * 5)'Адрес' — целочисленная константа? 6) — 12.3 — отрицательное вещественное число? 7) 'Сумма' — строковое значение? 2. Какие числа представлены в форме с плавающей точкой: 165, 10.3Е+02, 1234.678, 3789, 5.7Е0.2, 63.9Е —04 3. Какие из следующих утверждений неправильны: 1) для диапазона 1.. 260 лучше всего подходит тип byte; 2) для диапазона 0.. 75000 лучше всего подходит тип word; 3) для диапазона 'a'..'z' лучше всего подходит тип char; 4) для вещественных переменных обычно применяется тип real; 5) значение 32000 входит в тип integer. 4. Какой тип подходит для данных диапазона: 6..9 0? , — 40..+45? , +10..+65000? , 100,0 .. 10000,0? 5. Какой идентификатор описывает самый широкий диапазон данных? 6. Какие из следующих соотношений неправильны: 1) 6.22Е+02 = 622; 2)20Е — 03 = 0.02; 3)2347.6Е —03 = 2.34760; 4)0.2Е03 = 2000.0; 5) 1200Е+03 = 12000.0. 7. Какие из следующих утверждений неправильны: 1) перед шестнадцатеричными числами записывается знак #; 2) для описания переменных используется слово var; 3) для описания констант используется слово const; 4) имена переменных не обязательно описывать в разделе var; 5) значение константы можно изменять. 8. Чем отличаются следующие выражения и каков будет результат их выполнения: 1) 10 +6 * 2/2; 2)(10 + 6)* 2/2; 3)(10 + 6*2) /2; 4)10 +6* (2/2). 9. Какие приоритеты указаны правильно, а какие нет: 1) приоритет операции * выше, чем +;
90 Глава 4 2) not имеет высший приоритет; 3) приоритет операции + выше, чем —; 4) приоритеты операций * и / одинаковы. 10. Чему равен результат выражения: 1)61 > 91 ? - 2)10> —7? 3) 208 > 175? 11. Какие результаты выполнения выражений неправильны: 1) (3>2) AND (5>6) = True? 2) (60>70) OR(100<90) = True? 3)('a'<b')XOR(l>0) = True? 4) NOT (30>10)= False? 5) 60» 20 = True? 12. Какие результаты выполнения выражений неправильны: .1)24/12 =2? 2) 11 div 5 = 1 ? 3) 10 div 3 = 3 ? 4) 15+21 div2 = 25 ? 13. Найдите ошибки в программе: program ИМЯ; const 5)2 div3 = 1 ? 6)6 * 5 =30? 7) 11 mod 5=1? 8)14mod(5 + 3) = 2? А, В : integer Rezult : byte; begin Write('Введите два числа: '}; Read(А,В); Rezult := A div В; Write('Отношение равно Rezult) end Исправьте программу и добейтесь компиляции без ошибок. 14. Составьте программу вычисления периметра и площади треугольника по введен- ным в диалоге трем сторонам, откомпилируйте и проверьте ее действие. 15. Составьте программу, вычисляющую значение среднего арифметического трёх на- туральных чисел. 16. Составьте программу, вычисляющую по введенному значению радиуса длину ок- ружности и ее площадь. Для вычисления значения числа л=3,141592... в Паскале использу- ется стандартная функция с идентификатором PI. 17. Составьте программу, вычисляющую периметр квадрата по указанному значению его площади. 18. Составьте программу, вычисляющую скорость прямолинейного равномерного дви- жения тела по указанным значениям перемещения и времени, в течение которого это пере- мещение совершено. 19. Составьте программу подсчета размера оплаты за электроэнергию по введенным значениям расхода электроэнергии и тарифа (тариф — стоимость 1 кВт»ч).
Глава 5. ВВОД-ВЫВОД ДАННЫХ 5.1. ОБЩИЕ СВЕДЕНИЯ Решение самой простой задачи на ЭВМ не обходится без Операций ввода-выво- да информации. Ввод данных — это передача информации от внешнего носителя в оперативную память для обработки. Вывод — обратный процесс, когда данные пе- .редаются после обработки из оперативной памяти на внешний носитель. Внешним носителем может служить терминал ввода-вывода, алфавитно-цифровое печатаю- щее устройство, гибкий (дискета) или жесткий (винчестер) магнитный диск и дру- гие устройства. В языке Паскаль стандартным средством общения человека и ЭВМ являются предопределенные файлы Input и Output, которые по умолчанию являются парамет- рами программы. Программа получает входные данные из файла Input и помещает результат обработки в файл Output. Стандартно файлу Input назначена клавиатура, а файлу Output — экран терминала. 5.2. ПРОЦЕДУРЫ ВВОДА-ВЫВОДА Для выполнения операций ввода-вывода служат четыре процедуры: Read, 'Readln, Write й Writein. В данном подразделе будет рассмотрено их применение ^для ввода данных с клавиатуры и вывода на экран и печатающее устройство. 5.2.1. Процедура чтения Read Процедура чтения Read обеспечивает ввод числовых данных, символов, строк и т.д. для последующей их обработки программой. Формат: Read (XI, Х2,. . . ,Хп) ; или , Read (FV, XI, Х2,...,Хп); где Х1,Х2,...,Хп — переменные допустимых типов данных; FV — переменная, связанная с файлом, откуда будет выполняться чтение. В данном подразделе рассматривается в основ- ном первый вариант формата Значения XI, Х2,..., Хп набираются минимум через один пробел на клавиатуре и высвечиваются на экране. После набора данных для одной процедуры Read нажи- мается клавиша ввода Enter.
92 Глава 5 Значения переменных должны вводиться в строгом соответствии с синтакси- сом языка Паскаль. Если соответствие нарушено (например, XI имеет тип integer, а | при вводе набирается значение типа char), то возникают ошибки ввода-вывода.. Со- ] общение об ошибке имеет вид: I/O error XX, где XX — код ошибки. Пояснительный текст поможет определить причину программного прерыва- ] НИЯ. I Пример. var I : real; j J : integer; * К : char; begin' * Read (1, J, K); . Первый вариант ответа: 212.45 38 'IT ! Второй вариант ответа: 'Л' 121.34 23 < Первый вариант обеспечивает нормальный ввод данных, так как набираемые значения 212.45 38 и 'IT соответствуют типам переменных I, J, К в процедуре Read. Второй вариант ввода вызовет ошибку с кодом 10, ибо для переменной I типа real набирается значение типа char. Если в программе имеется несколько процедур Read, данные для них вводятся потоком, т.е. после считывания значений переменных для одной процедуры Read данные для следующей процедуры Read набираются в той же строке, что и для предыдущей, до окончания строки, затем происходит переход на следующую строку. Пример. var А, В, Sumi : integer; С, D, Sum2 : real; begin Read (A, B); Sumi := A + B; Read (C, D); Sum2 := C + D; end. Набираем на клавиатуре: 18758 34 2.62E — 02 1.54E+01. После набора каждой пары данных нажимаем клавишу Enter, т. е. 18758 34 Enter 2.62Е — 02 1.54Е+01 Enter.
Ввод-вывод данных 93 Процедура чтения Readln аналогична процедуре Read, единственное отличие заключается в том, что после считывания последней в списке значения для одной процедуры Readln данные для следующей процедуры Readln будут считываться с начала новой строки. Если в предыдущем примере заменить Read на Readln: Readln (А, В) ; Sumi := А + В; Readln (С, D); Sum2 := С + D;. то после набора на клавиатуре значений для А и В курсор автоматически перейдет на новую строку, где будут набираться данные для С и D: 18758 34 Enter * 2.62Е — 02 1.54Е+01 Enter 5.2.2. Процедура записи Write Процедура записи Write производит вывод числовых данных, символов, строк й булевских значений. Формат: Write (Yl, Y2 , . . .,Yn); ИЛИ Write (FV, Yl, Y2,...,Yn); где Yl, Y2,...,Yn — выражения типа integer, byte, real, char, boolean ht.a.;FV — имя фай- да, куда производится вывод. Для вывода на принтер FV равно Lst. Чтобы устройство Lst стало доступным, необходимо подключить модуль Printer с помощью зарезервированного слова uses. Пример, uses Printer; । var begin Write(234); {Выражение представлено значением} Write(A+B — 2); {Выводится результат выражения} f Write(Lst, 'Результат вычислений = ', Resultl); end. В первом варианте формата значения Yl, Y2,...,Yn выводятся на экран дисплея, йо втором — на печатающее устройство. 5.3. ФОРМАТЫ ВЫВОДА В процедурах вывода Write и Writein имеется возможность записи выражения, определяющего ширину поля вывода.
94 Глава 5; В приведенных ниже форматах используются следующие обозначения: I, р, q — целочисленное выражение; R — выражение вещественного типа; В — выражение булевского типа; Ch — выражение символьного типа; S — выражение строкового типа; # — цифра; * — знак "+" или " — "; _ — пробел. I — выводится десятичное представление величины I, начиная с позиции рас- положения курсора. ь t Значение I Выражение Результат. 134 Write (I); 134 5671 Write (I) ; 5671 287 Write (I,I,I); 287287287 Ир — выводится десятичное представление величины I в крайние правые по- зиции поля шириной р. Значение I Выражение Результат 134 Write (I:6); _134 1 Write (1:10); 1 312 Write (1+1:7); 624 R — в поле шириной 18 символов выводится десятичное представление вели- чины R в формате с плавающей точкой. Если R >= 0.0, используется формат Если R < 0.0, формат имеет вид — Значение R Выражение Результат 715.432 Write (R); 7.1543200000E+02 —1.919E+01 Write (R); _-l.9190000000E+01 567.986 Write (R/2); 2.8399300000E+02 R:p — в крайние правые позиции поля шириной р символов выводится деся- тичное представление значения R в формате с плавающей точкой. Если R >= 0.0, используется формат___. . . _##. . #Е*##, причем минимальная длина поля вывода составляет 7 символов. Если R < 0.0, формат имеет вид _. . . _ — #.##.. #Е*##. Минимальная длина поля вывода 8 символов. После десятичной точки выводится по крайней мере одна цифра. Значение R Выражение Результат 511.04 Write (R:15); 5.110400000Е+02 -511.04 Write (R:15); -5.11040000E+02 46.78 Write (-R:12); -4.67800E+01
Ввод-вывод данных 95 R:p:q — в крайние правые позиции поля шириной р символов выводится деся- тичное представление значения R в формате с фиксированной точкой, причем по- сле десятичной точки выводится q цифр (0 <= q <= 24), представляющих дробную часть числа. Если q = 0, ни дробная часть, ни десятичная точка не выводится. Если q > 24, то при выводе используется формат с плавающей точкой. Значение R Выражение Результат 511.04 Write (R:8:4); 511.0400 -46.78 Write (R:7:2); 46.78 -46.78 Write (R:9:4); _—46.7800 Ch — начиная с позиции курсора выводится значение Ch. Значение Ch Выражение Результат 'X' Write (Ch); X t •Y' Write (Ch); Y ' • ' Write (Ch, Ch, Ch); ••• Ch:p — в крайнюю правую позицию поля шириной р выводится значение Ch. Значение Ch Выражение Результат 'X' Write (Ch:3); X •Y' Write (Ch:5); Y • ? • Write (Ch:2,Ch:4); 1 1 S — начиная c позиции курсора, выводится значение S (строка или массив сим волов, если его длина соответствует длине строки). Значение S Выражение Результат 'Day N' Write (S); Day N ' Ведомость LI' Write (S); Ведомость 11 'RRRDDD' Write (S,S); RRRDDDRRRDDD S:p — значение S выводится в крайние правые позиции поля шириной р симво- ЛОВ. Значение S Выражение Результат । 'Day N' Write (S:10); Day N 'Ведомость 11' Write (S:13) ; —Ведомость 11 •RRRDDD' Write (S:7,S:7) ; _RRRDDD_RRRDDD В — выводится результат выражения В True или False, начиная с текущей по- зицйи курсора. Значение В Выражение Результат True Write (В); True False Write (В, not В); False True В:р — в крайние правые позиции поля шириной р символов выводится резуль- тат булевского выражения В True или False.
96 Глава 5 Значение В Выражение Результат True Write (В:6); __True False Write (В: 10); False True Write (B:5,not B:7); True False Оператор записи Writein аналогичен процедуре Write, но после вывода послед- него в списке значения для текущей процедуры Writein происходит перевод курсо- ра к началу следующей строки. Процедура Writeln, записанная без параметров, вызывает перевод строки. Для пояснения работы процедуры Writeln приведем фрагмент программы: А := 4; В := 6; С == 55/ Write(А: 3) ; Write(В:3); Write(С:3) ; Sum: = А + В + С; Writeln('А=', А); Writeln('В=', В); Writeln('С=', С) ; Writeln(1Сумма А+В+С равна ', Sum) ; Результат выполнения: 4 6 55 А=4 В=6 С=55 z Сумма А+В+С равна 65 Примером использования формата в процедуре Writeln может служить следую- щая программа. program DemoWritein; {Программа вычисляет площадь прямоугольника и выводит на печать результат} uses Printer; {Подключение модуля с библиотекой процедур и функций, обслуживающих принтер — устройство Lst} var А, В, S: integer; {А,В — длина сторон,S — площадь} begin А:= 8; В:= 4; S:= А * В; Writeln (Lst, '--------------------------------------' ) ; Writeln(Lst,'| Сторона A I Сторона В | Площадь |'); Writeln(Lst, '---------------------------------------'); Writeln(Lst,'|•, A:7, B:ll, S:ll, • | : 5) ; Writeln (Lst,'---------------------------------------') end.
Ввод-вывод данных 97 В результате работы программы получим таблицу, напечатанную на бумаге: | Сторона А | Сторона В | Площадь] ] 8 4 32 ] каждая строка которой будет печататься с первой позиции новой строки печатаю- щего устройства. Упражнение. Загрузите интегрированную среду программирования, введите текст дан- ной программы, откомпилируйте ее и проверьте результат выполнения. Если к вашему компьютеру не подключен принтер, то проверьте вывод на экран, для чего в записи спи- ска вывода процедуры V^iteln опустите слово Lst. Контрольные вопросы и задания Вопросы. 1. Какие процедуры служат в Паскале для выполнения операций ввода-вывода? 2. В чем заключается отличие процедуры чтения Readin от процедуры Read? 3. Как задать вывод информации на принтер? 4. Какой стандартный модуль обеспечивает использование принтера в вашей програм- ме? Каким образом описывается его подключение? 5. Для чего в процедурах вывода Write и Writein определяется ширина поля вывода? Какие обозначения используются в форматах вывода? » Задания. 1. Составьте программу, которая, используя процедуру Writein, изображает на экране домик: * * * * * *********** * * * * *********** 2. Составьте программу, которая выводит на экран компьютера заставку, аналогичную следующей: ********************************** * Программа * * вычисления суммы чисел * * Автор: Петров В.И. * ********************************** 3. Напишите программу, которая вводит значения трех переменных: А, В, С типа word и выводит их сумму. Ввод каждого значения произвести с отдельной строки. Результат 4-1 16
98 Глава 5 также помещается на отдельную строку. При составлении программы обеспечьте приглаше- ние к вводу данных. 4. Напишите программу, которая вводит значения четырех переменных А, В, С, D типа integer и выводит их сумму. Ввод пары значений А и В произвести на одной строке, С и D — на другой. Результат вывести на отдельную строку, и курсор оставить на той же строке. 5. Напишите программу, которая вводит значения двух переменных: А, В типа integer с приглашениями к вводу каждой переменной и выводит их разность. Результат ввода и ре- зультат расчета выводить на экран и параллельно на печать. Приглашение и ввод каждого значения произвести в отдельных строках. Вывод сопроводить пояснением. 6. Напишите программу ввода значений А, В, С в одной строке и выведите результат выражения А * (В / 3,14) + (С * 3) в отдельной строке. 7. Напишите программу ввода значений R, Y в одной строке и выведите результат вы- ражения R * Y2 + (Y/5) в той же строке. ♦ 8. Напишите программу вычисления площади прямоугольного треугольника, значения катетов которого А и В вводятся с клавиатуры. Результат вывести в следующем виде: "Для значений катетов 4 и 6 площадь прямоугольного треугольника равна 12”. 9. Напишите программу вычисления идеального веса, человека по формуле: Ид.вес = Рост в см — 100. Значение роста вводите с клавиатуры. Результат вывести в следующем виде: "Для человека ростом 165 см идеальный вес равен 65 кг". 10. Напишите программу получения следующей формы: ************************************ * А * В * А+В * А—В * А/В * - ************************************ 6 2 8(4 3.0 ************************************ 11. Вы положили деньги в Сбербанк на срочный депозит на три месяца из расчета 60 % годовых. Напишите программу, которая вычислит причитающуюся вам через три месяца сумму. 12. Розничная цена мужского костюма составляет Р руб. Торговая скидка в пользу ма- газина Т % розничной цены. Составьте программу определения оптовой цены костюма. 13. Составьте программу расчета, какую массу соли и воды надо взять для приготовле- ния раствора массой m г с массовой долей w %. 14. Составьте программу исследования положительного вещественного числа А, в ко- торой определялись бы значения следующих величин: целая часть, дробная часть, значение арифметического квадратного корня, остаток от деления на 5, символ по целой части числа. 15. Составьте программу, определяющую, сколько времени в минутах затратит школь- ник на дорогу от школы до стадиона, если известна длина этого расстояния S и средняя ско- рость движения школьника V км/ч? Значения S и V задаются с клавиатуры. 16. Составьте программу, вычисляющую, сколько процентов от А + В — С приходит- ся: а) на А; б) на В; в) на С?
Глава 6. ОПЕРАТОРЫ j 6.1. ОБЩИЕ СВЕДЕНИЯ Оператором называется предложение языка программирования, задающее . полное описание некоторого действия, которое необходимо выполнить. Основная К часть программы на языке Турбо Паскаль представляет собой последбвательность операторов. Разделителем операторов служит точка с запятой. Все операторы язы- ка Турбо Паскаль можно разделить на две группы: простые и структурные. 6.2. ПРОСТЫЕ ОПЕРАТОРЫ Операторы, не содержащие никаких других операторов, называются просты- ми. К ним относятся операторы присваивания, безусловного перехода, вызова про- ; цедуры и пустой оператор. 6.2.1. Оператор присваивания Оператор присваивания (:=) предписывает выполнить выражение, заданное в .его правой части, и присвоить результат переменной, идентификатор которой расположен в левой части. Переменная и выражение должны быть совместимы по типу. Ниже представлен общий вид оператора присваивания. Оператор присваивания выполняется следующим образом: сначала вычисляет- ся выражение в правой части присваивания, а затем его значение присваивается пе- г ременной, указанной в левой части оператора. Например, для оператора Rezult := А f div В; сначала выполняется целочисленное деление значения переменной А на i значение переменной В, а затем результат присваивается переменной Rezult. В предыдущих примерах мы использовали этот оператор, например: А:= 8;
100 Глава 6 S:= А * В; Ostatok := A mod В; Ratio := А / В; 6.2.2. Оператор безусловного перехода (go to) Оператор безусловного перехода (go to) означает "перейти к" и применяется в случаях, когда после выполнения некоторого оператора надо выполнить не сле- дующий по порядку, а какой-либо другой, отмеченный меткой оператор. Синтаксическая диаграмма для оператора перехода имеет следующий вид: Напомним, что метка объявляется в разделе описания меток и может содер- жать как цифровые, так и буквенные символы. Пример. до to 999; до to EndBlock; При использовании оператора go to необходимо помнить, что областью дейст- вия метки является только тот блок, в котором она описана. Передача управления в другой блок запрещена. Использование безусловных передач управления в программе считается теоре- тически избыточным и подвергается серьезной критике, так как способствует соз- данию малопонятных и трудномодифицируемых программ, которые вызывают большие сложности при отладке и сопровождении. Поэтому рекомендуется мини- мальное использование оператора go to с соблюдением следующих правил: • следует стремиться применять операторы перехода (если кажется невозмож- ным обойтись без них) для передачи управления только вниз (вперед) по тексту программы; при необходимости передачи управления назад следует использовать операторы цикла; • расстояние между меткой и оператором перехода на нее не должно превы- шать одной страницы текста (или высоты экрана дисплея). 6.2.3. Оператор вызова процедуры Оператор вызова процедуры служит для активизации предварительно опреде- ленной пользователем, или стандартной, процедуры. Например: ClrScr; {Вызов стандартной процедуры очистки экрана} InitWork(True); {Вызов пользовательской процедуры}
Операторы 101 6.2.4. Пустой оператор Пустой оператор не содержит никаких символов и не выполняет никаких дей- ствий. Обычно пустой оператор используется для организации перехода к концу локального или глобального блока в случаях, если необходимо пропустить не- сколько операторов, но не выходить из блока. Для этого перед зарезервированным словом end ставятся метка и двоеточие, например: label Metka; begin go to Metka; {Переход в конец блока} * Metka: {Пустой оператор помечен меткой} end; 6.3. СТРУКТУРНЫЕ ОПЕРАТОРЫ Структурные операторы представляют собой конструкции, построенные из других операторов по строго определенным правилам. Все структурные операторы можно разделить на три группы: составные, условные, повтора. 6.3.1. Составной оператор Составной оператор представляет собой группу из произвольного числа опера- торов, отделенных друг от друга точкой с запятой, и ограниченную операторными скобками begin и end. Синтаксическая диаграмма составного оператора записывается так: Составной оператор воспринимается как единое целое и может находиться в любом месте программы, где синтаксис языка допускает наличие оператора. 6.3.2. Условные операторы Условные операторы предназначены для выбора к исполнению одного из воз- можных действий (операторов) в зависимости от некоторого условия (при этом од- но из действий может быть пустым, т. е. отсутствовать). В качестве условий выбора используется значение логического выражения. В Турбо Паскале имеются два ус- ловных оператора: if и case.
102 Глава 6 Оператор условия if.. Оператор условия if является одним из самых популяр- ных средств, изменяющих естественный порядок выполнения операторов програм- мы. Синтаксическая диаграмма оператора условия if выглядит таким образом: Как видно из диаграммы, он может принимать одну из следующих форм: if <условие> then <оператор1> ЕСЛИ. <условие> ТО <оператор1> else <оператор2>; ИНАЧЕ <оператор2> if <условие> then <оператор>; ЕСЛИ <условие> ТО <оператор> Оператор условия if выполняется следующим образом. Сначала вычисляется выражение, записанное в условии. В результате его вычисления получается значе- ние булевского типа. В первом случае, если значение выражения есть True (ис- тина), выполняется <оператор!>, указанный после слова then (ТО). Если результат вычисления выражения в условии есть False (ложь), то выполняется <оператор2>. Во втором — если результат выражения True, выполняется <оператор>, если False — оператор, следующий сразу за оператором if. Операторы if могут быть вложен- ными. Пример фрагмента программы с оператором условия if: Read(Ch); if Ch='N' then ParolTrue else Parol:= False; Read(X); if Parol = True then if X = 100 then Write(*Пароль и код правильные') else begin Writein('Ошибка в коде'); Halt(l) end; В данном примере с клавиатуры считывается значение переменной символьно- го типа Ch. Затем проверяется условие Ch=?4'. Если оно выполняется, то перемен- ной Parol булевского типа присваивается значение True, если условие не выполня- ется, False. Затем с клавиатуры считывается значение кода X. Далее оператор if проверяет условие Parol = True. Если оно имеет значение True, то выполняется про- верка введенного пароля оператором if Х=100. Если условие Х=100 имеет значение True, то выводится сообщение “Пароль и код правильные”, и управление в про- грамме передается на оператор, следующий за словом end, если оно имеет значение
Операторы 103 False, выполняется составной оператор, стоящий после слова else, который выво- дит на экран видеомонитора сообщение “Ошибка в коде”, и вызывает стандартную процедуру Halt( 1) для остановки программы. Примечание. При использовании вложенных условных операторов может возникнуть синтаксическая неоднозначность, иллюстрируемая следующей схемой: if условие! then if условие2 then <оператор1> else <оператор2> Возникающая двусмысленность, к какому оператору if принадлежит часть else <опера- тор2>, разрешается тем, что служебное слово else всегда ассоциируется (связывается) с бли- жайшим по тексту служебным словом if, которое еще не связано со служебным словом else. В связи с этим следует проявлять аккуратность при записи вложенных операторов усло- вия. Упражнение 1. Составим программу, которая/ вычисляет частное двух целых чисел. В связи с тем, что делить на нуль нельзя, организуем контроль ввода данных. Для контроля вводимых значений делителя используем оператор условного перехода if ... then ... else. Текст программ может выглядеть следующим образом: program Tutor_If_Then_Else; » var А, В : integer; Rezult: real; begin Write('Введите значение делимого A >'); Read(A); Write('Введите значение делителя В >'); Read(В); if В=0 {Контроль ввода} then Writein('На нуль делить нельзя') {Условие выполнено} else {Условие не выполнено} begin {Начало составного оператора} Rezult := А / В; Writeln.('Частное чисел ' ,А, ' и ' ,В, ' = ',Rezult) ; end; {Конец составного оператора} end. Введите текст программы, откомпилируйте ее и исполните для разных целых значений переменных А и В. Попробуйте задать значение В=0 и убедитесь, что защита ввода работа- ет. В будущих ваших программах выполняйте контроль ввода данных. 6.3.3. Получение подсказки по языку программирования Часто программисты, особенно начинающие, нуждаются в уточнении синтак- сиса написания той или иной конструкции языка. Как уже было сказано в гл. 2., интегрированная среда программирования позволяет оперативно получить подсказ-
104 Глава 6 ку по языку программирования. Для этого курсор устанавливается на нужном заре- зервированном слове и нажимается Ctrl+Fl. Например, для получения подсказки по синтаксису оператора if установите курсор на слове if и нажмите Ctrl+Fl. На эк- ране будет раскрыто окно подсказки. Нажимая клавишу со стрелкой вниз, или PageDown, просмотрите весь текст подсказки. Если есть необходимость, можно пример из подсказки скопировать в окно редактирования. Для этого выберите оп- цию Index пункта Help главного меню интегрированной среды программирования или нажмите клавиши Shift+Fl. После этого на экране развернется окно со списком разделов помощи. Манипу- лируя мышью или нажимая клавишу со стрелкой вниз или PageDown, переместите указатель на слово if и нажмите Enter. Для копирования нужного фрагмента, пере- мещаясь по тексту подсказки, установите курсор в начало копируемого блока и, прижав клавишу Shift, манипулируя мышью или клавишами со стрелками вниз, вправо, выделите другим цветом весь блок, как показано на рис. 6.1. Flip. Edit Search Run Cnmnilf» Dphnu Tnnls OntHnns Window Hp.1' Рис. 6.1. Окно подсказки по оператору if с помеченным блоком Для получения дополнительных возможностей в окне подсказки нажмите Alt+Fl0 для вызова локального меню, как показано на рис. 6.2. Скопируйте блок в карман, для чего воспользуйтесь опцией Сору локального меню или нажмите Ctrl+Ins, затем нажмите Esc для отказа от подсказки. После это- го установите курсор в окне редактирования в позицию, куда нужно скопировать пример, и вызовите локальное меню. Выберите в меню опцию Paste или нажмите Shift+Ins. Блок из кармана будет скопирован в окно редактирования. Подробное описание использования справоч- ной системы интегрированной среды программирования см. в приложении С.
Операторы 105 Рис. 6.2. Локальное меню окна редактирования 6.3.4. Тестирование и отладка программ Как вы уже обнаружили, примеры и задания по мере изучения учебного мате- риала усложняются. В связи с этим сложнее становится проверять правильность функционирования программ и обнаруживать ошибки в их текстах. Для проверки правильности функционирования программы выполняется тес- тирование — исполнение программы с использованием некоторого набора вход- ных данных, охватывающего весь спектр возможных значений для данного типа за- дач и проверяющего граничные условия, а также позволяющего через контроль промежуточных и конечных результатов решения задачи в ходе исполнения про- граммы проверить выполнение операторов программы в требуемой последователь- ности и правильность действия всех алгоритмических конструкций (ветвлений, циклов, обращений к подпрограммам и т.п.). Процесс тестирования удостоверяет качество программы, поэтому он должен быть документирован, т. е. будущие пользователи должны знать, как и при каких обстоятельствах программа тестировалась, каковы были входные данные и резуль- таты, с тем чтобы тест можно было повторить. Если цель тестирования — только выявить ошибки в программе, то для обна- ружения и устранения ошибок в программе выполняют ее отладку. Отладка в ин- тегрированной среде программирования Турбо Паскаль заключается в том, что с помощью встроенного в интегрированную среду программирования специального средства — отладчика анализируется поведение программы в “окрестностях” ошибки. С этой целью в интегрированной среде программирования обеспечивается возможность трассировки программы, т. е. выполнения "по шагам" с остановкой в
106 Глава 6 указанных точках или при выполнении заданных условий просмотра и изменения содержимого ячеек памяти компьютера, регистров процессора. Эти возможности отладки предоставляются программисту через пункт Debug главного меню интегрированной среды программирования. Для просмотра значе- ний переменных в процессе выполнения программы активизируйте опцию Add watch, как показано на рис. 6.3. Edit Search Run Compile Options Window Hel program Tutor_If_Then_Else; var fi.B : integer; Rezult: real; I begin Write('Введите значение делимо! Read(fl); г WriteCВведите значение делите Read(B); if В=О (контроль then WritelnCHa ноль делит|_ else (условие не begin (начало составного оператора! Rezult :== Й / В; WriteIn('Частное чисел '.Й.' и ’„В»’ = *,Rezult); end; (коней составного оператора) Breakpo ints Gall stack Register Watch Output User screen Ctrl*F3 Alt+F5 Eualuate/modifи .. Ctrl*F4 | Add watch Ctrl+FV 11 | Add breakpoint... 1 end. Fl Rely | Insert a watch expression into the Watch window Рнс. 6.3. Вид интегрированной среды программирования с активизированным пунктом Debug главно- го меню В окно добавления просмотра Add watch введите выражение (например: В=0) для наблюдения за его значением в ходе выполнения программы, как показано на рис. 6.4. Рнс. 6.4. Окно ввода выражений для просмотра в процессе отладки
Операторы 107 Для включения переменной (выражения) в список просмотра можно, уста- новив курсор на актуальной переменной или начале выражения, нажать клавиши Ctrl+F7. Во введенном на экране окна ввода выражений для просмотра, нажимая клавишу со стрелкой вправо, задать нужное выражение (оно копируется из окна ре- дактирования). Для завершения следует нажать Enter или выбрать кнопку [ ОК ]. Для того чтобы в процессе отладки одновременно с окном редактирования на экране было выведено окно просмотра в главном меню, выберите пункт Window и задайте режим Tile (расположение непересекающихся окон на экране "черепи- ца") отображения окон. После этого на экране будут одновременно видны и окно редактирования с текстом программы, и окно просмотра с текущими значениями избранных вами для наблюдения переменцых, как показано на рис. 6.5. Рис. 6.5. Вил экрана компьютера с окнами редактирования и просмотра , Если вы ошибочно ввели неверный идентификатор переменной или желаете удалить некоторый идентификатор из окна просмотра, то нажмите клавишу F6, после этого из окна редактирования вы перейдете в окно просмотра. Клавишами со стрелками вверх-вниз установите курсор на удаляемом идентификаторе и на- жмите клавишу Del. Если вы хотите пополнить список переменных, чьи значения просматриваются в процессе отладки, то нажмите Ins и введите идентификатор или соответствующее выражение. Для отладки программы в режиме пошагового прохода выберите режим Step over пункта Run главного меню интегрированной среды программирования, как по- казано на рис. 6.6.
108 Глава 6 Рис. 6.6. Выбор режима пошагового прохода программы при отладке Можно просто нажимать клавишу F8, при этом выполняется текущая подсве- ченная строка и курсор перемещается к следующей строке, а вам остается наблю- дать за изменениями значений переменных в окне просмотра. Если эти изменения не соответствуют ожидаемым в соответствии с условием задачи, то займитесь коррекцией алгоритма и программы, после чего заново отком- пилируйте программу и повторите проверку исполнения в пошаговом проходе. Ес- ли есть необходимость пройти по "шагам" через всю программу с пошаговой про- веркой вызываемых процедур, функций, методов, объектов, то выберите режим Trace into пункта Run главного меню или нажмите F7. Контрольные точки останова. Для отладки длинных программ, чтобы не просматривать весь текст программы, а сосредоточиться на отдельном фрагменте, можно установить в начале фрагмента контрольную точку останова. Контрольные точки похожи на сигнал “стоп” для программы. Для задания контрольной точки нужно, установив курсор в данной строке, выбрать в пункте меню Debug опцию Add breakpoint (добавить точку останова) или просто нажать Ctrl+F8. Эта команда устанавливает на текущей строке точку останова. Например, на рис. 6.7 показана установка контрольной точки останова на строке 10. Строка программы с точкой останова будет высвечена. Если там уже имеется точка останова, то она отменяет- ся. После того как вы установите контрольные точки и запустите программу, она будет выполняться нормально до первой встретившейся контрольной точки. После останова программы вы можете просмотреть текущие значения переменных и про- должить исполнение программы по шагам (F8 или F7).
Операторы 109 По окончании отладки удалите все контрольные точки останова, выбрав пункт меню Debug, в нем опцию Breakpoints, и в выведенном на экран окне списка точек останова программы активизируйте кнопку Clear АП (очистить все точки останова). Рис. 6.7. Установка контрольной точки останова на строке 10 Часто просмотр значений переменных в конкретном месте программы обеспе- чивается за счет установки курсора в нужную строку и запуска исполнения про- граммы с остановом в строке, на которой расположен курсор. На рис. 6.8 показан выбор пункта Go to cursor из меню Run. ile Edit Search Compile Debug Tools Option Run Step ouer Trace into program Tutor_If_Th var A,В : integer Rezult: real; begin WriteC Введите з Read(A>; |________________ Write С Введите значение делителя 1 5’ >; Read(B); Ctrl+F9 F8 _____F7 Program reset Ctrl+F2 Parameters... if B=O (контроль ввода! then WriteInС На ноль делить нельзя') (условие выполнено! else (условие не выполнено! begin (начало составного оператора! Rezult := А /В; 10:1 Д] В=О: True Watches Go tf? Fl Help | Run program from the run bar to the cursor position Рис. 6.8. Запуск исполнения программы с остановом в строке, на которой расположен курсор
по Глава 6 После запуска программа исполняется до строки, на которой расположен кур- сор, и останавливается, позволяя программисту просмотреть текущие значения пе- ременных в данном месте программы. На рис. 6.9 показан просмотр текущих зна- чений переменных в строке 14, до которой исполнилась программа. File Edit Search Run Conpile pebitff Tools Options Window Help IF_THEN.PfiS program Tutor_If_Then_Else; var A„B : integer; Rezult: real; begin WriteCВведите значение делимого A >’>; Read<A); WriteCВведите значение делителя В >*>; Read<B>; if B=0 <контроль ввода! then WritelnCHa ноль делить нельзя') < ус лови* выполнено) else <условие не выполнено) ________begin__________<начало составного оператора)_________ WriteIn<*Частное чисел *»A,* и ‘,B,* = Rezult); А: 3 В: 5 В=0: False Rezult: 0.0 Watches -2 Рис. 6.9. Просмотр текущих значений переменных в строке 14, на которой расположен курсор Чтобы закрыть окно просмотра, установите его текущим и нажмите Alt+F3. Рекомендация. Действие достаточно сложных программ или их фрагментов, включающих сомнительные, на ваш взгляд, операторы, следует обязательно тести- ровать, используя режим отладки и просматривая текущие значения переменных. Оператор выбора case. Если один оператор if может обеспечить выбор из двух альтернатив, то оператор выбора case позволяет сделать выбор из произволь- ного числа имеющихся вариантов. Он состоит из выражения, называемого селекто- ром (selection — выбор альтернативы), и списка параметров, каждому из которых предшествует список констант выбора (список может состоять и из одной кон- станты). Синтаксическая диаграмма оператора case выглядит следующим обра- зом: Следуя данной диаграмме, получим следующий формат записи оператора case: case <выражение-селектор> of <список1>: <оператор1; >
Операторы 111 <список2>: <оператор2; > <списокЫ>: <операторЫ> else <оператор> end; Оператор case работает следующим образом. Сначала вычисляется значение выражения-селектора, затем обеспечивается реализация того оператора, константа выбора которого равна текущему значению селектора. Если ни одна из констант не равна текущему значению селектора, выполняется оператор, стоящий за словом else. Если слово else отсутствует, активизируется оператор, находящийся за словом end, т. е. первый оператор за границей case. Селектор должен относиться к одному из целочисленных типов (находящихся в диапазоне — 32768..32767): булевскому, литерному или пользовательскому. Список констант выбора состоит из произвольного количества значений, или диа- пазонов, отделенных друг от друга запятыми. Границы диапазона записываются двумя константами через разграничитель Тип констант в любом случае должен совпадать с типом селектора. В синтаксическом описании, приведенном выше, предполагается использование одного оператора для каждой альтернативы, но при необходимости можно задать несколько операторов, сгруппировав их в составной оператор. В то же время ветвь else допускает использование последовательности операторов, разделенных символом При использовании оператора выбора case должны выполняться следующие правила: 1. Значения выражения "переключателя", записанного после служебного слова case, должны принадлежать дискретному типу (лат. discretus — прерывистый, дробный, состоящий из отдельных частей); для целого типа они должны лежать в диапазоне integer. 2. Все константы, предшествующие операторам альтернатив, должны иметь тип, совместимый с типом выражения. 3. Все константы в альтернативах должны быть уникальны в пределах операто- ра варианта (т. е. повторения констант в альтернативах не допускаются); диапазо- ны не должны пересекаться и не должны содержать констант, указанных в данной или других альтернативах. < Ниже приведены типичные формы записи оператора case. Селектор интервального типа: case I of 1..10 : Writein (’число 1:4, ' в диапазоне 1 — 10'); 11..20 : Writein ('число ’, 1:4, ’ в диапазоне 11 — 20');
112 Глава 6 21..30 : Writeln ('число ', 1:4, ' в диапазоне 21 — 30') else Writeln ('число ', 1:4, ' вне пределов контроля') end; Селектор целочисленного типа: case I of 1 : Z := I + 10; 2 : Z := I + 100; 3 : Z := I + 1000 end; Селектор перечисляемого пользовательского типа: * var Season: (Winter, Spring, Summer, Autumn); begin case Season of Winter : Writeln('Winter'); Spring : Writeln('Spring'); Summer : Writeln('Summer'); Autumn : Writeln('Autumn') end; end; Пример программы с использованием оператора case, которая по введенному вами номеру дня недели выводит на экран видеомонитора его название на русском языке. program Day_Week; var Day : byte; begin Write ('Введите номер дня недели :'); Readln(Day); case Day of {Вычисление значения селектора и выбор} 1 : Writeln('Понедельник' ) ; 2 : Writeln('Вторник'); 3 : Writeln('Среда'); 4 : Writeln('Четверг'); 5 : Writeln('Пятница'); б : Writeln('Суббота'); else Writeln('Воскресенье' ) ; end; end.
Операторы 113 В данном примере на экран видеомонитора выводится приглашение 'Введите номер дня недели с клавиатуры считывается целочисленное значение дня недели и присваивается переменной Day. Затем в зависимости от значения селектора DAY обеспечивается реализация того оператора,, константа выбора которого равна текущему значению селектора. Например, если значение Day равно 3, то реализует- ся оператор Writeln('Cpefla'). Если значение Day равно 7, а ни одна из констант не равна этому значению селектора, то выполняется оператор, стоящий за словом else (на экран выводится текст ‘Воскресенье’)- Если слово else отсутствует, активизиру- • ется оператор, находящийся за словом end;, т. е. первый оператор за границей case. Если значение Day не равно значению ни одной из констант выбора (например, Day=8 или Day=0), то активизируется оператор, находящийся за словом end;, т. е. первый оператор за границей case — оператор end. Упражнение 2. Введите текст программы, откомпилируйте ее и исполните для разных целых значений переменной Day. Проанализируйте действие оператора выбора case при различных значениях номера дня (Day). Обратите внимание, что тип Day и констант выбо- ра 1— 6 одинаковый — byte. Упражнение 3. Модифицируйте программу, дополнив ее контролем правильности вво- да (если пользователь введет значение дня недели <1 или >7, то необходимо вывести на экран видеомонитора сообщение об ошибке ввода). Для этого используйте оператор усло- вия if... then... else. 6.3.5. Операторы повтора Если в программе возникает необходимость неоднократно выполнить некото- рые операторы, то используются операторы повтора (цикла). В языке Паскаль раз- ' личают три вида операторов цикла: while, repeat, for. Они используются для орга- низации циклов различных типов. Выражение, управляющее повторениями, долж- Г но иметь булевский тип. й Если число повторений оператора (составного оператора) заранее неизвестно, а I задано лишь условие его повторения (или окончания), используются операторы while, repeat. Оператор for используется, если число повторений заранее известно. 1 Оператор while. Оператор while (пока) часто называют оператором цикла с ' предусловием за то, что проверка условия выполнения тела цикла производится в г самом начале оператора. Синтаксическая диаграмма для данного оператора выглядит следующим обра- зом: while 4 выражение 1 оператор
114 Глава 6 Формат записи: while <условие продолжения повторений> do <тело цикла>; Условие — булевское выражение, тело цикла — простой или составной опе- ратор. Перед каждым выполнением тела цикла вычисляется значение выражения условия. Если результат равен True, тело цикла выполняется и снова вычисляется выражение условия. Если результат равен False, происходят выход из цикла и пере- ход к первому после while оператору. Примером работы while может служить программа Demo While, которая произ- водит суммирование 10 произвольно введенных целых чисел. program DemoWhile; const Limit = 10; {Ограничение на количество вводимых чисел} var Count, Item, Sum: integer; begin Count:= 0; {Счетчик чисел} Sum: = 0; {Сумма чисел} while (Count < Limit) do {Условие выполнения цикла} begin Count:= Count+1; Write('Введите Count, ' — e целое число: '); Readln(Item); {Ввод очередного числа |C клавиатуры} Sum:— Sum+Item; end; Writein(* Сумма введенных чисел равна *, Sum); end. В данном примере в разделе описания констант описана константа Limit=10, задающая ограничение на количество вводимых чисел. В разделе описания пере- менных описаны переменные Count, Item, Sum целочисленного типа. В начале выполнения программы обнуляются значения счетчика введенных чисел Count и их суммы. Затем выполняются цикл ввода 10 чисел и их суммирование. Вначале оператор условия while проверяет условие Count < Limit. Если условие верно, то выполняет- ся составной оператор в теле цикла: begin Count:= Count+1; Write('Введите ', Count, e целое число: ');
Операторы 115 Readln(Item); Sum: = Sum+Item; end в котором вводится значение очередного числа, и на это значение увеличивается значение суммы. После этого управление в программе вновь передается оператору цикла while, опять проверяется условие Count < Limit. Если условие верно, то вы- полняется составной оператор и т. д., пока значение переменной Count будет мень- ше 10. Как только значение Count станет равно 10 и условие Count < Limit не будет соблюдено, выполнение цикла завершится, а управление в программе будет пере- дано на оператор, црходящийся за словом end, т. е. первый оператор за границей while. Это вызов процедуры Writeln, которая выведет сообщение 'Сумма введенных чисел равна' и напечатает значение переменной Sum. Упражнение 4. Введите текст программы, откомпилируйте ее и исполните для разных значений чисел. Проверьте выполнение программы, пронаблюдав значения переменных Sum, Item и условия Count < Limit в режиме отладки "по шагам". Оператор повтора repeat. Оператор повтора repeat аналогичен оператору while, но отличается от него, во-первых, тем, что условие проверяется после оче- редного выполнения операторов тела цикла (очередной итерации) и таким образом гарантируется хотя бы однократное выполнение цикла, а во-вторых, тем, что крите- рием прекращения цикла является равенство выражения константе True. За это цикл repeat часто называют циклом с постусловием, или циклом "ДО", так как он прекращает выполняться, как только значение выражения условия, записанного после слова until, равно True (истина). Оператор повтора repeat состоит из заголовка repeat, тела и условия окончания until. Синтаксическая диаграмма для данного оператора выглядит следующим обра- зом: Формат записи: repeat <оператор;> <оператор> until <условие окончания цикла>;
116 Глава 6 Операторы, заключенные между словами repeat и until, являются телом цикла. Вначале выполняется тело цикла, затем проверяется условие выхода из цикла. Именно поэтому цикл, организованный с помощью оператора repeat, в любом случае выполнится хотя бы один раз. Если результат булевского выражения равен False, то тело цикла активизируется еще раз; если результат True, происходит вы- ход из цикла. При программировании операторов тела цикла следует обеспечить влияние по крайней мере одного из операторов тела цикла на значение условия, иначе цикл бу- дет выполняться бесконечно. В следующем фрагменте показано, как оператор repeat используется для ожи- дания нажатия клавиш Y и N. Нажатие других клавиш^будет игнорироваться: uses Crt; var YN: char; begin repeat YN: = ReadKey until Upcase(YN) in ['Y'j'N'J; end. Примером действия оператора repeat может служить программа DemoRepeat, которая вводит и суммирует любое количество целочисленных значений. Если вве- дено значение 999, то на экран выводится результат суммирования. program DemoRepeat; var X: integer; Sum: real; begin Sum: =0 ; repeat {Повторять} Write('Значение X= ’); / {Начало тела цикла} Readln(X); {Считать очередное значение X с клавиатуры} if X О 999 then Sum:» Sum+X; until X = 999; {Условие окончания цикла} Writein('Сумма введенных чисел» ',Sum); end.
Операторы 117 В данном примере в разделе описания переменных описана переменная X це- лочисленного типа integer и Sum вещественного типа real. В начале выполнения программы обнуляется значение суммы чисел. Затем зарезервированным словом repeat объявляется цикл, после чего следуют операторы тела цикла, которые выводят на экран запрос 'Значение Х= ', считывают введенное с клавиатуры значение X. Оператор if проверяет его на неравенство числу 999 и, если оно не равно 999, увеличивает значение суммы Sum на значение числа X. В конце цикла оператор until X = 999 проверяет условие окончания цикла. Если зна- чение выражения X = 999 истинно, то цикл завершится, а управление в программе будет передано на оператор, находящийся за словом until, т. е. первый оператор за границей цикла repeat. Это вызов процедуры Writein, которая выведет сообщение ‘Сумма введенных чисел равна’ и напечатает значение переменной Sum. Упражнение 5. Введите текст программы, откомпилируйте ее и исполните для разных значений чисел. Проверьте выполнение программы, пронаблюдав значения переменных X, Sum и условия Х=999 в режиме отладки "по шагам". Оператор повтора for. В случаях, когда число повторений может быть зара- нее известно, для организации циклической обработки информации применяется оператор повтора for. Часто этот оператор повтора называют оператором цикла с параметром, так как число повторений/Задается переменной, называемой пара- метром цикла, или управляющей перецённой. Оператор повтора for состоит из заголовка и тела цикла. Синтаксическая диаграмма для данного оператора выглядит следующим обра- зом: Как видно из диаграммы, он может быть представлен в двух форматах: for <параметр цикла> := <S1> to <S2> do <оператор>; for <параметр цикла> := <S1> downto <S2> do <оператор>; где SI и S2 — выражения, определяющие соответственно начальное и конечное значения параметра цикла; for ... do — заголовок цикла; <оператор> — тело цикла. Тело цикла может быть простым или составным оператором. Оператор for обеспечивает выполнение тела цикла до тех пор, пока не будут перебраны все зна- чения параметра цикла от начального до конечного.
118 Глава 6 Заголовок оператора повтора for определяет: • диапазон изменения значений управляющей переменной (параметра цикла) и одновременно число повторений оператора, содержащегося в теле цикла; • направление изменения значения параметра цикла (возрастание — to или убывание — downto). Пример. for I:= 1 to 100 do Read(M[IJ); {Чтение элементов массива} for 1:= 100 downto 1 do Write{Вывод элементов массива} При первом обращении к оператору for вначале вычисляются выражения S1, S2 и осуществляется присваивание <параметр цикла>:=81. После этого циклически повторяются следующие действия. 1. Проверяется условие <параметр цикла>:<=82. 2. Если условие выполнено, то оператор for продолжает работу (выполняется оператор в теле цикла), если условие <параметр цикла>:<=82 не выполнено, то оператор for завершает работу, и управление в программе передается на оператор, следующий за циклом. 3. Значение управляющей переменной изменяется на +1 (to) или —1 (downto) и далее с п. 1. Обратите внимание, что шаг изменения управляющей переменной — единица. На использование управляющей переменной (параметра цикла) в цикле for на- лагаются следующие ограничения. 1. В качестве параметра должна использоваться простая переменная, опи- санная в текущем блоке. 2. Управляющая переменная должна иметь дискретный тип. 3. Начальные и конечные значения диапазона должны иметь тип, совместимый с типом управляющей переменной. При этом допустим любой скалярный тип, кро- ме вещественного. 4. В теле цикла запрещается явное изменение значения управляющей перемен- ной (например, оператором присваивания). 5. После завершения оператора значение управляющей переменной становится неопределенным, если только выполнение оператора не было прервано оператором перехода. Примером действия оператора for может служить программа DemoFor, кото- рая выводит на экран таблицу перевода из градусов по шкале Цельсйя(С) в градусы по Фаренгейту(Р) для значений от 15°С до 30°С с шагом 1 градус. Перевод осуще- ствляется по формуле: F = С* 1.8+32.
Операторы 119 program DemoFor; var I: integer; F: real; begin Writein (' Температура '); for I:= 15 to 30 do (Заголовок цикла с параметром) begin (Начало тела цикла) F:= 1*1.8+32; Writein('По Цельсию^ по Фаренгейту^ F:5:2) end; (Конец тела цикла) end. В блоке описания переменных описаны параметр цикла I типа integer и пере- менная F — температура по Фаренгейту типа real. Переменная I, помимо функций управляющей переменной, является переменной, хранящей целочисленные значе- ния температуры по шкале Цельсия. В начале выполнения программы на экран вы- водится надпись ‘ Температура ‘, а затем оператором повтора выводится табли- ца соотношения температуры в шкалах Цельсия и Фаренгейта. Печать таблицы вы- полняется оператором Writein('По Цельсию= по Фаренгейту^ F:5:2) Цикл выполняется следующим образом. При первом обращении к оператору for вычисляются значения начального (15) и конечного (30) параметров цикла, и управляющей переменной I присваивается начальное значение 15. Затем циклически выполняется следующее: 1. Проверяется условие 1<=30. 2. Если оно соблюдается, то выполняется составной оператор в теле цикла, т. е. рассчитывается значение выражения I* 1.8+32, затем оно присваивается перемен- ной F, и на экран выводится сообщение 'По Цельсию= по Фаренгейту^ F:5:2 Если условие 1<=30 не соблюдается, т. е. как только I станет > 30, оператор те- ла цикла не выполняется, а управление в программе передается за пределы опера- тора for, в нашем примере на оператор end;. Программа завершает работу. 3. Значение параметра цикла I увеличивается на единицу, и управление переда- ется в заголовок цикла for для проверки условия. Далее цикл повторяется с п.1. Упражнение 6. Введите текст программы, откомпилируйте ее и исполните, проверив выполнение программы "по шагам". Используя окно просмотра, наблюдайте изменение зна-
120 Глава 6 чения управляющей переменной I и выражения 1<=30. Сравните значение выражения в процессе выполнения цикла и в момент завершения цикла. Вторым примером оператора цикла for может служить программа, которая пе- чатает на экране символы американского стандартного кода обмена информацией (ASCII) в порядке убывания кода. program DemoASCII; var A: integer; begin for A:= 255 downto 0 do (Цикл с убыванием параметра) Writein('код символа = ',A, ' символ = ',Chr(A)); end. * В данной программе применяется цикл for с убыванием значения управляющей переменной А (используется указание downto — убывание). Упражнение 7. Введите текст программы, откомпилируйте ее и исполните, проверив выполнение программы "по шагам". Обратите внимание на изменение значений управляю- щей переменной и значение выражения А>0. Как только значение выражения А>0 станет False, цикл for завершается. Рассмотрим пример программы, имитирующей арифметический калькулятор, в которой используются операторы присваивания, условия и повтора. program DemoCalc; var X,Y,Rezult : real; Operation, Ans : char; begin repeat {Начало цикла с постусловием) Write(’X = •); Read(X); (Считывание первого операнда) Write('Y = •); Readln(Y); (Считывание второго операнда) Writein('операция (+,—,*,/) >’); Readln(Operation); (Считывание знака операции) case Operation of {Выбор арифметического действия) •+' : Rezult := X+Y; : Rezult := X-Y; : Rezult := X*Y; ’/• : Rezult := X/Y; else Writein(* Ошибка ввода'); end; I
Операторы 121 Writeln(X,Operation,Y,Rezult); (Печать арифметического выражения} Write(* Продолжить (Y/N)'); Readln(Ans); (Считывание ответа на вопрос} Until (Ans='N’) or (Ans=’n’); (Проверка условия окончания цикла} end. В разделе описания переменных описаны переменные — операнды X, Y и ре- зультат арифметических операций Rezult вещественного типа, а также переменная Operation символьного типа, в которой хранится значение знака арифметической операции, и Ans — переменная символьного типа, которой присваивается значе- ние "Y" или "N". Процесс выполнения арифметических операций калькулятором организован с помощью оператора repeat и продолжается до тех пор, пока переменной Ans не бу- дет присвоено значение *N' или 'п'. В теле цикла сначала запрашиваются и считываются с клавиатуры значения операндов: Write('X = ') ; Read(X); Write('Y = '); Readln(Y); затем запрашивается и считывается знак арифметической операции Writeln('операция (+,—,*,/) >'); Readln(Operation); Оператор выбора case по значению переменной Operation (селектор) выбирает знак операции и в зависимости от его значения выполняет арифметическую опера- цию. Например, если значение переменной Operation равно значению констан- ты выбора '—*, то выполняется оператор присваивания Rezult := X—Y и т. д. Если значение переменной Operation не равно ни одному значению константы вы- бора, то управление передается на оператор, стоящий за словом else, и на экран вы- водится сообщение ‘Ошибка ввода’. Если знак операции был введен верно, то на экран выводится сообщение вида: 5 —3 = 2. После этого на экран выводится запрос ‘Продолжить (Y/N)’ и с клавиатуры считывается значение переменной символьного типа Ans. Если значение выраже- ния (Ans=tN') or (Ans-n') будет False, то цикл повторится вновь, иначе цикл будет завершен и управление в программе будет, передано на оператор end. Упражнение 8. Введите текст программы, откомпилируйте ее и исполните, проверив выполнение программы "по шагам". Обратите внимание на изменение значений селектора Operation и выполнение оператора выбора case, а также значение выражения (Ans=,N') or (Ans-n’) в процессе выполнения и по окончании цикла.
122 Глава 6 6.3.6. Вложенные операторы цикла Если телом цикла является циклическая структура, то такие циклы называют вложенными. Цикл, содержащий в себе другой цикл, называют внешним, а цикл, содержащийся в теле другого цикла, называют внутренним. Внешний и внутрен- ний циклы могут быть трех видов: циклами с предусловием while, циклами с посту- словием repeat или циклами с параметром for. Правила организации внешнего и внутреннего циклов такие же, как и для про- стого цикла каждого из видов. Но при программировании вложенных циклов необ- ходимо соблюдать следующее дополнительное условие: все операторы внутрен- него цикла должны полностью располагаться в теле внешнего цикла. Рассмотрим пример простой задачи, решение которой предполагает использо- вание вложенных циклов, — задачи вывода на экран таблицы умножения. С ис- пользованием цикла for вариант решения данной задачи может быть следующим: program Tab_Umnl; var I, J : byte; begin for I:=l to 10 do (Внешний цикл) for J:=l to 10 do (Внутренний цикл) Writein(I,' * ',J,' = (Тело внутреннего цикла} end. Проанализируем действие данной программы. В разделе описания переменных описываются переменные I, J целого типа byte, выполняющие функции управляю- щих переменных циклов for. Выполнение программы начинается с внешнего цикла. При первом обращении к оператору внешнего цикла for вычисляются значения начального (1) и конечного (10) параметров цикла и управляющей переменной I присваивается начальное зна- чение 1. Затем циклически выполняется следующее: 1. Проверяется условие 1<=10. 2. Если оно соблюдается, то выполняется оператор в теле цикла, т. е. выполня- ется внутренний цикл. 2.1. При первом обращении к оператору внутреннего цикла for вычис- ляются значения начального (1) и конечного (10) параметров цикла и управ- ляющей переменной J присваивается начальное значение 1. Затем циклически выполняется следующее: 2,2. Проверяется условие J<=10.
Операторы 123 2.3. Если оно удовлетворяется, то выполняется оператор в теле цикла, т. е. оператор Writeln(I,’ * ’,J,’ = ’,I*J), выводящий на экран строку таблицы ум- ножения в соответствии с текущими значениями переменных I и J. 2.4. Затем значение управляющей внутреннего цикла J увеличивается на единицу и оператор внутреннего цикла for проверяет условие J<=10. Ес- ли условие соблюдается, то выполняется тело внутреннего цикла при неиз- менном значении управляющей переменной внешнего цикла до тех пор, по- ка выполняется условие J<=10. Если условие J<=10. не удовлетворяется, т. е. как только J станет боль- ше 10, оператор тела цикла не выполняется, внутренний цикл завершается и управление в программе передается за пределы оператора for внутреннего цикла, т. е. на оператор for внешнего цикла. 3. Значение параметра цикла I увеличивается на единицу, и проверяется усло- вие 1<=10. Если условие 1<=10 не соблюдается, т. е. как только I станет больше 10, оператор тела цикла не выполняется, внешний цикл завершается и управление в программе передается за пределы оператора for внешнего цикла, т. е. на оператор end, и программа завершает работу. Таким образом, на примере печати таблицы умножения наглядно ввдно, что при вложении циклов изменение управляющей переменной вложенного цикла про- ходит полный цикл от начального до конечного значения при неизменном значе- нии управляющей переменной внешнего цикла, затем значение управляющей пере- менной внешнего цикла изменяется на единицу и опять изменение параметра внут- реннего цикла претерпевает изменения от начального до конечного значения с ша- гом, равным единице. И так до тех пор, пока значение параметра внешнего цикла не станет больше конечного значения, определенного в операторе for внешнего цикла. Упражнение 9. Введите текст программы Tab Umnl, откомпилируйте и исполните ее в режиме отладки "по шагам”. Наблюдая за значениями параметров циклов I и J и выражений 1<=10 и J<=10, проанализируйте действие операторов внешнего и внутреннего циклов. Упражнение 10. Изучите текст программы Tab_Umn2, объясните, чем он отличается от предыдущей программы. Объясните формат вывода значения произведения I*J на экран оператором Write(I*J:4). Каков будет результат исполнения программы? program Tab_Umn2; var I,J : byte; begin for I:=l to 10 do {Внешний цикл} begin for J:=l to 10 do {Внутренний цикл) Write(I*J:4);
124 Глава 6 Writeln; end; end. Введите текст программы Tab_Umn2, откомпилируйте и исполните ее в режиме отлад- ки "по шагам". Наблюдая за значениями параметров циклов I и J и выражений 1<=10 и J<=10, проанализируйте действие операторов внешнего и внутреннего циклов. Упражнение 11. Проанализируйте текст программы ввода натуральных чисел и печати на экране тех из них, которые являются совершенными. (Совершенное число — число, равное сумме всех своих делителей, исключая себя самого. Например, число 6 — совершен- ное, так как оно равно сумме всех своих делителей: 1+2+3.) Ввод числа нуль означает конец ввода. program Demo_Sover; (Определение совершенных чисел} var S,N,I,J : integer; begin repeat (Начало внешнего цикла} Write('Введите значение натурального числа >'); Read(N); S:=0; (Обнуление значения суммы) for I:=l to N—1 do (Внутренний цикл: поиск и суммирование делителей) if N mod 1 = 0 then S:=S+I; (Если число N без остатка делится на I, то число I — делитель числа N, поэтому увеличить значение S на величину значения I} if N=S then Writeln('Совершенное число ',N:6) else Writeln('Несовершенное число ',N:6); until N=0; (Условие окончания внешнего цикла} end. Загрузите интегрированную среду программирования, введите текст программы Demo Sover, откомпилируйте и исполните ее в режиме отладки "по шагам". Наблюдая за значениями параметров циклов I и S и выражений I<=N—1 и N=0, проанализируйте дейст- вие операторов внешнего и внутреннего циклов. Контрольные данные: для N от 1 до 1000 совершенными являются числа 6,28 и 496. 6.4. ПРАВИЛА ПУНКТУАЦИИ При записи операторов необходимо соблюдать следующие правила пунктуа- ции: 1. Точка с запятой не ставится в разделах описаний после зарезервированных слов unit, uses, label, type, const, var и ставится после завершения каждого описа- ния. 2. Точка с запятой не ставится после begin и перед end, так как эти слова явля- ются операторными скобками, а не операторами.
Операторы 125 3. Точка с запятой является разграничителем операторов, ее отсутствие между операторами вызывает ошибку компиляции. 4. В операторах цикла точка с запятой не ставится после while, repeat, do и пе- ред until. 5. В условных операторах точка с запятой не ставится после then и перед else. Контрольные вопросы и задания Вопросы. 1. Что такое оператор? Чем отличаются простые и структурные операторы? 2. Оператор присваивания, назначение и порядок выполнения. 3. Оператор безусловного перехода, его назначение и особенности применения. 4. Назначение оператора вызова процедуры. 5. В чем особенности пустого оператора? Его назначение? 6. Что представляет собой составной оператор? Как ограничиваются операторы, объе- диненные в составной оператор? 7. Назначение, формы записи и порядок выполнения оператора условия if. 8. Особенности использования вложенных условных операторов. 9. Зачем нужна отладка программ? Какие возможности для отладки программ преду- смотрены в интегрированной среде программирования? 10. Каковы отличия оператора выбора case от оператора условия if? ! 11. Какие правила должны выполняться при использовании оператора выбора case? 12. Как оперативно получить подсказку по языку программирования в интегрированной среде программирования? 13. Каково назначение операторов повтора (цикла)? 14. Какие требования предъявляются к выражениям, управляющим повторениями? 15. В чем отличия операторов повтора while и repeat? 16. В каких случаях предпочтительнее использовать для организации циклов оператор повтора for? Что записывается в заголовке этого оператора? 17. Каким образом в операторе цикла for описывается направление изменения значения параметра цикла? 18. Какие ограничения налагаются на использование управляющей переменной (пара- метра цикла) в цикле for? 19. Какие правила пунктуации необходимо соблюдать при записи операторов? 20. Что такое вложенные циклы? Какие дополнительные условия необходимо соблю- дать при организации вложенных циклов? Задания. 1. Составьте программу определения стоимости набора конфет, в который входят сле- дующие сорта:
126 Глава 6 Наименование Количество Цена, руб. Наименование Количество Цена, руб. Красная шапочка 500г к Воронежские 100 г b Алые паруса 200 г а Чародейка 250 г V 2. Даны круг и квадрат. Составьте программу, определяющую по введенным вами зна- чениям длин стороны квадрата и радиуса круга, верно ли утверждение "Круг нписан в квад- рат!'. (Используйте логическую величину REZULT, принимающую значение TRUE, если ут- верждение истинно, и значение FALSE, если утверждение ложно.) 4. Составьте программу вычисления суммы цифр введенного с клавиатуры трехзначно- го натурального числа. Например, для числа 128 сумма цифр 11, для числа 345 сумма цифр 12. 5. Составьте программу, вычисляющую по введенному вами значению текущего време- ни (часов, минут, секунд) угол (в градусах) между положением часовой стрелки в начале су- ток и ее положением в текущее время. Например, если текшее время составляет 3 ч 30 мин 00 с, то этот угол составит 105°. 108'. 6. Напишите программу-модель анализа пожарного датчика в помещении, которая вы- водит сообщение Пожароопасная ситуация', если температура (в нашей модели она будет вводиться с клавиатуры) в комнате превысила 60°С. 7. Составьте программу, которая из двух вводимых вами целых чисел печатает заклю- чение о том, какое число больше. 8. Рис расфасован в два пакета. Вес первого — m кг, второго — п кг. Составьте про- грамму, определяющую: а) какой пакет тяжелее — первый или второй? б) определите вес более тяжелого пакета.. 9. Составьте программу, проверяющую, верно ли утверждение, что введенное вами це- лое число является четным. 10. Составьте npoipaMMy, проверяющую, верно ли утверждение, что введенное вами целое число делится без остатка на 3. 11. Напишите программу, которая анализирует человека по возрасту и относит к одной из четырех групп: дошкольник, ученик, работник, пенсионер. Возраст вводится с клавиату- ры. 12. Составьте программу, определяющую, входит ли введенная вами цифра в десятич- ную запись введенного вами трехзначного числа, и печатающую сообщение о том, входит ли эта цифра в запись числа или нет. 13. Составьте программу, определяющую, лежит ли точка с указанными координатами X, Y на окружности радиуса R с центром в начале координат. 14. Составьте программу, определяющую, пройдет ли график функции у=5х2—7х+2 че- рез заданную точку с координатами (а,Ь). 15. К финалу конкурса лучшего по профессии "Специалист электронного офиса" были допущены трое: Иванов, Петров, Сидоров. Соревнования проходили в три тура. Иванов в первом туре набрал ml баллов, во втором — nl, в третьем — pl. Петров — соответственно m2, п2, р2; Сидоров — m3, пЗ, рЗ. Составьте программу, определяющую, сколько баллов на- брал победитель.
Операторы 127 16. Составьте программу, которая по трем введенным вами числам определит, могут ли эти числа быть длинами сторон треугольника, и если да, то какой получится треугольник с данными длинами сторон (прямоугольный, остроугольный, тупоугольный). 17. Квадраты при игре в крестики-нулики занумерованы, как показано на рисунке. Зада- ны номера трех квадратов: Nl, N2, N3, причем N1<N2<N3. Проверить, ле- х I 2 I з жат ли квадраты: а) на одной диагонали; 4 5 6 б) на одной вертикали; 7 8 9 в) на одной горизонтали. ----------- 18. Напишите программу-фильтр, которая при нажатии любых клавиш выводит на эк- ран только буквы и цифры, при этом указывая, что выводится: буква или цифра. 19. Напишите программу, которая по паролю будет определять степень доступности со- трудника к секретной информации в базе данных. Доступ к базе имеют только шесть чело- век, разбитых на три группы по степени доступа. Они имеют следующие пароли: 9583, 1747 — доступны модули базы А, Б, С 3331, 7922 — доступны модули базы Б, С 9455, 8997 — доступен модуль базы С 20. Составьте программу, реализующую эпизод применения компьютера в книжном магазине. Компьютер запрашивает стоимость книг, сумму денег, внесенную покупателем; если сдачи не требуется, печатает на экране "спасибо"; если денег внесено больше, то печа- тает "возьмите сдачу," и указывает сумму сдачи; если денег недостаточно, то печатает об этом сообщение и указывает размер недостающей суммы. 21. В ЭВМ поступают результаты соревнований по плаванию для трех спортсменов. Составьте программу, которая выбирает лучший результат и выводит его на экран с сообще- нием, что это результат победителя заплыва. 22. Составьте программу, которая по введенному вами к — числу грибов печатает фразу "Мы нашли в лесу к грибов", причем согласовывает окончание слова "гриб" с числом к (Количество грибов может быть любым целым числом: 1, 3, 34, 127 и т. п. Окончание фразы определяется значением последней цифры.) 23. Составьте программу, которая для целого числа к (от 1 до 99), введенного вами, напечатает фразу "Мне к лет", где к — введенное число, при этом в нужных случаях слово "лет" заменяя на слово "год" или "года". (Например: при к=70 "Мне 70 лет", при к=15 "Мне 15 лет", при к=23 "Мне 23 года" и т.п.) 24. Составьте программу для вычисления числа дней в месяце, если даны: номер меся- ца N — целое число от 1 до 12, целое число А, равное 1 для високосного года и 0 в против- ном случае. 25. Составьте программу, которая вычисляет сумму чисел от 1 до N. Значение N (N должно быть меньше 100) вводится с клавиатуры. 26. Напишите протрамму печати таблицы перевода расстояний из дюймов в сантимет- ры (1 дюйм = 2,5 см) для значений длин от 1 до 20 дюймов. 27. С помощью while напишите программу вывода всех четных чисел в диапазоне от 2 до 100 включительно.
128 Глава 6 28. Составьте и отладьте программу, вычисляющую сумму квадратов чисел от 1 до введенного вами целого числа п. 29. С помощью while напишите программу определения суммы всех нечетных чисел в диапазоне от 1 до 99 включительно. 30. С помощью цикла while напишите программу определения идеального веса для взрослых людей по формуле: Ид.вес = рост—100. Выход из цикла: значение роста = 250. 31. С помощью repeat напишите программу-фильтр, которая вводит любые символы, но! комментирует только буквы русского алфавита. Завершение работы программы — по нажа- тии буквы "Я". 32. С помощью repeat напишите программу, которая требует у вас пароль^ например 111, и если пароль правильный, то заполняет все строки экрана сообщением "Молодец!!!". Если после пятой попытки пароль все равно неверен, выйти из программы. 33. Составьте программу получения в порядке убываем всех делителей данного числа. 34. Составьте программу определения наибольшего общего делителя двух натуральных чисел. 35. Составьте программу определения наименьшего общего кратного двух натуральных чисел. 36. Составьте программу, подсчитывающую количество цифр вводимого вами целого неотрицательного числа. (Можно использовать операцию целочисленного деления для по- следовательного уменьшения числа на один разряд.) 37. Составьте и отладьте программу, определяющую максимальное из всех введенных вами чисел. (Пусть признаком конца ввода чисел является введенное число 0.) 38. Найти наибольшее й наименьшее значение функции у=3х2+х—4, если на заданном интервале [а,Ь] х изменяется с шагом 0,1. 39. Вычислите сумму квадратов N четных натуральных чисел. 40. Вычислить: а) 1+2+4+8+...+210 б) (1+2)*(1+2+3)*...*(1+2+...+10) 41. В бригаде, работающей на уборке сена, имеется N косилок. Первая из них работа- ла m ч., а каждая следующая на 10 мин. больше, чем предыдущая. Сколько часов прорабо- тала вся бригада? 42. Билет называют "счастливым", если в его номере сумма первых трех цифр равна сумме последних трех. Подсчитать число тех "счастливых" билетов, у которых сумма трех цифр равна 13. 43. В ЭВМ вводятся по очереди координаты N точек. Определить, сколько из них попа- дает в круг радиусом R с центром в точке (а,Ь). 44. В ЭВМ вводятся по очереди данные о росте N учащихся класса. Определить сред- ний рост учащихся класса. . 45. Составьте программу, суммирующую штрафное время команд при игре в хоккей. Выводить на экран суммарное штрафное время обеих команд после любого его измене- ния.После окончания игры выдать итоговое сообщение.
Операторы 129 46. Составьте программу вычисления степени числа а с натуральным показателем п. (Записать варианты программы с разными видами циклов while, repeat, for). 47. Составьте программу вычисления суммы всех двузначных чисел. 48. Составьте программу вычисления факториала натурального числа п. Факториалом (п!) натурального числа п называется произведение всех чисел от 1 до п, включая п. 49. Для заданного числа п составьте программу вычисления суммы S=l+l/2+l/3+l/4+...+l/n, где п — натуральное число. 50. Для заданного п составьте программу вычисления суммы S=(— l)i+1/(2*i— 1), где i=l,2,3,...,n. 51. Дана последовательность: 1; 1+1/2; 1+1/2+1/3; 1+1/2+1/3+1/4;...; l+l/2+...+l/n. Со- ставьте программу, вычисляющую первый член последовательности, превосходящий задан- ное число а. 52. Каждая бактерия делится на две в течение одной минуты. В начальный момент име- ется одна бактерия. Составьте программу, которая рассчитывает количество бактерий на за- данное вами целое значение момента времени (15 мин, 7 мин и т.п.). 53. Составьте программу вывода на экран всех простых чисел, не превосходящих за- данного N. (Простым называется натуральное число больше единицы, имеющее только два делителя: единицу и само это число.) 54. В 1202 г. итальянский математик Леонард Пизанский (Фибоначчи) предложил та- кую задачу: пара кроликов каждый месяц дает приплод — двух кроликов (самца и самку), от которых через два месяца уже получается новый приплод. Сколько кроликов будет через год, если в начале года имелась одна пара? Согласно условию задачи числа, соответствую- щие количеству кроликов, которые появляются через каждый месяц, составляют последова- тельность 1,1,2,3, 5, 8,13,21,34,... Составьте программу, позволяющую найти все числа Фибоначчи, меньшие заданного числа N. 55. Составьте программу, которая выводит полную запись десятичного числа 42*4*, в которой пропущены две цифры, если известно, что данное число кратно 72. s 56. В старояпонском календаре был принят 60-летний цикл, состоявший из пяти 12-лет- них подциклов. Подциклы обозначались названиями цвета: зеленый, красный, желтый, бе- лый и черный. Внутри каждого подцикла годы носили названия животных: крысы, коровы, тигра, зайца, дракона, змеи, лошади, овцы, обезьяны, курицы, собаки и свиньи. (1984 г. — ' год зеленой крысы — был началом очередного цикла.) Напишите программу, которая вво- дит номер некоторого года нашей эры и печатает его название по старояпонскому кален- дарю. 57. Составьте программу, которая печатает все натуральные числа меньше 100, для ко- торых f(x)=n, если значение функции f(n) равно количеству букв в записи числа п русски- ми буквами, например: f(l)=4, так как в слове “один” четыре буквы; f(45)=9, так как в запи- ; си числа “сорок пять” девять букв. \ 5-116
Глава 7. ПРОЦЕДУРЫ И ФУНКЦИИ 7.1. НЕОБХОДИМОСТЬ СТРУКТУРИЗАЦИИ В ПРОГРАММИРОВАНИИ Развитие индустрии создания программных средств и все более широкое ис- пользование различных программ для удовлетворения информационных потребно- стей человека существенно повышают требования к надежности программного из- делия, т. е. к уменьшению числа оставшихся нёвыявленных ошибок в программе и таких неучтеных ситуаций, при возникновении которых программа может выдать неопределенный результат или прекращает свое нормальное функционирование. Значительное увеличение сложности задач, решаемых с помощью ЭВМ, при- водит к увеличению размеров и сложности программ, что порождает дополнитель- ные трудности при их разработке и отладке. Увеличение продолжительности жизненного цикла программ приводит к тому, что с течением времени из-за изме- нения условий использования программ возникает необходимость их модифика- ции, повышения их эффективности, удобства пользования ими. Для разрешения возникших при этом проблем в практике программирования выработан ряд приемов и методов, которые принято называть методами структур- ного программирования. Под структурным программированием понимают такие методы разработки и записи программы, которые ориентированы на максимальные удобства для воспри- ятия и понимания ее человеком. При прочтении программы в ее следующих друг за другом фрагментах должна четко прослеживаться логика ее работы, т. е. не должно быть "скачков" на фрагменты программы, расположенные где-то в другом месте программы. Структурное программирование — “программирование без go to”, т. е. не ис- пользуются операторы перехода без особой необходимости. В связи с этим отдель- ные фрагменты программы представляют собой некоторые логические (управляю- щие) структуры, которые определяют порядок выполнения содержащихся в них правил обработки данных. Любая программа получается построенной из стандарт- ных логических структур, число типов которых невелико.
Процедуры и функции 131 Основные логические структуры описаны нами ранее: следование последовательность операторов, групп операторов, выполняе- мых друг за другом в порядке их следования в тексте программы; ветвление — управляющая структура, которая в зависимости от выполнения заданного условия определяет выбор для исполнения одного из двух или более за- данных в этой структуре групп операторов; повторение — цикл, в котором группа операторов может выполняться по- вторно, если соблюдается заданное условие. Существенная особенность всех этих структур — то, что каждая из них имеет только один вход и только один выход, что и обеспечивает логически последова- тельную структуру программы. Все эти структуры определяются рекурсивно, т. е. каждая из входящих в структуру групп операторов может быть одним оператором, группой операторов и может быть любой из допустимых структур — допускается вложение структур. Так как программа задает правила обработки данных, то проектирование самих данных при изготовлении программы имеет не менее важное значение, чем проек- тирование правил их обработки. Очевидно, что, чем четче определены сами дан- ные, тем легче разрабатывать правила их обработки. Простота и надежность про- граммы существенно зависят рт того, насколько удобно отдельные обрабатывае- мые данные объединены в некоторые структуры. При этом язык программирования Паскаль требует от программиста четкого описания вводимой в употребление структуры данных, что позволяет транслятору обеспечивать работу с каждой такой структурой и следить за корректностью ее использования. Для того чтобы возложить на транслятор контроль за корректностью использо- вания в программе различных типов данных, в Паскале требуется описывать кон- станты и переменные перед их применением с указанием их типа. Чтобы эти описа- ния было легче использовать транслятору или программисту, Паскаль требует чет- кой структуризации программы, отводя для различного рода информации строго отведенное место (см структуру программы). Метод нисходящего проектирования программ. С массовым внедрением вычислительной техники процесс программирования постепенно превращается в промышленное изготовление программ. Для этой цели создаются разнообразные технологии программирования. Примерами таких технологий могут служить тех- нология нисходящего программирования и технология восходящего программиро- вания. Технология нисходящего программирования базируется на методе програм- мирования "сверху-вниз". Часто этот метод называют методом пошаговой детали- зации. Большинство специалистов в области программирования придерживаются той точки зрения, что именно этот метод создает предпосылки к решению сложных проблем. Основой такого метода является идея постепенной декомпозиции исход-
132 Глава? ной задачи на ряд подзадач. Сначала формулируется самая грубая модель реше- ния, отдельные детали которой на первом этапе могут быть довольно расплывчаты- ми (как вид какого-либо участка земли с большой высоты, в котором неразличимы мелкие подробности). По мере разработки программы, разбивая наиболее неясные части алгоритма и добиваясь все более точных и детализированных формулировок, мы получаем более подробное решение, как бы опускаемся с большой высоты ни- же и начинаем при этом различать более мелкие детали. Решение отдельного фраг- мента сложной задачи может представлять собой самостоятельный программный блок, называемый подпрограммой. Такой процесс детализации продолжается до тех пор, пока не станут ясны все детали решения задачи. В этом случае программу решения сложной задачи можно представить как иерархическую совокупность от- носительно самостоятельных фрагментов — подпрограмм. Таким образом, подпрограммой называют обособленную, оформленную в ви- де отдельной синтаксической конструкции и снабженную именем часть програм- мы. Использование подпрограмм позволяет, сосредоточив в них подробное описа- ние некоторых операций, в остальной программе только указывать имена подпро- грамм, чтобы выполнить эти операции. Такие вызовы подпрограммы возможны не- однократно из разных участков программы, причем при вызове подпрограмме мож- но передать некоторую информацию (различную в разных вызовах), чтобы одна и та же подпрограмма выполняла решение подзадачи для разных случаев. Понятие подпрограммы как обособленной именованной части программы со своими собственными объектами (константами, переменными и т.п.) является во многих языках программирования основным средством структурирования про- грамм. Современные подходы к разработке программ поощряют явное оформление в виде подпрограммы любого достаточно самостоятельного и законченного про- граммного фрагмента. 7.2. ПОДПРОГРАММЫ В ЯЗЫКЕ ПАСКАЛЬ За наличие подпрограмм как средства структурирования программ язык про- граммирования Турбо Паскаль называется процедурно-ориентированным. Подпрограммы в Турбо Паскале реализованы посредством процедур и функ- ций. Имея один и тот же смысл и аналогичную структуру, процедуры и функции различаются назначением и способом их использования. Процедура — это независимая именованная часть программы, которую можно вызвать по имени для выполнения определенных действий. Структура процедуры повторяет структуру программы. Процедура не может выступать как операнд в вы- ражении. Упоминание имени процедуры в тексте программы приводит к активиза- ции процедуры и называется ее вызовом. Например, Read(F) читает с клавиатуры некоторое значение и присваивает его переменной F, Delay(5) вызывает задержку выполнения программы на 5 мс.
Процедуры и функции 133 Функция аналогична процедуре, но имеются два отличия: функция передает в точку вызова скалярное значение; имя функции может входить в выражение как операнд. Например, функция Chr(65) возвратит в точку вызова символ А (код ASCII — 65), Sqr(X) — возведет в квадрат значения целого или вещественного X и возвратит в точку вызова вычисленное значение квадрата числа X. Итак, отличие подпрограмм-процедур от подпрограмм-функций состоит в том, Что процедуры служат для задания совокупности действий, направленных на изме- нение внешней по отношению к ним программной обстановки, а функции, являясь частным случаем процедур, отличаются от них тем, что они обязательно возвраща- ют в точку вызова основной программы единственный результат как значение име- ни этой функции. Все процедуры и функции языка Турбо Паскаль делятся на две группы: встро- енные (стандартные) и определенные пользователем. Первые входят в состав языка и вызываются для выполнения по строго фиксированному имени. Вторые разраба- тываются и именуются самим пользователем. Все стандартные средства располо- жены в специализированных библиотечных модулях, которые имеют системные имена. 7.2.1. Стандартные библиотечные модули В систему Турбо Паскаль версии 6.0 и старше включены восемь модулей: System, Crt, Dos, Graph, Graph3, Overlay, Printer, ТурбоЗ и специализированная библиотека Турбо Vision. Модуль System подключается по умолчанию, все осталь- ные должен подключать программист с помощью зарезервированного слова uses. Например: uses Crt, Dos, Printer; Рассмотрим кратко назначение каждого из модулей. System — сердце Турбо Паскаля; содержащиеся в нем подпрограммы обеспе- чивают работу всех остальных модулей системы. Crt — содержит средства управления дисплеем и клавиатурой компьютера. Dos — включает средства, позволяющие реализовывать различные функции DOS. Graph3 — поддерживает использование стандартных графических подпро- грамм версии Турбо Паскаль 3.0. Overlay — содержит средства организации оверлейных программ. Printer — обеспечивает быстрый доступ к печатающему устройству. ТигЬоЗ — обеспечивает максимально возможную совместимость с версией Турбо Паскаль 3.0. Graph — содержит пакет графических средств, обеспечивающих эффективную работу с адаптерами CGA, EGA, VGA, HERC, IBM 3270, MCGA и ATT6300.
134 Глава? Турбо Vision — библиотека объектно-ориентированных подпрограмм для раз- работки пользовательских интерфейсов. 7.2.2. Встроенные функции и процедуры Модуль System подключается к программе автоматически, поэтому его имя не указывается в разделе uses. По этой причине программе становятся доступны его встроенные процедуры и функции. Арифметические процедуры и функции. Abs(X:real/integer):real/integer — вычисление абсолютной величины X. Тип ре- зультата совпадает с типом параметра. ArcTan(X:real):real — вычисление угла, тангенс которого равен X радиан. Cos(X:real) : real — вычисление косинуса X; параметр задает значение угла в радианах. Exp(X:real): real — вычисление экспоненты X, т. е. значение Е в степени X. Е является основанием натурального логарифма и равно 2.718282. Frac(X:real):real — вычисление дробной части X. Int(X:real):real — вычисление целой части X. Ln(X:real):real — вычисление натурального логарифма X, т. е. логарифма по основанию е (е = 2.718282). Pi:real — возвращает значение числа Пи (3.141592653897932385), Sin(X:real):real — вычисление синуса X. Параметр задает значение угла в ра- дианах. Sqr(X) — возведение в квадрат значения целого или вещественного значения X. Тип результата совпадает с типом параметра. Sqrt(X:real):real — вычисление квадратного корня из X. Randonrreal — генерирует значение случайного числа из диапазона 0..0.99. Random(I:word):word — генерирует значение случайного числа из диапазона 0..I. Randomize — изменение базы генератора случайных чисел. Скалярные процедуры и функции. Dec(X{,n}) — процедура уменьшает значение целочисленной переменной X на величину п. При отсутствии необязательного параметра п значение X уменьшается на единицу. Inc(X{,n}) — процедура увеличивает значение целочисленной переменной X на п. При отсутствии необязательного параметра п значение X увеличивается на единицу. Pred(S) — функция возвращает элемент, предшествующий S в списке значений типа. Тип результата совпадает с типом параметра. Если предшествующего S эле- мента не существует, возникает программное прерывание.
Процедуры и функции 135 Succ(S) — функция возвращает значение, следующее за S в списке значений типа. Тип результата совпадает с типом параметра. Если следующее за S значение отсутствует, возникает программное прерывание. Odd(I:integer):boolean — возвращает True, если I нечетное, и False, если I чет- ное. Функции преобразования типов. Chr(I:byte):char — возвращает символ стандартного кода обмена информацией с номером, равным значению I. Если значение параметра больше 255, возникает программное прерывание. Ord(S):longint — возвращает порядковый номер значения S в множестве, опре- деленном типом S. < Round(X:real):longint — возвращает значение X, округленное до ближайшего целого числа. Trunc(X:real):longint — возвращает ближайшее целое число, меньшее или рав- ное X, если X >= 0, и большее или равное X, если Х< Q. Процедуры управления программой. Delay(I:word) — задержка выполнения программы на I мс. Exit — выход из выполняемого блока в окружающую среду. Если текущий блок является процедурой или функцией, выход производится во внешний блок. Если Exit указана в операторной части основной программы, программа прекраща- ет работу, и управление передается системе программирования. Halt(N:word) — прекращение выполнения программы и передача управления системе программирования (если выполнялся .PAS-файл) или DOS (если выпол- нялся .ЕХЕ-файл). N — код завершения программы, передаваемый в операцион- ную систему. RunError(ErrCode:word) — прекращение выполнения программы и генерация ошибки времени выполнения. ErrCode — параметр типа byte, содержащий номер ошибки. Специальные процедуры и функции. FillChar(P,Dl,Z) — заполняет побайтно область основной памяти заданным зна- чением (заполнителем). Является одной из самых быстродействующих процедур. Область начинается с первого байта указанной переменной Р и имеет размер, за- данный параметром DI. Р — переменная любого типа; D1 — целочисленное выра- жение, указывающее длину; Z — заполнитель, выражение литерного или байтового типа. Move(Pl,P2,Dl) — пересылает содержимое основной памяти, начиная с перво- го байта переменной Р1, в область, которая начинается с первого байта переменной Р2. Длина областей определяется параметром DI. Р1 и Р2 — переменные любого типа; D1 — целочисленное выражение.
136 Глава 7 Hi(I:integer):byte — выделяет старший байт значения I и помещает его в млад- ший байт результата. Старший байт результата равен 0. Lo(I:integer):byte — выделяет младший байт значения I и помещает его в млад- ший байт результата. Старший байт результата равен 0. ParamCount: string — возвращает число параметров, переданных программе в командной строке. ParamStr (n:word) : string — возвращает указанный параметр командной стро- ки. SizeOf(IT):word — вычисляет объем основной памяти в байтах, которую зани- мает указанная переменная или тип. ГТ — идентификатор переменной или типа данных. Swap(I:integer):integer — обменивает содержимое младшего и старшего байтов целочисленного выражения, заданного параметром I типа integer. Вызов стандартной процедуры или функции. Ранее мы уже рассматривали примеры программ, в которых использовались некоторые стандартные процедуры и функции. Для использования стандартной процедуры или функции к программе подключается тот или иной специализированный библиотечный модуль, в котором записана данная стандартная процедура или функция (исключение составляет мо- дуль System, так как он подключается к программе автоматически), для чего имя специализированного библиотечного модуля указывается в разделе uses. Затем в программе записывается вызов процедуры или функции, для чего записывается ее имя и указываются фактические параметры, например: Pi, Sin(X), Chr(125), Inc(X,5). Так как после выполнения функции ее значение присваивается имени, то имя функции используется в выражении. 7.3. ПРОЦЕДУРЫ И ФУНКЦИИ ПОЛЬЗОВАТЕЛЯ Если в программе возникает необходимость частого обращения к некоторой группе операторов, выполняющих действия или вычисляющих значение какого-ли- бо выражения, то рационально сгруппировать такую группу операторов в само- стоятельный блок, к которому можно обращаться, указывая его имя. Такие разра- ботанные программистом самостоятельные программные блоки называются под- программами пользователя. Они являются основой модульного программирова- ния. Разбивая задачу на части и формируя логически обособленные модули как процедуры и функции, программист реализует основные принципы широко ис- пользуемого в практике системного подхода и методов нисходящего программиро- вания. При вызове подпрограммы (процедуры или функции), определенной програм- мистом, работа главной программы на некоторое время приостанавливается и на- чинает выполняться вызванная подпрограмма. Она обрабатывает данные, передан- ные ей из главной программы. По завершении выполнения подпрограмма-функция
Процедуры и функции 137 возвращает главной программе результат (подпрограмма-процедура не возвращает явно результирующего значения). Передача данных-из главной программы в подпрограмму и возврат результата выполнения функции осуществляются с помощью параметров. Параметром называется переменная, которой присваивается некоторое зна- чение в рамках указанного применения. Различают формальные параметры — па- раметры, определенные в заголовке подпрограммы, и фактические параметры — выражения, задающие конкретные значения при обращении к подпрограмме. При обращении к подпрограмме ее формальные параметры замещаются фактическими, переданными из главной программы. Механизм передачи параметров рассмотрим далее. t 7.3.1. Процедуры Описание процедуры включает заголовок (имя) и тело процедуры. Заголовок состоит из зарезервированного слова procedure, идентификатора (имени) процеду- ры и необязательного, заключенного в круглые скобки, списка формальных пара- метров с указанием типа каждого параметра. Имя процедуры — идентификатор, уникальный в пределах программы. Тело процедуры представляет собой локаль- ный блок, по структуре аналогичный программе. Общая структура описания проце- дур и функций иллюстрируется следующими синтаксическими диаграммами. Описание процедуры: ----) заголовок процедуры тело процедуры Описание функции: ——I заголовок функции Заголовок процедуры:
138 Глава? Заголовок функции: Описания меток, констант, типов и т. д. действительны только в пределах дан- ной процедуры. В теле процедуры можно использовать любые глобальные кон- станты и переменные. procedure <имя> (Формальные параметры);4 const ...; type . . . ; var ...; begin <операторы> end; В качестве примера опишем процедуру, которая прерывает выполнение про- граммы и выдает соответствующее сообщение об ошибке: procedure Abort(Msg: string); begin Writein('Ошибка: Msg); Halt(l); end; В данной процедуре пользователя использована переменная Msg типа string, в которой хранится текст сообщения о характере ошибки, вызвавшей прерывание программы. Для прерывания выполнения программы используется стандартная процедура Halt из стандартного библиотечного модуля System. Процедура не может выполниться сама, ее необходимо вызвать по имени и указать фактические параметры того же типа, что и формальные. Количество и тип формальных параметров равны количеству и типу фактических параметров. В ка- честве примера приведем фрагмент программы, в котором используется описанная выше процедура Abort: program DemoProc;{Подсчет суммы десяти введенных целых положитель- ных чисел: если будет введено отрицательное число, прервать выпол- нение } const Limit = 10; {Ограничение на количество вводимых чисел} var
Процедуры и функции 139 Count, Item, Sum : integer; {$1 ABORT.PAS } (Директива компилятору на включение текста про- граммы ABORT.PAS в качестве подпрограммы} begin Count:= 0; ( Sum:= 0; while (Count < Limit) do (Условие выполнение цикла} begin Count:= Count+1; Write('Введите ', Count, e целое число: '); Readln(Item); if Item < 0 then (Если введено отрицательное число} Abort('Ведено отрицательное число'); {Вызов процедуры} Sum:— Sum+Item; end; Writein('Сумма введенных чисел равна ', Sum); end. В разделе описания программы описываются константа Limit, ограничиваю- щая количество вводимых чисел; в разделе описания переменных описываются пе- ременные Count, Item, Sum типа integer. Затем в блоке описания записана директи- ва компилятору {SI ABORT.PAS}, указывающая, что при компиляции данной про- граммы в нее нужно включить в качестве процедуры программу ABORT.PAS. В начале программы обнуляются значения количества введенных чисел Count и их сумма Sum. Потом выполняется цикл, пока очередное вводимое число меньше предельного, заданного значением константы Limit. Сначала устанавливается но- мер очередного числа, затем на экран выводится приглашение "Введите 1—е (2—е и т.п.) число", считывается значение числа с клавиатуры в переменную Item. Затем проверяется условие Item<0. Если условие выполняется, то вызывается внешняя процедура Abort, которой передается фактический параметр-значение “введено отрицательное число”. Это значение присваивается формальному параметру Msg процедуры Abort. Процедура Abort выводит на экран сообщение “Ошибка: и печатает текст сообщения — значе- ние параметра Msg “введено отрицательное число”, после чего вызывает стандарт- ную процедуру Halt(l), которая прерывает выполнение программы. Если условие Item<0 не выполняется, то значение суммы Sum увеличивается на значение введенного числа Item, и управление передается в заголовок цикла для проверки условия Count < Limit. Если условие соблюдается, то тело цикла выпол- няется еще раз, иначе цикл завершается, а управление в программе передается на оператор, следующий за циклом, т. е. за резервированным словом end;, обозначаю- щим окончание составного оператора в теле цикла. После этого на экран выводится
140 Глава 7 сообщение “Сумма введенных чисел равна” и печатается значение переменной Sum. На этом выполнение программы завершается. Упражнение 1. Запустите интегрированную среду программирования. Введите в пер- вое окно редактора текст программы DemoProc, а во второе окно — текст процедуры Abort и запишите файлы на диск под соответствующими именами. Примечание. Чтобы открыть новое окно в пункте File главного меню интегрированной среды программирования, выберите опцию New. Для перехода из одного окна в другое на- жимайте F6. Откомпилируйте файл DemoProc. Если появятся сообщения об ошибке, внеси- те исправления и откомпилируйте вновь. После того как компиляция выполнится успешно, задайте для просмотра в окне отладчика величины Sum, Item и выраже- ние Count < Limit. Для того чтобы одновременно нА экране наблюдать окна с тек- стами программы и процедуры, а также окно просмотра значений переменных в пункте Window главного меню интегрированной среды, задайте опцию Tile. На- жатием клавиши F7 запустите программу на исполнение в пошаговом режиме с за- ходом в процедуры. Экран компьютера будет выглядеть, как показано на рис. 7.1. Рис. 7.1. Вид экрана компьютера с окнами программы, процедуры и просмотра Нажимая клавишу F7, наблюдайте за значениями переменных и выражениями в окне просмотра. В ответ на запрос "Введите число" введите отрицательное число и обратите внимание на соблюдение условия Item < 0, вследствие чего из основной программы вызывается процедура Abort. При этом в формальный параметр процедуры — пе- ременную Msg передается значение сообщения “Введено отрицательное число”. Результатом действия процедуры будет вывод на экран сообщения "Ошибка: введе- но отрицательное число" и прерывание работы программы.
Процедуры и функции 141 Как видно из примера, параметры обеспечивают механизм замены, который позволяет выполнять процедуру с различными строковыми сообщениями. Если процедура возвращает в программу какие-то значения, соответствующие переменные должны быть описаны, как параметры-переменные с использованием слова var (см. Параметры). 7.3.2. Функции Функция, определенная пользователем, состоит из заголовка и тела функции. Заголовок содержит зарезервированное слово function, идентификатор (имя) функ- ции, заключенный в круглые скобки, необязательный список формальных парамет- ров и тип возвращаемого функцией значения. Тело функции представляет собой локальный блок, по структуре аналогичный программе: function <имя> (Формальные параметры) : <тип результатам* const ...; type ...; var ...; begin <операторы> end; В разделе операторов должен находиться, по крайней мере, один оператор, присваивающий имени функции значение. В точку вызова возвращается результат последнего присваивания. Обращение к функции осуществляется по имени с необязательным указанием списка аргументов. Каждый аргумент должен соответствовать формальным пара- метрам, указанным в заголовке, и иметь тот же тип. В качестве примера приведем программу вычисления выражения Z=(A5+A“3)/2*A^ в которой возведение в сте- пень выполняется функцией Step. {Программа вычисления выражения Z=(А5+А-3)/2*АМ} program DemoFunc; var М : integer; A,Z,R : real; {Функция вычисления степени N, X — формальные параметры, резуль- тат, возвращаемый функцией в точку вызова, имеет вещественный тип} function Step(N : integer; X : real): real; var I : integer; Y : real; begin Y:=l;
142 Глава? for I:=l to N do (Цикл вычисления N—й степени числа X} Y:=Y*X; Step:=Y (Присваивание функции результата вычисления степени} end; (Конец процедуры-функции} begin {Начало основной программы} Write('Введите значение числа А и показатель степени М'); Readin (А,М) ; Z:—Step(5,А); {Вызов функции с передачей ей фактических парамет- ров 5, А} Z:=Z+ Step(3,1/А) ; {Вызов функции с передачей ей фактических параметров 3, 1/А} if М=0 then R:=l else if M>0 then R:=Step(M,A) {Вызов функции с передачей ей фактических параметров М, А} else R:=Step(-M,A); {Вызов функции с передачей ей фактических параметров —И, А} Z:=Z/(2*R) ; Writein('Для А=' ,А, *М=* ,М,' Значение выражения =’, Z); end. В начале программы описываются переменная целого типа М и переменные вещественного типа A, Z, R, после этого описывается функция вычисления степени числа Step с формальными параметрами N и X, результат, возвращаемый функци- ей в точку вызова, — вещественного типа. В описании функции вводятся две локальных (местных) переменных I и Y. Пе- ременная I служит для подсчета числа повторений цикла, а в Y накапливается зна- чение степени как произведения N одинаковых сомножителей. В заключение функ- ции Step присваивается значение вычисленного произведения. В начале выполнения основной программы на экран выводится запрос “Введите значение числа А и показатель степени М” и считывается с клавиатуры значение вещественного числа А и целого числа М. Затем выполняется оператор Z:=Step(5,A). Вначале осуществляется вызов функции Step с передачей ей фактических параметров 5, А. Их значения присваива- ются формальным параметрам функции N и X. По окончании вычисления степени числа значение функции Step, вычисленное для фактических параметров 5 и А, присваивается переменной Z. Аналогично в операторе Z := Z + Step(3,l/A) сначала осуществляется вызов функции Step с передачей ей фактических параметров 3, 1/А, после чего значение переменной Z увеличивается на величину возвращенного в основную программу результата вычисления функции Step. Оператор ifM=0 then R:=l else if M>0 then R:=Step(M,A) else R:=Step(— M,A) проверяет условия M=0, M>0 и в зависимости от их соблюдения либо при- сваивает переменной R значение 1 (при М=0), либо выполняет вызов функции Step для фактических значений М, А или —М, А, а после вычисления значения функции
Процедуры и функции 143 Step присваивает его переменной R. Оператор Z:=Z/(2*R) выполняет вычисление значения выражения Z/(2*R), а затем присваивает вычисленное значение перемен- ной Z. В заключение программы стандартная процедура Writeln выводит на экран со- общение о результате вычислений степени М числа А. Упражнение 2. Запустите интегрированную среду программирования. Введите текст программы DemoFunc и запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, задайте для просмотра в окне отладчика величины А, М, I, Y, Step, Z, R. Установите видимыми одновременно ок- на редактора с текстом программы и окно просмотра Исполните программу в пошаговом режиме с заходом в функцию и пронаблюдайте за изменениями значений переменных в ос- новной программе и в подпрограмме-функции, обратите внимание на передачу значений при вызове функции отьфактических параметров основной программы формальным пара- метрам функции и возврат вычисленного функцией значения в основную программу. 7.3.3. Механизм передачи параметров Как было показано в приведенных выше примерах программ, с использованием процедур и функций, в. заголовке процедуры или функции может быть задан спи- сок параметров, которые называются формальными. Название “формальные” эти параметры получили в связи с тем, что в этом списке заданы только имена для обо- значения исходных данных и результатов работы процедуры, а при вызове подпро- граммы на их место будут подставлены конкретные значения (выражений) и имен. Этот список указывается после имени подпрограммы и заключается в круглые скобки. Список формальных параметров, указываемых в заголовке подпрограммы, мо- жет включать в себя: • параметры-значения; • параметры-переменные, перед которыми должно стоять служебное слово var и за которыми указывается их тип; • параметры-процедуры, перед которыми должно стоять служебное слово procedure; • параметры-функции, перед которыми должно стоять служебное слово function и после которых указывается тип значения, возвращаемого функцией в ос- новную программу; • нетипизированные параметры, перед которыми должно стоять служебное слово var и отсутствует указание типа. В списке должны быть перечислены имена формальных параметров и их типы. Имя параметра отделяется от типа двоеточием, а параметры друг от друга — точ- кой с запятой. Имена параметров одного типа можно объединять в подсписки, в которых имена отделяются друг от друга запятой.
144 Глава 7 Примеры заголовков: procedure Р(procedure В; function С : real; Q, W, R : char); procedure А; Между формальными и фактическими параметрами должно быть полное соот- ветствие: • формальных и фактических параметров должно быть одинаковое количество; • порядок следования фактических и формальных параметров должен быть один и тот же; • тип каждого фактического параметра должен совпадать с типом соответст- вующего формального параметра. Параметры-значения. Параметры-значения испрльзуются только для переда- чи исходных данных из основной программы в подпрограмму (процедуру или функцию), в списке формальных параметров они перечисляются через запятую с обязательным указанием их типов, как было, например, в выше приведенных при- мерах: procedure Abort (Msg: string) ; function Step(N : integer; X : real): real; Если формальный параметр объявлен как параметр-значение, то фактическим параметром может быть произвольное выражение. При вызове подпрограммы фак- тические параметры вычисляются и используются как начальные значения фор- мальных параметров, т. е. осуществляется подстановка значений. Если формаль- ный параметр определен как параметр-значение, то перед вызовом процедуры это значение вычисляется, полученный результат помещается во временную память и передается процедуре. Даже если фактический параметр — простейшее выраже- ние в виде константы или переменной, все равно процедуре будет передана лишь копия этой константы (переменной). В процессе выполнения подпрограммы фор- мальные параметры могут изменяться, но это никак не отразится на соответствую- щих фактических параметрах-переменных, которые сохранят те значения, которые имели до вызова подпрограммы, так как меняются не они, а их копия. Поэтому па- раметры-значения нельзя использовать для передачи результатов из подпрограммы в основную программу. Пример программы с использованием передачи параметров по значению: program Par_Znach; var А,В : real; {Процедура вычисления квадратов двух чисел и вывода на экран их суммы} procedure Sum_Square(X, Y : real); {X,Y — формальные параметры} begin X:=X*X; Y:=Y*Y;
Процедуры и функции 145 Writeln('Сумма квадратов =',X+Y); end; {Конец процедуры} begin {Начало основной программы} А:=1.5; В:=3.4; Sum_Square(A,B); {Вызов процедуры Sum_Square с передачей ей значе- ний фактических параметров А и В} end. При вызове процедуры Sum_Square с фактическими параметрами А, В значе- ния этих параметров (один раз) копируются в соответствующие формальные пара- метры X, Y, и дальнейшие преобразования формальны^ параметров X,Y внутри процедуры Sum_Square уже никак не влияют на значения переменных А, В. Упражнение 3. Запустите интегрированную среду программирования. Введите текст программы ParZnach, запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, задайте для просмот- ра в окне отладчика величины А, В, X, Y. Установите видимыми одновременно окно редак- тора с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с заходом в процедуру и пронаблюдайте за изменениями значений переменных в основной программе и в процедуре, обратите внимание на передачу значений при вызове процедуры ее формальным параметрам, а также на то, что в процессе выполнения процедуры и по ее окончании значения переменных А, В не изменялись. Параметры-переменные. Параметры-переменные используются для опреде- ления результатов выполнения процедуры и в списке формальных параметров пе- речисляются после зарезервированного слова var с обязательным указанием типа. Каждому формальному параметру, объявленому как параметр-переменная, должен соответствовать фактический параметр в виде переменной соответствующего ти- па, например: procedure Example(var M,N : integer; var Y : real); Если формальный параметр определен как параметр-переменная, то при вызо- ве процедуры ей передается сама переменная, а не ее копия,и изменение парамет- ра-переменной приводит к изменению фактического параметра в вызывающей про- грамме. Следовательно, исходные данные в процедуру из программы могут переда- ваться как через параметры-значения, так и через параметры-переменные, а резуль- таты работы процедуры возвращаются в вызывающую программу только через па- раметры-переменные. Пример программы, использующей параметры-переменные: program Sum_Sub_Square; var А,В : real; SumAB, SubAB : real; {Процедура с параметрами-переменными Sum, Sub} procedure Sum_Sub(X,Y : real; var Sum, Sub : real);
146 Глава? begin Sum:=X*X+Y*Y; Sub:=X*X-Y*Y; end; {Конец процедуры) begin {Начало основной программы) А:=1.5; В:=3.4; Sum_Sub(А,В, SumAB,SubAB); {Вызов процедуры с передачей ей фактических параметров-значений А, В и параметров-переменных SumAB, SubAB) Writein(’Сумма квадратов чисел ' ,А,’ и *,В,' =',SumAB); Writein('Разность квадратов чисел ’ ,А,* и ’,В,' =', SubAB); end. t Упражнение 4. Запустите интегрированную среду программирования. Введите текст программы Sum Sub Square и запишите файл на диск под соответствующим именем, а за- тем откомпилируйте его. После того как компиляция выполнится успешно, задайте для про- смотра в окне отладчика величины А, В, Sum, Sub, SumAB, SubAB. Установите видимыми одновременно окна редактора с текстом программы и окно просмотра. Исполните програм- му в пошаговом режиме с заходом в процедуру и пронаблюдайте за изменениями значений переменных в основной программе и в процедуре, обратив внимание на передачу значений при вызове процедуры ее формальным параметрам, а также на то, что значения переменных SumAB и SubAB в основной программе изменялись в процессе выполнения процедуры. Параметры-процедуры и параметры-функции. В качестве расширения стандартного Паскаля, Турбо Паскаль позволяет рассматривать процедуры и функции как объекты, которые можно присвоить переменным и которые могут вы- ступать в качестве параметров; процедурные типы делают это возможным. Как только процедурный тип определен, можно объявлять переменные этого типа. Такие переменные называются процедурными переменными. Они могут быть использованы в качестве формальных параметров при вызове процедур и функций. Подобно тому, как целочисленной переменной можно присвоить цело- численное значение, процедурной переменной можно присвоить процедурное зна- чение. Такое значение может, конечно, быть и другой процедурной переменной, но может также (?ыть идентификатором процедуры или функции. В этой ситуации объявление процедуры или функции можно рассматривать как особый вид объяв- ления константы, значением которой является процедура или функция. Как и во всех других операциях присваивания, переменная в левой части и переменная в правой части оператора присваивания должны быть совместимы- ми по присваиванию. Для того чтобы считаться совместимыми по присваиванию, процедурные типы должны иметь одинаковое число параметров, параметры в со- ответствующих позициях должны быть тождественных типов; наконец, типы ре- зультатов функций должны быть идентичны. Дополнительно к совместимости ти- пов процедура или функция должны удовлетворять следующим требованиям, если они присваиваются процедурной переменной.
Процедуры и функции 147 • Они должны быть объявлены с директивой far (использование дальнего типа вызова подпрограмм) и откомпилированы в состоянии {SF+}; • Они не должны быть: стандартной процедурой или функцией; вложенной процедурой или функцией; inline процедурой или функцией; interrupt процедурой или функцией. При использовании параметров-процедур или параметров-функций в списке перед соответствующими формальными параметрами указывается зарезервирован- ное слово procedure или function, например: procedure Exampl(К,L:integer; var М real; procedure Prob; function Step: rekl); В списке формальных параметров процедуры Exampl: К, L — параметры-значения; М — параметр-переменная; Prob — параметр-процедура; Step — параметр-функция, результатом выполнения которой будет значение вещественного типа. При вызове подпрограммы на место формальных параметров-процедур и пара- метров-функций осуществляется подстановка имен соответствующих фактических процедур или функций. При этом, если процедуры и функции, фигурирующие в ка- честве фактических параметров, имеют, в свою очередь, параметры, они могут быть только параметрами-значениями. Параметры процедурного типа особенно полезны в ситуациях, когда над множеством процедур или функций'выполняются общие действия. Примером ис- пользования параметров-функций может служить программа, которая с помощью одной и той же процедуры печати таблицы выводит на экран три таблицы арифме- тических функций (сложения, умножения и произведения суммы на разность чи- сел), каждая из которых выполняется отдельной функцией. program Demo_Tabl; {Описание процедурного типа Func — целой функции двух аргументов целого типа) type Func — function(X,У : integer) : integer; ($F+) {Директива компилятору на использование дальнего типа вызо- ва Подпрограмм} {Описание функции сложения двух целых чисел} function Add(X,Y : integer) : integer; begin Add:=X+Y;
148 Глава? end; {Описание функции умножения двух целых чисел} function Mult(X,Y : integer) : integer; begin Mult:=X*Y; end; {Описание функции умножения суммы на разность двух целых чисел} function Funny(X,Y : integer) : integer; begin Funny := (X+Y) * (X-Y) ; end; ($F- ) {Описание процедуры вывода на экран таблиць> для двух чисел от 1 до 10, вид арифметической операции задается значением параметра-функ- ции Operation} procedure Type_Tabl(W,H : integer; Operation : Func); var X,Y : integer; begin for Y:=l to H do begin for X:=l to W do Write(Operation(X,Y):5); Writeln; end; end; begin {Начало главной программы} Type_Tabl(10,10,Add) ; {Вызов процедуры для печати таблицы сло- жения} Туре_ТаЫ(10,10,Mult); {Вызов процедуры для печати таблицы ум- ножения } Туре_ТаЫ(10,10,Funny);{Вызов процедуры для печати таблицы про- изведений суммы чисел от 1 до 10 на их разность} end. Примечание. {SF+} — указание (директива) компилятору Турбо Паскаль на использо- вание дальнего (far) типа вызова для корректной обработки вызова процедуры Туре ТаЫ с параметрами-функциями. В данной программе процедура Туре_ТаЫ представляет собой общее действие, выполняемое над параметрами-функциями Add, Mult, Funny. После запуска про- граммы сначала вызывается процедура Туре_ТаЫ для фактических параметров 10, 10 и Add, в результате чего формальным параметрам X и Y присваиваются значе- ния чисел 10 и 10, а формальному параметру Operation процедурного типа Func присваивается имя фактической функции Add. В результате этого на экран будет
Процедуры и функции 149 выведена таблица сложения от 1 до 10. Затем процедура Туре ТаЫ вызывается к исполнению для фактических параметров 10, 10 и параметра-функции Mult, в ре- зультате чего на экран будет выведена таблица умножения от 1 до 10. Аналогично вызов процедуры Туре ТаЫ с параметрами 10, 10 и Funny даст в результате на эк- ране таблицу произведения суммы на разность чисел от 1 до 10. Упражнение 5. Запустите интегрированную среду программирования. Введите текст программы Demo Tabl и запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, исполните программу в пошаговом режиме с заходом в процедуру и пронаблюдайте за вызовом функций вычисле- ния суммы, произведения двух чисел или произведения их суммы и разности. Обратите вни- мание на исполнение оператора Write(Operation(X,Y):5), как в зависимости от фактического значения параметра-функции Operation процедурного типа Func осуществляется вызов раз- личных функций Add,iMult или Funny. Попробуйте удалить строку с директивой компиля- тору об использовании дальнего типа вызова или возьмите в фигурные скобки описание процедурного типа Func и пронаблюдайте за результатом. В случае появления ошибок нажа- тием клавиши F1 получите справку о причинах ошибки и рекомендации на коррекцию. 7.3.4. Область действия параметров При создании программ, использующих процедуры, следует учитывать, что все объекты (метки, константы, типы, переменные, процедуры и функции), которые описываются после заголовка процедуры, называются локальными объектами и доступны только в пределах этой процедуры, но недоступны вызывающей про- грамме. Все эти объекты создаются при входе в процедуру и уничтожаются при вы- ходе из нее. Если одно и то же имя определено в нескольких процедурах, вызывае- мых одной и той же программой, то в каждой процедуре этому имени соответству- ет свой локальный объект. Все объекты, описанные в вызывающей программе, называются глобальными и являются доступными внутри процедур, вызываемых этой программой. Поэтому обмен данными между программой и вызываемой ею процедурой может произво- диться и через глобальные переменные. Если одно и то же имя определено и в про- грамме, и в вызываемой ею процедуре, то ему соответствует глобальный объект, но внутри процедуры глобальный объект недоступен, он как бы экранируется (маскируется) локальным объектом с таким же именем. В Турбо Паскале допускается любой уровень вложенности процедур и функ- ций. Процедура, описанная в основной программе, в свою очередь, может иметь описания внутренних процедур и функций и т. д. При этом объекты, описанные в вызывающей процедуре, являются глобальными по отношению к вызываемой про- цедуре. Модою схематически изобразить структуру блоков некоторой Паскаль-про- граммы, как показано на рис. 7.2.
150 Глава? Рис.7.2. Структура блоков некоторой Паскаль-программы Для доступа к объектам, описанным в различных блоках, требуется соблюдать следующие правила; 1. Имена объектов, описанных в некотором блоке, считаются известными в пределах данного блока, включая и все вложенные блоки. 2. Имена объектов, описанных в блоке, должны быть уникальны в пределах данного блока и могут совпадать с именами объектов из других блоков. 3. Если в некотором блоке описан объект, имя которого совпадает с именем объекта, описанного в объемлющем блоке, то это последнее имя становится недос- тупным в данном блоке (оно как бы экранируется одноименным объектом данного блока). Если применить эти правила к предыдущей схеме, можно сказать, что объекты, описанные в блоке В, известны (видимы) кроме самого блока В еще и блоках С и D, но невидимы в блоке А. Объекты, описанные в блоке F, известны только в пре- делах этого блока. Например: program Exampl_proc; procedure Р; procedure А; var J : integer; {Локальная переменная J, является глобаль- ной по отношению к процедуре В) procedure В; var J ; integer; {Локальная переменная J, экранирует гло- бальную переменную J, описанную в вызывающей процедуре А} begin Writein(J); end; begin
Процедуры и функции 151 J:=l; В; (Вызов процедуры В} end; begin А; {Вызов процедуры.А) end. Иногда при вызове подпрограмм-функций возникают побочные эффекты, вы- ражающиеся в том, что вносятся нежелательные изменения в значения глобальных переменных. Поэтому будьте внимательны при описании параметров-перемен- ных, при выборе имен учитывайте наличие глобальных объектов с такими имена- ми. В качестве примера с вложенными подпрограммами рассмотрим пример про- граммы, определяющей количество сверхпростых чисел в натуральном ряду чисел, не превышающих 1000. Сверхпростым называется число, если оно простое, и чис- ло, полученное из исходного числа, при записи цифр исходного числа в обратном порядке (перевертыш) тоже-будет простым. Например, 13 и 31 — сверхпростые числа. '{Подсчет количества сверхпростых чисел < 1000} program Sverx_Prost; - var X, К : integer; {Описание глобальных переменных X, К} {Функция проверки числа X на простое число} function Prost(Y : integer):boolean; var D, I : integer; {Описание локальных переменных D,I,Flag} Flag : boolean; begin D:=0; Flag:—False; for I:=2 to Y-l do if Y mod 1=0 then D:=D+1; if D=0 then Flag:=True; Prost:=Flag; {Нашли простое число, имеющее только два дели- теля: единицу и само это число} end; {Перевертыш простого числа} function Povorot(Y:integer):integer; var S:integer; {Описание локальной переменной S} begin if Y<10 then S:=Y; if (Y>10) and (Y<100) then S:=Y mod 10*10+Y div 10; if (Y>=100) and (Y<1000) then S:=Y mod 10*100+Y mod 100 div 10*10+Y div 100;
152 Глава? Povorot:=S; end; begin {Начало главной программы) Writeln(* *:20,* Сверхпростые числа *); К:=2; (2 и 3 — простые числа) for Х:=4 to 999 do begin if Prost(X) then {Вызов функции определения простого чис- ла х) if Prost(Povorot(X)) then {Вызов функции определения простого числа для числа-перевертыша, полученного вычислением функции Povorot(х) от простого числа) begin * Writeln(X); {Печать сверхпростого числа) К:=К+1; end; end; Writeln ('Всего найдено ' ,К, * сверхпростых чисел < 1000'*); end. Упражнение 6. Проанализируйте текст программы, обратив особое внимание на при- менение функций определения простого числа, функцию получения перевертыша и вложен- ный вызов функции Prost(Povorot(X)), при котором переменные D, I, Flag, локальные для функции Prost, являются глобальными для функции Povorot. Запустите интегрированную среду программирования. Введите текст программы Sverx Prost и запишите файл на диск под соответствующим именем, а затем откомпилируй- те его. После того как компиляция выполнится успешно, задайте для просмотра в окне от- ладчика переменные X, К, I, D, S, Flag. Установите видимыми одновременно окна редакто- ра с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с за- ходом в функцию и пронаблюдайте за изменениями значений переменных в основной про- грамме и в функциях, обратите внимание на передачу значений при вызове функции от фак- тических параметров основной программы формальным параметрам функции, на возвраще- ние вычисленных функциями значений в точку вызова главной программы, а также на ха- рактер изменения значений параметров локальных переменных I, D и глобальных перемен- ных X, К. 7.3.5. Рекурсии Рекурсия — это такой способ организации вычислительного процесса, при ко- тором процедура или функция в ходе выполнения составляющих ее операторов об- ращается сама к себе. Примером программы с использованием рекурсии может быть программа вы- числения факториала числа. (Факториалом натурального числа п называют произ- ведение чисел 1*2*...*п.)
Процедуры и функции 153 {Вычисление факториала числа п с использованием рекурсивной функ- ции) program Demo_Rekurs; var N : integer; F : longint; function Fakt(N : integer): longint; {Описание функции, N — формальный параметр-значение типа integer, результат выполнения функции типа longint) begin {Начало вычисления функции) if N=1 then Fakt:=l {Проверка условия завершения рекурсии) else Fakt:=N*Fakt(N—1) {Рекурсивное вычисление N!} end; begin * {Начало главной программы) Writein('Введите число N >'); Read(N) ; F:=Fakt(N); {Вызов функции для фактического параметра N) Writein('Для числа ',N,' Значение факториала =',F); end. После запуска программы на экран выводится запрос "Введите число N >", за- тем с клавиатуры считывается значение целого числа N и в выражении F:=Fakt(N) вызывается функция Fakt с параметром—значением N. В подпрограмме-функции вычисления факториала проверяется условие N=l. Если оно выполняется, то функ- ции Fakt присваивается значение 1, на этом выполнение подпрограммы-функции завершается. Если условие N=1 не соблюдается, то выполняется вычисление произ- ведения N*Fakt(N—1). Вычисление произведения носит рекурсивный характер, так как при этом осуществляется вызов функции Fakt(N—1), значение которой вычис- ляется, в свою очередь, через вызов функции Fakt, параметром которой также бу- дет функция Fakt, и т. д., до тех пор пока значение формального параметра N=l. Так как базовая часть описания рекурсивной функции Fakt определяет значение Fakt для N=l, равным единице, то рекурсивные вызовы функции Fakt больше не выполняются, а наоборот, выполняется вычисление функции Fakt для чисел, воз- растающих от 1 до N, причем функция Fakt всякий раз возвращает значение, равное произведению очередного k-го числа на факториал от к—1-го числа. Последнее возвращение результата вычисления функции Fakt присвоит переменной F значе- ние произведения всех чисел от 1 до N, т. е. факториал числа N. Итак, при выполнении рекурсивной подпрограммы осуществляется многократ- ный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи. В нашем примере решение при N=1 тривиально, т. е. Fakt=l. Затем осуществляется возврат на верхний уровень с последовательным вычислением значения функции Fakt.
154 Глава? Упражнение 7. Запустите интегрированную среду программирования. Введите текст программы Demo Rekurs и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, задайте для просмот- ра в окне отладчика переменные N, F. Установите видимыми одновременно окна редактора с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с захо- дом в функцию и пронаблюдайте за изменением значения переменной N при рекурсивных вызовах функции Fakt. Следует учитывать, что использование рекурсивной формы организации алго- ритма обычно выглядит изящнее итерационной и дает более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать перепол- нение стека. Это объясняется тем, что при каждом входе в подпрограмму ее ло- кальные переменные размещаются в особым образом организованной области па- мяти, называемой программным стеком. 7.3.6. Нетрадиционное использование подпрограмм В Турбо Паскале есть случаи нетрадиционного объявления подпрограмм, когда в объявлении процедуры содержится директива interrupt (прерывание), external (внешняя) или inline (встроенная) или вместо блока в объявлении процедуры или функции написано forward (опережающая). Interrupt (прерывание). Объявление процедуры может содержать директиву interrupt перед блоком, и тогда процедура рассматривается как процедура преры- вания. Прерыванием называется временное прекращение процесса, вызванное со- бытием, внешним по отношению к этому процессу. Необходимость в процедурах прерывания возникает, когда программист решает определить собственные алго- ритмы реакции на прерывание операционной системы, отменив при этом стан- дартные реакции. Отметим, что процедуры прерывания нельзя вызывать с помо- щью операторов процедур и что каждая процедура прерывания должна задавать список параметров точно так, как это показано ниже: procedure МуInt(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP:Word); interrupt; Примечание. CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP — регистры процессора. Внешние объявления (external). Внешние объявления позволяют связывать отдельно скомпилированные процедуры и функции, написанные на языке ассемб- лера. С помощью директивы {$L имя файла} внешнюю программу можно связать с программой или модулем, написанным на Паскале. Приведем следующие примеры объявлений внешних процедур: procedure MbveWord(var Source, Dest; Count: Word); external; procedure FillLong(var Dest; Data: Longint; Count: Word); external.
Процедуры и функции 155 В тексте программы при объявлении внешних подпрограмм нужно задать ди- рективу компилятору $L, аргументом которой является имя OBJ-файла, содержа- щего код подключаемой подпрограммы, например: {$L BLOCK.OBJ} Assembler. Assembler-объявление позволяет вам написать процедуру или функцию на встроенном Ассемблере (язык программирования низкого уровня, близкий к машинному). Inline (встроенная). Директива inline позволяет записывать инструкции в ма- шинном коде, не используя блок операторов. При вызове обычной процедуры компилятор создает код, в котором параметры процедуры помещаются в стек, а за- тем для вызова процедуры генерируется инструкция call. Когда вы вызываете внут- реннюю процедуру, компилятор генерирует код из директивы inline вместо call. Таким образом, тйпеЬгроцедура "расширяется" при каждом обращении к ней ана- логично макрокоманде на языке ассемблера. (Макрокоманда — macro — предло- жение языка программирования, вместо которого компилятор при трансляции за- писывает несколько машинных команд.) Опережающие объявления (forward). Помимо прямых рекурсий, рассмотрен- ных ранее, рекурсивный вызов может быть и косвенным, т. е. одна процедура вы- зывает другую процедуру, которая, в свою очередь, вызывает первую процедуру. А так как до вызова процедуры она обязательно должна быть описана, то ис- пользуется опережающее объявление-, процедура содержит описание только сво- его заголовка, вслед за которым ставится зарезервированное слово forward (опере- жающий), а описание текста процедуры помещается далее в тексте программы уже без повторения описания списка формальных параметров и называется опреде- ляющим объявлением. Опережающее объявление и определяющее объявление должны находиться в одной и той же части объявления процедур и функций. Между ними могут быть объявлены другие процедуры и функции, и они могут вызывать процедуру с опе- режающим объявлением. Таким образом, возможна взаимная рекурсия. Определяющее объявление процедуры может быть external или assembler. Однако оно не может быть near-; far-; inline- или другим forward-объявлением. Оп- ределяющее объявление также не может содержать директивы interrupt, near или far. Опережающие объявления не допускаются в интерфейсной части модуля. На- пример: program Demo_Kos_Rekurs; var а,Ь : real; procedure Pl (x : real); forward; {Опережающее объявление процедуры} procedure P2(у : real); begin
156 Глава? Pl(а); {Вызов процедуры Р1} end; procedure Pl; {Текст процедуры без описания предваритель- но описанных параметров} begin Р2(а); {Вызов процедуры Р2} end; begin Р2(а); Р1(Ь); {Вызов процедур Р2 и Р1} end. Как видно из примера, опережающее объявление процедуры Р1 позволило ис- пользовать обращение к ней из процедуры Р2, так как при трансляции компилятору до вызова процедуры Р1 из опережающего объявления становятся известными ее формальные параметры и он может правильно организовать ее вызов. В самом теле процедуры Р1 уже нет необходимости описывать параметры, так как это было сде- лано при опережающем описании. Примером программы с использованием вложенных подпрограмм-процедур может быть программа Demo Tower, в которой реализован алгоритм древней игры "Ханойские башни". Имеются три стержня, на одном из них (например, на левом) насажены диски разных размеров, причем диски располагаются так, чтобы стер- жень с дисками напоминал башню, т. е. внизу располагаются самые большие дис- ки, а вверху маленькие. Цель игры — перенести башню с правого стержня на ле- вый, причем за один раз можно переносить только один диск и при этом можно на- саживать только диск с меньшим диаметром на диск с большим диаметром. Сред- ний стержень является вспомогательным для временного хранения дисков. program Demo_Tower; {Ханойские башни — перенести кольца с правого стержня — 3 на левый — 1} type Position — (Left, Centre, Right); var N : integer; procedure MoveDisk(From, Tol : Position); {Перемещение диска} procedure WritePos(P : Position); begin case P of Left : Write('1'); Centre : Write('2');
Процедуры и функции 157 Right : Write(•3’); end; end; begin WritePos(From); Write(’->’); WritePos(Tol); Writein end; procedure MoveTower(Hight:integer; From,Tol,Work:position); begin if Hight>0 then x begin MoveTower(Hight—1,From,Work,Tol); {Рекурсивный вызов} MoveDisk(From,Tol); MoveTower(Hight—1,Work,Tol,From); {Рекурсивный вызов} end; , end; begin Writein('Введите число колец'); Readln(N); MoveTower(N,Right,Left,Centre); {Вызов процедуры с передачей ей фактических параметров} end. Упражнение 8. Изучите текст программы. Запустите интегрированную среду програм- мирования. Введите текст программы Demo_Tower и запишите файл на диск под соответст- вующим именем, а затем откомпилируйте его. После того как компиляция выполнится ус- пешно, задайте для просмотра в окне отладчика переменные From, Tol, Work, Hight=l. Уста- новите видимыми одновременно окна редактора с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с заходом в процедуры и пронаблюдайте за ре- курсивным вызовом процедуры MoveTower. В дальнейшем после изучения графических процедур Турбо Паскаля вы може- те представить перенос дисков со стержня на стержень графически. Контрольные вопросы и задания Вопросы. 1. Что понимают под структурным программированием? Зачем оно применяется? 2. Назовите основные логические структуры и проиллюстрируйте примерами про- грамм. Как обеспечивается логически последовательная структура программы? 3. В чем заключается метод нисходящего программирования? 4. Что называется подпрограммой? В чем состоит сходство и различие подпрограмм- процедур и подпрограмм-функций в языке Турбо Паскаль?
158 Глава 7 5. В чем различие между стандартными и определенными пользователем подпрограм- мами? Приведите примеры. 6. Запишите синтаксическую диаграмму определения процедуры, функции. 7. Опишите последовательность событий при вызове процедуры или функции. 8. В каких случаях в программе указывается директива компилятору {$!}? 9. Что называется параметром, и каково его назначение? Формальные, фактические па- раметры, их взаимосвязь. 10. Для чего используется исполнение программы в пошаговом режиме с заходом в процедуры, и как это осуществить? 11. Каковы отличия параметров-значений от параметров-переменных, особенности их описания и применения. 12. Каковы особенности параметров-процедур и параметров-функций? 13. Чем отличаются локальные и глобальные параметры? Какова область их действия? 14. Что такое рекурсия? 15. В каких случаях требуется предварительное или внешнее описание подпро- грамм? Каковы особенности использования подпрограмм с предварительным описанием? 16. Даны фрагменты программ: а) var c,d : integer; procedure P(x,y : integer); begin y: = x+1; end; 6) var c,d : integer; procedure Q(x : integer; var у : integer); begin y:= x+1; end; B> var c,d : integer; procedure R(var x,y : integer); begin y:= x+1; end; a) для каждой из этих процедур указать, какие из ее параметров являются параметрами- значениями, а какие — параметрами-переменными. б) определить, что будет выдано на печать в следующих случаях: с:=2; d:=0; P(sqr(c)+c,d); Writeln(d);
Процедуры и функции J 59 с:=2; d:=0; Q(sqr(c)+c,d) ; Writeln(d); Почему при изменении в процедуре параметра-значения соответствующий фактический параметр не меняет своего значения? Что надо сделать, чтобы он менял значение? в) допустимы ли обращения R(sqr(c)+c,d) и R(c,d) ? Почему невыгодно объявлять пара- метр, не меняющийся в процедуре, параметром-переменной? 17. Что будет напечатано следующей программой? a) program DemoPrintl; var х,у : char; procedure P(x : integer); const у : true; begin Writein(x,'*',y); end; procedure Q(x : integer); var x : char; begin x:=succ(y) ; y:='*'; Writein(x,’ ’,y); end; begin x:=’a'; y:='5'; P(8) ; Writein(x,* ',y); Q; Writein(x,' ',y); end. 6) program DemoPrint2; var a,b,c,d : integer; procedure P(var b : integer c : integer); var d : integer; begin a:=5; b:=6; c:=7; d:=8; Writein(a,b,c,d) ; end; begin a:=l; b:=2; c:=3; d:=4; P(a,b); Writein(a,b,c,d) ; - end.
160 Глава? Задания. 1. Напишите программу вычисления среднего геометрического модулей двух введен- ных с клавиатуры целых чисел X и Y. Программа должна использовать цикл WHILE DO. Условие выхода из цикла — значение числа, равное 999. (Среднее геометрическое — ко- рень квадратный из произведения модулей.) 2. Напишите программу вычисления выражения у = (tg(X) + sin(X)) * ecos(X) при Х=3.7. Результат вывести в формате 5:2. 3. Напишите программу, которая определит первое отрицательное число последова- тельности: А= Sin(i/100). i= 1,2,3... 4. Напишите программу, которая с помощью функции Chr выведет на экран кодовую таблицу ПЭВМ (ASCII-таблицу). Задержите выведенную информацию на 5 с и очистите эк- ран. 5. Напишите программу, которая выведет на экран 1b строк по 5 случайных чисел в диапазоне 0..36. 6. С помощью цикла FOR и функции Odd напишите программу, выводящую все нечет- ные числа в диапазоне 1..100. 7. Напишите программу, которая по значениям двух катетов вычисляет гипотенузу и площадь треугольника. 8. Напишите программу вычисления расстояния между двумя точками с заданными ко- ; ординатами X1, Y1, Х2, Y2. ; 9. Напишите процедуру-заставку к программе вычисления математических функций в виде | ****************************************** * Программа * * вычисления математических функций * * Автор: Смирнов А.П. * ****************************************** Заставка выводится на очищенный экран, удерживается на экране 3 с, затем экран очи- щается. Вызовите процедуру Zastavka в начале программы. 10. Напишите функцию возведения в степень по формуле: Ав = Exp(Ln(A)*B) и используйте ее в программе для возведения в 4-ю степень вещественного числа 2,87. 11. Оформите процедуру Proverka проверки пользователя на право работы с програм- мой. Используйте для этого пароль = SCHOOL Если пароль неправильный, выйти из про- граммы по Halt. 12. Напишите программу вычисления функции Y по формуле: кХ Y = (Ln2 (1+Х ) + Cos' (Х+1)), kZ3 , при к < 1 где Х = Z(Z+1) , прик>= 1
Процедуры и функции 161 13. Напишите программу, состоящую из трех процедур и основной программы. Первая процедура организует ввод двух целых чисел X и Y, вторая вычисляет их сумму, третья вы- водит результат. Используйте эти процедуры в основной программе. Используйте X,Y как глобальные переменные. Эта программа послужит прообразом всех ваших будущих про- грамм, т.к. в ней реализуется принцип работы любой системы: логически выделенные ввод, обработка и вывод результата. 14. Напишите программу вычисления площади поверхности и длины экватора на осно- ве известного радиуса планет солнечной системы. Форму планет будем считать шаром. Вы- числение площади и длины экватора оформите отдельными функциями. 15. Составить программу поиска большего из четырех чисел с использованием подпро- граммы поиска большего из двух. 16. Даны координаты вершин многоугольника (xl,y 1, х2,у2,..., х10,у!0). Определить его периметр (вычисление расстояния между вершинами оформить подпрограммой). 17. Вычислить сумму: 1! +2! + 3! +...+п!, используя функцию вычисления факториала числа к! 18. Вычислить сумму простых, сверхпростых, совершенных чисел, не превосходящих заданного числа N. 19. Составьте программу вычисления числа сочетаний из N по М. Число сочетаний оп- ределяется по формуле N!/(M!*(N—М)!), где N — количество элементов перебора. Исполь- зуйте подпрограмму вычисления факториала. 20. Определить НОД трех натуральных чисел. 21. Даны действительные числа s,t. Составить программу вычисления выражения f(t,—2s, 1.17) + f(2.2,t, s—t), где f(a,b,c) = (2a—b—sin(c))/(5+|c|). 22. Дано натуральное число N. Составить программу, определяющую, есть ли среди чи- сел п, п+1,..., 2п близнецы, т. е. простые числа, разность между которыми равна 2. (Исполь- зовать процедуру распознавания простых чисел). 23. Составьте программу перевода двоичной записи натурального числа в десятичную. 24. Составьте программу сокращения дроби М/N, где М, N — натуральные числа. 25. Составьте программу вычисления суммы квадратов простых чисел, лежащих в ин- тервале (M,N). 26. Составьте программу подсчета числа четных цифр, используемых в записи N-знач- ного числа М. 27. Составьте программу вычисления суммы трехзначных чисел, в десятичной записи которых нет четных цифр. 28. Составьте программу вывода на экран всех натуральных чисел, не превосходящих N и делящихся на каждую из своих цифр. 29. Составьте программу нахождения наименьшего натурального N-значного числа X (Х>=10), равного утроенному произведению своих цифр. 30. Составьте программу подсчета числа всех натуральных чисел, меньших М, квадрат суммы цифр которых равен X. б-це
Часть III СТРУКТУРИРОВАННЫЕ ТИПЫ ДАННЫХ * Все простые данные, которые рассматривались в предыдущих главах, имеют два характерных свойства: неделимость (атомарность) и упорядоченность их зна- чений. Составные, или структурные, типы данных в отличие от простых задают мно- жества сложных значений с одним общим именем. Можно сказать, что структур- ные типы определяют некоторый способ образования новых типов из уже имею- щихся. Таким образом, Турбо Паскаль допускает образование структур данных произ- вольной сложности, позволяя тем самым достичь адекватного представления в про- грамме тех данных, с которыми она оперирует. Существует несколько методов структурирования, каждый из которых отлича- ется способом обращения к отдельным компонентам и, следовательно, способом обозначения компонентов, входящих в структурные данные. По способу организа- ции и типу компонентов в сложных типах данных выделяют следующие разновид- ности: • регулярный тип (массивы); • комбинированный тип (записи); • файловый тип (файлы); • множественный тип (множества); • строковый тип (строки); • в языке Турбо Паскаль версии 6.0 и старше введен объектовый тип (объекты). В отличие от простых типов данных данные структурированного типа характе- ризуются множественностью образующих этот тип элементов, т. е. переменная или константа структурированного типа всегда имеет несколько компонентов. Каждый компонент в свою очередь может принадлежать структурированному типу, т. е. возможна вложенность типов.
Строки 163 Глава 8. СТРОКИ 8.1. ОПИСАНИЕ СТРОКОВОГО ТИПА Изучение данных структурированного типа начнем со строкового типа данных (строк). Строка — это последовательность символов кодовой таблицы персональ- ного компьютера. При использовании в выражениях строка заключается в апост- рофы. Количество символов в строке (длина строки) может динамически изменять- ся от 0 до 255. Для определения данных строкового типа используется идентифи- катор string, за которым следует заключенное в квадратные скобки значение мак- симально допустимой длины строки данного типа. Если это значение не указывает- ся, то по умолчанию длина строки равна 255 байт. Переменную строкового типа можно определить через описание типа в разделе определения типов или непосредственно в разделе описания переменных. Строко- вые данные могут использоваться в программе также в качестве констант. Недопустимо применение строковых переменных в качестве селектора в опе- раторе case. Определение строкового типа устанавливает максимальное количество симво- лов, которое может содержать строка. Формат: type <имя типа> = string [максимальная длина строки]; var <идентификатор,...> : <имя типа>; Переменную типа string можно задать и без описания типа: var <идентификатор,...> : string [максимальная длина строки]; Пример. const Address = 'ул. Переверткина, 25'; {Строковая константа} type Flot = string[125]; var Fstr : Flot; {Описание с заданием типа} Stl : string; {По умолчанию длина строки = 255} St2,l St3 : string[50]; Nazv : string[280]; {Ошибка, длина Nazv превышает 255} Строка в языке Турбо Паскаль трактуется как цепочка символов. (Для строки *из N символов отводится N+16afrr; N байт-для хранения символов строки, а один байт — для значения текущей длины строки.)
164 Глава 8 К любому символу в строке можно обратиться, указав его номер. В самом на- чале строки (под нулевым номером) расположен байт, содержащий значение теку- щей длины строки. 0 1 2 3 4 •.. N+1 N текущая длина строки Поэтому для определения объема памяти в байтах, требуемой для размещения строки, к значению ее максимальной длины прибавляется 1. Например, для раз- мещения в памяти переменных Fstr, Stl, St2 требуется соответственно 126, 35 и 51 байт. Рассмотрим структуру размещения строки в памяти на следующем примере. Пусть М — максимальная длина строки, L — текущая длина, А — ячейка памяти. Тогда: А — содержит величину текущей длины; А+1 — первый символ строки; A+L — последний значащий символ; A+L+1 — незанятые ячейки памяти А+М 8.2. СТРОКОВЫЕ ВЫРАЖЕНИЯ Выражения, в которых операндами служат строковые данные, называются строковыми. Они состоят из строковых констант, переменных, указателей функ- ций и знаков операций. Над строковыми данными допустимы операция сцепления и операции отношения. Операция сцепления (+) применяется для сцепления нескольких строк в одну результирующую строку. Например: Выражение Результат 'А'+'Г+"+'386' 'АТ 386' 'Турбо'+'Паскаль +'7.0' 'Турбо Паскаль 7.0' Следует учитывать, что в операциях сцепления длина результирующей строки не должна превышать 255. Операции отношения (=, о, >, <, >=, <=) проводят сравнение двух строковых операндов и имеют приоритет более низкий, чем операция сцепления, т. е. вначале всегда выполняются все операции сцепления, если они присутствуют, и лишь по- том реализуются операции отношения. Сравнение строк производится слева напра- во до первого несовпадающего символа, и та строка считается больше, в которой первый несовпадающий символ имеет больший номер в стандартной таблице об- мена информацией. Результат выполнения операций отношения над строковыми
Строки 165 операндами всегда имеет булевский тип и принимает значение True, если выраже- ние истинно, и False, если выражение ложно. Например: Выражение Результат 'MS-DOS'^MS-Dos' True 'program'>’PROGRAM' True Если строки имеют различную длину, но в общей части символы совпадают, считается, что более короткая строка меньше, чем более длинная. Строки считают- ся равными, если они полностью совпадают по длине и содержат одни и те же сим- волы. Например: Выражение Результат 'Принтер ^'Принтер' True 'Intel!'-Intel!' True Для присваивания строковой переменной результата строкового выражения используется оператор присваивания (:=). Пример. Strl := 'Группа учащихся’; Str2 := Strl + ' школы-лицея'; Fio : = ’Бочаров А.А. ' ; Если значение переменной после выполнения оператора присваивания превы- шает по длине максимально допустимую при описании величину, все лишние сим- волы справа отбрасываются, например: Описание А Выражение Значение А A: string[6] А “'ГРУППА Г; 'ГРУППА' A: string[8] А := ТРУППА 1'; ТРУППА 1' A: string[2] А := ТРУППА Г; гр. Допускается смешение в одном выражении операндов строкового и литерного типа. Если при этом литерной переменной присваивается значение строкового ти- па, длина строки должна быть равна единице, иначе возникает ошибка выполне- нюь К отдельным символам строки можно обратиться по номеру (индексу) данного символу в строке. Индекс определяется выражением целочисленного типа, которое записывается в квадратных скобках сразу за идентификатором строковой перемен- ной или константы.Например, выражения Str2[l+2] и Str2[7] обеспечат доступ к третьему('Д') и седьмому('2') символам последнего значения переменной Str2 в приведенном выше фрагменте. Запись Str2[0] дает доступ к нулевому байту, содержащему значение текущей длины строки. Значение нулевого байта не должно превышать 255, но нарушение этого правила не вызывает программного прерывания, так как директива компиля- тора R по умолчанию находится в пассивном состоянии {$R—}. Для обеспечения
166 Глава 8 строгого контроля за диапазоном допустимых значении индекса следует перевести директиву R в активное состояние {$R+}. В этом случае компилятор активизирует дополнительные команды для проверки правильности диапазона. Обычно актив- ный режим R устанавливается на стадии отладки программ. Для обработки строковых данных можно использовать специальные процеду- ры и функции. 8.3. СТРОКОВЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ Delete (St,Poz,N) — удаление N символов строки St, начиная с позиции Poz. Если значение Poz > 255, возникает программное прерывание. Значение St Выражение * Результат 'абвгде' Delete(Str, 4,2); 'абве' 'река Волга' Delete(Str, 1, 5); 'Волга' Insert (Strl, Str2, Poz) — вставка строки Strl в строку Str2, начиная с позиции Poz. Например: var Si, S2 : stringfll]; SI := ' ЕС S2 := 'ЭВтеД!*; Insert(SI,S2,4); В результате выполнения последнего выражения значение строки S2 станет равным 'ЭВМ ЕС 184 Г. Str (IBR,St) — преобразование числового значения величины IBR и помещение результата в строку St. После IBR может записываться формат, аналогичный фор- мату вывода. Если в формате указано недостаточное для вывода количество разря- дов, поле вывода расширяется автоматически до нужной длины. Значение IBR Выражение Результат 1500 Str(IBR:6,St) _1500' 4.8Е+03 Str(IBR: 10,St) ' 4800' 76854 Str(—IBR:3,St) '—76854' Vai (St,IBR,Code) — преобразует значение St в величину целочисленного или вещественного типа и помещает результат в IBR. Значение St не должно содер- жать незначащих пробелов в начале и в конце. Code — целочисленная переменная. Если во время операции преобразования ошибки не обнаружено, значение Code равно нулю, если ошибка обнаружена (например, литерное значение переводится в цифровое), Code будет содержать номер позиции первого ошибочного символа, а значение IBR не определено.
Строки 167 Значение St '1450' '14.2Е+02' ’14.2А+02 Выражение Результат Val(St,IBR,Cod) Val(St,IBR,Cod) Val(St,IBR,Cod) Code=0 Code=0 Code=5 Copy (St,Poz,N) — выделяет из St подстроку длиной N символов, начиная с по- зиции Poz. Если Poz > Length(St), то результатом будет пробел; если Poz > 255, воз- никнет ошибка при выполнении. Функция Length описана ниже. Poz, N — цело- численные выражения. Значение St 'ABCDEFG' 'ABCDEFp' Выражение Copy(St, 2, 3) Copy(St, 4, 10) Результат 'BCD' 'DEFG' Concat (Strl,Str2,...,StrN) — выполняет сцепление строк Strl, Str2,..,StrN в том порядке, в каком они указаны в списке параметров. Сумма символов всех сцеплен- ных строк не должна превышать 255, например: Выражение Результат Concat('AA','XX','Y') 'AAXXY' Сопса1('Индекс ','394063') 'Индекс 394063' Length (St) — вычисляет текущую длину в символах строки St. Результат име- ет целочисленный тип, например: Значение St '123456789' 'System 370' Выражение Length(St) Length(St) Результат 9 10 Pos (Strl,Str2) — обнаруживает первое появление в строке Str2 подстроки Strl. Результат имеет целочисленный тип и равен номеру той позиции, где находится первый символ подстроки Strl. Если в Str2 подстроки Strl не найдено, результат равен 0. Значение Strl 'abcdef 'abcdef Выражение Pos('de',Strl) PosCr*,Strl) Результат 4 0 UpCase (Ch) — преобразует строчную букву в прописную. Параметр и резуль- тат имеют литерный тип. Обрабатывает буквы только латинского алфавита, напри- мер: Значение 'd' 'w' Выражение UpCase(Ch) UpCase(Ch) Результат 'D' •W
168 Глава 8 8.3.1. Упражнения Упражнение 1. Составим программу, которая после ввода строки строчных латинских букв заменяет их на прописные. Решение данной задачи можно разделить на три самостоя- тельные части: ввод строки строчных латинских букв, преобразование символов в пропис- ные, вывод полученной строки из прописных букв. Назовем вводимую строку символов S и опишем ее как переменную строкового типа: var S: string; Для обращения к символу, входящему в строку, введем переменную I типа byte, так как в строке может быть не более 255 символов. Для ввода строки используем стандартную функцию Readln(S), для вывода полученной строки из прописных букв используем стан- дартную функцию Writeln(S). Для преобразования символвв строки из строчных в пропис- ные введем подпрограмму-процедуру UpChar. program DemoUpper; (Преобразование строчных букв в ПРОПИСНЫЕ} var S: string; (Описание S — строки переменной длины} begin Write('Введите исходную строку: '); Readin(S); (Ввод исходной строки} UpChar(S);(Преобразование символов строки из строчных в про- писные } Writein(S); (Вывод выходной строки} end. При разработке подпрограммы-процедуры UpChar(S), выполняющей преобразование символов строки из строчных в прописные, используем стандартную функцию UpCase(S[I]) для преобразования строчной латинской буквы в прописную. Действие подпрограммы за- ключается в том, что, начиная с 1-го символа и до конца строки, применяя стандартную функцию UpCase(S[I]) к очередному символу заменяем в строке строчной символ пропис- ным. Так как эта операция повторяется, то будет удобно записать ее циклом for, параметр которого будет изменяться от 1 до величины длины строки, которую вычислит стандартная функция Length(S). Текст процедуры будет таким: procedure UpChar(var S: string); var I: byte; (Локальная переменная I — номер очередного символа в строке} for I:= 1 to Length(S) do (Просматривая с 1-й до последней бук- вы строки} S[I]:= UpCase(S[I]); (Преобразовать очередной символ} end; В результате получится следующая программа: program DemoUpper; (Преобразование строчных букв в ПРОПИСНЫЕ} var S: string; (Описание S — строки переменной длины}
Строки 169 procedure UpChar(var S: string); {Процедура преобразования троки} var I: byte; {Локальная переменная I — номер очередного син- ода в строке} begin for I:= 1 to Length(S) do {Просматривая с 1-й до последней |уквы строки} S[I]:= UpCase(S[I]); {Преобразовать очередной символ} end; >egin {Начало основной программы} Write('Введите исходную строку: '); Readln(S); {Ввод исходной строки} UpChar(S); * {Вызов процедуры преобразования символов строки [3 строчных в прописные с передачей ей параметра-переменной S} Writeln(S); (Вывод выходной строки} md. Запустите интегрированную среду программирования. Введите текст программы )emoUpper и запишите файл на диск под соответствующим именем, а затем откомпилируй- е его. После того как компиляция выполнится успешно, проверьте действие программы, адавая строки текста в нижнем латинском регистре. Упражнение 2. Изменим программу так, чтобы она во введенном слове подсчитывала [исло букв "а" и заменяла их буквами "б". Так как операции ввода-вывода строки анало- ичны, то можно сделать вывод о том, что достаточно заменить процедуру UpChar на про- [едуру ChangeChar и поменять соответствующий вызов. В процедуре ChangeChar, просматривая строку с целью поиска позиции буквы "а", что южно организовать с помощью цикла, используя while и стандартную функцию Posfa', S). Сак только функция Pos обнаруживает первое появление в строке S подстроки "а", она воз- ращает результат — номер позиции буквы "а". Счетчик найденных букв "а" увеличивается ia единицу, а в эту позицию вписывается буква "б" и т. д., пока в строке есть буквы "а". Гекст процедуры может выглядеть следующим образом: >rocedure ChangeChar(var S: string); var N : byte; begin N:=0; {Обнуление числа букв "a"} while Pos('a', S) > 0 do (Если найдена буква "а", то} begin N:=N+1; (Увеличить счетчик букв "а"на 1} S[Pos('a', S)] := 'б';{Записать в позицию буквы "а" букву ’б"} end; Writeln ('В слове было ',N,' букв "а"'); end;
170 Глава 8 С использованием процедуры ChangeChar текст программы, подсчитывающей число букв "а" во введенной строке и заменяющей их буквами "б", будет таким: program Change_Iietter; {Подсчет и замена букв "а" на "б"} var S: string; procedure ChangeChar(var S: string);{Процедура замены буквы "a" на "б"} var N : byte; begin N:=0; (Обнуление числа букв "a"} while Pos('a', S) > 0 do (Если найдена буква "а”, то} begin * N:=N+1; (Увеличить счетчик букв "а"на 1} S[Pos('a', S)] := 'б';(Записать в позицию буквы "а" букву "б"} end; Writein ('В слове было ’ ,N,' букв "а"'); end; begin (Основная программа} Write('Введите исходную строку: '); Readln (S); ChangeChar(S); (Вызов процедуры замены "а" на "б"} Writein('Получилась строка ' ,S) ; end. Запустите интегрированную среду программирования. Введите текст программы Change_Letter и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. После того как компиляция выполнится успешно, проверьте действие программы с отладкой в пошаговом режиме и наблюдением текущих значений переменной S. Упражнение 3. Составим программу, которая запрашивает две строки по четыре сим- вола, состоящие из цифр. Если в строках введены не цифры, выдайте сообщение и прервите работу программы. Программа склеивает введенные строки, затем преобразует исходные строки в числа, подсчитывает их сумму, преобразует результат в строку и печатает строки, полученные в результате склеивания и преобразования суммы чисел в строку. Действие программы можно представить в виде трех самостоятельных фрагментов: ввод первой и второй строк и преобразование их в число, вывод на экран результата склеивания строк и суммирования чисел. Вывод на экран результатов организуем с помощью стандартных про- цедур вывода Writein, а для ввода строк и преобразования их в числа создадим процедуру Inp Str. Для передачи данных между процедурой и основной программой введем формаль- ные параметры-переменные S типа string и X типа integer. Для преобразования введенной строки в число применим стандартную функцию Vai, а для анализа операции преобразова- ния строки в число введем локальную переменную Code целого типа. После преобразования строки в число проверим значение переменной Code, если оно не равно 0, то значит в строке не все символы являются цифрами. Значение Code укажет позицию первого символа в стро-
Строки 171 ке, не являющегося цифрой. В этом случае напечатаем на экране сообщение об ошибке и укажем позицию неверно введенного символа в строке, после чего прервем работу про- граммы, используя стандартную процедуру Halt. Получим следующий текст процедуры InpStr: procedure Inp_Str(var S: string; var X:integer);(Процедура ввода строки цифр и преобразования строки в число} var Cod: integer; {Результат преобразования строки в число} begin Write('Введите строку цифр'); Readln(S); Val(S, X, Cod);(Преобразование строки S в целое число X} if Cod О 0 tften (Если не все символы в строке являются цифра- ми} begin Writein('Ошибка! В позиции ',Cod,' введенной строки не циф- ра'); Halt(l); (Прерывание программы} end; end; С учетом вышесказанного полный текст программы решения задачи будет записан сле- дующим образом: program Demo_Val_Str; var SI, S2 : string; XI, X2 : integer; procedure Inp_Str(var S: string; var X:integer);(Процедура ввода строки цифр и преобразования строки в число} var Cod: integer; (Результат преобразования строки в число} begin Write('Введите строку цифр*); Readln(S); Val(S, X, Cod);(Преобразование строки S в целое число X} if Cod О 0 then {Если не все символы в строке являются циф- рами} begin Writein('Ошибка! В позиции *,Cod,' введенной строки не циф- ра') ; Halt(l); {Прерывание программы} end; end ; begin {Начало основной программы}
172 Глава 8 Inp_Str(SI,X1)l {Вызов процедуры ввора строки с фактическими па- раметрами-переменными S1,X1} Inp_Str(S2,X2); {Вызов процедуры ввода строки с фактическими па- раметрами-переменными S2,Х2} Writeln('Результат склеивания строк -> ',Concat(SI,S2)); Writeln('Сумма введенных чиаел= •,Х1+Х2); end. Запустите интегрированную среду программирования. Введите текст программы Demo Val Str и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. После того как компиляция выполнится успешно, проверьте действие программы с отладкой в пошаговом режиме и наблюдением значений переменных SI, S2, XI, Х2. Упражнение 4. Составим программу, определяющую^ является ли введенное слово пе- ревертышем. Перевертышем называется слово, которое одинаково читается как сначала, так и с конца, например: шалаш, казак. Как видно из определения, для выяснения, является ли слово перевертышем, необхо- димо сравнивать 1-й и последний символ в строке, 2-й и предпоследний, 3-й и предпредпос- ледний символ, и т. д. до середины слова. Если в процессе сравнения будет установлено от- личие сравниваемых символов, т. е. выясняется, что слово читается слева направо иначе, чем справа налево, значит можно сделать вывод, что это слово не является перевертышем. Если в процессе сравнения не будет выявлено отличие сравниваемых символов, значит это слово — перевертыш. Введем следующие переменные: для хранения слов — Word типа string с максимальным размером слов 30 символов и переменную I целого типа, указываю- щую номер позиции сравниваемого символа от начала строки. Заголовок программы можно будет записать следующим образом: program Perev_Word; var I : byte; Word : string[30]; Вначале программа должна выводить сообщение о вводе строки и считывать значение строки в переменную Word. Это можно записать следующим образом: Write('Введите слово '); Readln(Word); Повторяющуюся операцию сравнения первого и последнего символа в строкё, затем второго и предпоследнего и т. д., запишем с помощью цикла for, параметр которого, изме- няясь от 1 до середины строки, будет указывать номер позиции символа от начала строки. Конечное значение параметра цикла установим равным середине слова, целочисленное зна- чение которого вычислим, используя стандартные функции Тпшс и Length. Заголовок опе- ратора цикла запишем следующим образом: for I:=l to Trunc(Length (Word)/2) do В теле цикла запишем оператор сравнения соответствующих символов: if Word[IJOWord[Length (Word)—1+1] . Если результат сравнения соответствующих очередных символов примет значение True, то данная строка не является перевертышем, и дальнейшее сравнение не имеет смыс-
Строки 173 ла. Выведем на экран сообщение, что данное слово не является перевертышем, и завершим цикл процедурой exit, которая передает управление в конец программы. . Если результат сравнения соответствующих очередных символов имеет значение False, то это свидетельствует о том, что данные символы одинаковы, но следует продолжить срав- нение оставшихся, параметр цикла увеличивается на 1, проверяется условие I <= Trunc(Length(Word)/2) и если оно соблюдается, то тело цикла выполняется еще раз. Если условие I <= Trunc(Length(Word)/2) не выполняется, то цикл завершается, и управление передается на оператор WritelnCIIepeBepTbiin'). Программа завершает работу. Полный текст программы можно представить таким образом: program Perev_Word; (Является ли введенное слово перевертышем?} var I : byte; Word : string[30]; begin . Write(* введите слово '); Readln(Word) ; for I:=l to Trunc(Length(Word)/2) do (Проверяем символы пооче- редно от начала до середины слова} begin if Word[I]<>Word[Length (Word)— I+1] then [Если соответствующие символы не одинаковы} begin Writein (* Неперевертыш'); exit (Выход из цикла и завершение программы, дальше не имеет смысла сравнивать } end; end; Writein (' Перевертыш'); end. Запустите интегрированную среду программирования. Введите текст программы Perev Word и запишите файл на диск под соответствующим именем, а затем откомпилируй- те его. После того как компиляция выполнится успешно, проверьте действие программы с отладкой в пошаговом режиме и наблюдением значений переменных Word, I, Word[I], Word[length( Word)—1+1 ]. Упражнение 5. Составим программу, которая обращает введенное слово, т. е. пере- ставляет символы в слове в обратном порядке, например: Петя —,ятеП, мама — амам, про- грамма — аммаргорп. Алгоритм обращения слова можно представить в виде следующей циклической процедуры обмена соответствующих символов: значение первого символа слова запоминается в некоторой переменной символьного типа Ch, затем на место первого символа записывается значение последнего символа, а на его место записывается значение первого символа, хранимое в переменной Ch, после этого аналогично выполняется обмен второго и предпоследнего символа, третьего и предпредпоследнего и т. д., пока не дойдем до середины слова. Далее обмен продолжать не нужно, иначе символы опять займут преж- ние места.
174 Глава 8 Исходя из этих рассуждений, введем следующие переменные: для хранения слова Word типа string с максимальным размером слов 30 символов и переменную I целого типа, ука- зывающую номер позиции сравниваемого символа от начала строки, а также символьную переменную Ch для временного хранения значения символа при обмене. Заголовок про- граммы можно будет записать следующим образом: program Obr_Word; var I : byte; Ch : char; Word : string[30]; Аналогично предыдущей программе вначале выведем на экран запрос о вводе слова и считаем с клавиатуры значение слова в переменную Word. Этот фрагмент программы мож- но записать следующим образом: Write('введите слово '); Readln(Word); После ввода значения слова выполнение циклической процедуры обмена значений со- ответствующих символов (первого и последнего, второго и предпоследнего символов, третьего и. предпредпоследнего и т. д. до середины слова) запишем в виде цикла for, пара- метр которого, изменяясь от 1 до середины слова, значение которого определяется резуль- татом выражения Trunc(Length(Word)/2), указывает на позицию очередного символа в сло- ве. Обмен соответствующих символов в слове с использованием символьной переменной Ch запишем следующим образом: Ch:=Word[I];(переменной Ch присвоили значение I-го символа от начала слова} Word[I]:=Word[Length(Word)—1+1];(в I-ю позицию записали значе- ние I-го символа от конца (Length(Word)—I+1-го от начала) слова) Word[length(Word)—1+1]:=Ch; {в Length(Word)—I+1-ю позицию от начала слова записали значение I-го символа, временно хранимое в переменной Ch) В заключительной части программы выведем значение "обращенного" слова на экран компьютера с помощью стандартной процедуры вывода: Write (* Получилось слово *,Word) Полный текст этой программы будет таким: program Obr_Word; var I : byte; Ch : char; Word : string[30]; begin Write(* введите слово ') ; Readln(Word); for I:=l to Trunc(Length (Word)/2) do {Перебирая символы пооче- редно от начала до середины слова)
Строки 175 begin (Обмениваем соответствующие символы} Ch:=Word[I]; Word[I]:=Word[Length (Word) —1+1]; Word[Length(Word)—1+1]:=Ch; end; Write('Получилось слово ',Word); end. Запустите интегрированную среду программирования. Введите текст программы Obr Word и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, проверьте действие программы с от- ладкой в пошаговом режиме и наблюдением значений переменных Word, I, Word[I], Ch, Word[length( Word)—1+1 ]. i Задачу реверсирования (обращения текста) можно решить и с помощью следующей программы с рекурсивным вызовом подпрограммы-функции, например: program Rever_Str; type stroka = string[30]; var S, Rev_S : stroka; function Reverse(Str:stroka) : stroka; var FirstChar : char; {Первый символ в строке} OstatokStr : stroka; (Остаток строки после удаления пер- вого символа} begin if Length(Str)=1 then Reverse := Str (Завершение рекурсии} else begin FirstChar:=Str[1]; Delete(Str,1,1); OstatokStr:=Reverse(Str) ; (Рекурсивные вызовы функции} Reverse:=Concat(OstatokStr,FirstChar); end; end; begin (Начало основной программы} Write('Введите любой текст'); Readin(S); Rev_S:=Reverse(S); (Вызов функции с параметром-значением S} Writein(Rev_S,' есть перевернутое *,S); end. Упражнение 6. Изучите текст программы ReverStr. Запустите интегрированную среду программирования. Введите текст программы Rever Str и запишите файл на диск под соот-
176 Глава 8 ветствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, проверьте действие программы с отладкой в пошаговом режиме и наблюдением значений переменных Str, FirstChar, OstatokStr. Упражнение 7. Изучите текст программы, содержащей один из вариантов реализации алгоритма "бегущая строка". program DemoStringGo; {Программа "бегущая строка"} uses Crt; {Подключение специализированного модуля, содержащего стандартные процедуры и функции управления дисплеем и клавиатурой компьютера} type Stroka = string[160]; var Vhod: Stroka; procedure GoString (X,Y:byte; InSt:Stroka);{X,Y — координаты "бегущей строки", InSt — формальный параметр-значение типа Stroka для передачи из основной программы текста сообщения, который дол- жен "бежать" на экране} var Stl: Stroka; ' I: byte; begin {Начало GoString} Stl:=’ {Начальное значе- ние строки Stl} ClrScr; {Очистить экран} Stl:= Stl+InSt; {Приклеивание к строке Stl значения InSt спра- ва} for I:=l to Length(Stl) do begin Delete(Stl,1,1); {Удаление 1 символа из строки Stl, начи- ная с позиции 1 и смещение за счет этого строки Stl влево на 1 символ} GoToXY(X,Y); {Стандартная процедура специализированного модуля Crt, предназначенная для перемещения курсора в позицию с координатами X, Y} Write(Stl); Sound(1000); {Звуковое сопровождение движения строки} Delay(5); NoSound Delay(90); DelLine {Стандартная процедура специализированного моду- ля Crt, предназначенная для удаления строки, отмеченной курсором} end end; {Конец GoString}
Строки 177 begin {Начало основной программы DemoStringGo} GoString(1, 10, 'Для получения бумажной копии включите прин- тер!!!'); {Вызов процедуры GoString с параметрами — значениями 1,10 — координатами начала строки и фактическим параметром- значением — текстом сообщения} Vhod:= 'Установите бумагу !!!'; GoString(1, 14, Vhod) {Вызов процедуры GoString с пара- метрами — значениями 1, 14 — координатами начала строки и фактиче- ским параметром-переменной Vhod — текстом сообщения} end. {Конец основной программы DemoStringGo} Как видно из текста программы, эффект "бегущей строки” создается циклическим уда- лением из строки, выводимой на экран, заранее приклеенных к ней слева пробелов. Запустите интегрированную среду программирования. Введите текст программы DemoStringGo и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. После того как компиляция выполнится успешно, задайте для просмотра в окне отладчика величины I, X, Y, Vhod, Stl. Установите видимыми одновременно окна редакто- ра с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с заходом в процедуру GoString и пронаблюдайте за изменениями значений переменных в ос- новной программе и в подпрограмме-процедуре, обратите внимание на передачу парамет- ров при вызове процедуры. Сфера применения процедуры GoString весьма широка: от вывода сопровождаемых звуковым сигналом предупредительных и аварийных сообщений до организации элементов меню. Упражнение 8. Измените предыдущую программу так, чтобы она обеспечивала ввод имени пользователя и при первом вызове процедуры GoString печатала “бегущую строку” — приветственное сообщение типа 'Здравствуйте, '+Name, а при втором вызове процедуры GoString переменной Vhod присваивалось значение 'До свидания, '+Name. Имя пользовате- ля можно представить как строку длиной 30 символов. Упражнение 9. Изучите программу, удаляющую в строке все, что заключено между фигурными скобками и их самих. Например, имеется 111 {333}999, надо получить 111999. Эта программа моделирует работу компилятора языка Паскаль для удаления из компили- руемого кода комментариев. program Demo_Del; var S: string; I, J: integer; begin Write('Введите строку: '); Readln(S); {Ввод исходной строки} repeat I:= Pos('{', S); {Определили позицию левой "{"скобки} if I > 0 then begin
178 Глава 8 J:= Pos(’)’, S); {Определили позицию правой "}"скобки} if (J > I) then {Если правая скобка правее левой, то} Delete(S, I, J—1+1) {Удалить из строки подстроку с позиции левой "{" скобки по позицию правой "}" скобки} else {В случае если "}" расположена левее "{"} begin Writein('Неверно введены комментарии'); 1:= О; {Выход из цикла} end; end; until 1=0; Writein(S); {Выходная строка} Readln; Ь end. Запустите интегрированную среду программирования. Введите текст программы Demo_Del и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, задайте для просмотра в окне отлад- чика величины I, J, S. Установите видимыми одновременно окна редактора с текстом про- граммы и окно просмотра. Исполните программу в пошаговом режиме и пронаблюдайте за изменениями значений переменных. Контрольные вопросы н задания Вопросы. 1. Что такое строка? 2. Каким идентификатором определяются данные строкового типа? 3. Какова максимально возможная длина строки? Как определить.текущую длину стро- ки? 4. Какие выражения называются строковыми? 5. Какие операции допустимы над строковыми данными? 6. Каким образом производится сравнение строк? 7. Какие требования предъявляются к записи выражений с операндами строкового и литерного типа? 8. Как можно обратиться к отдельным символам строки? 9. Назначение специальных процедур и функций обработки данных строкового типа. Приведите примеры. Задания. 1. Напишите программу, подсчитывающую количество букв во введенном с клавиатуры слове. Ввод осуществляйте в цикле while do. Выход из программы — строка ’999'. 2. Напишите программу, подсчитывающую количество вхождений заданной вами бук- вы в введенной строке.
Строки 179 3. Напишите программу, которая вводит строку и выводит ее, сокращая каждый раз на 1 символ до тех пор, пока в строке не останется 1 символ. 4. Напишите программу, определяющую число слов в строке. Одно слово от другого отделяется 1 пробелом. 5. Составьте программу, определяющую, является ли введенное слово числом. 6. Введите 2 целых числа. Преобразуйте числа в две строки, объедините их в одну строку и выведите на экран результат. 7. Напишите программу, которая удаляет из введенной строки любой требуемый вве- денный с клавиатуры символ. Процесс удаления выделите в отдельную процедуру DelChlnString, строку и символ определите как глобальные переменные. В результате у вас должна получиться протрамма-модель работы одного из режимов любого текстового редак- тора. 8. Составьте программу, удаляющую все пробелы из введенной строки. Для удаления постройте отдельную функцию NewSt и примените в ней оператор Repeat и функцию Pos. 9. Напишите программу, сортирующую символы введенной с клавиатуры строки в по- рядке возрастания их номеров в ASCII-таблице. Например, если введено: 'СВА', в результа- те надо получить 'АВС'. 10. Вычислите длину самого короткого слова в предложении из трех слов, разделен- ных пробелами. 11. Выясните, какая из букв первая или последняя встречается в заданном слове чаще. 12. Задано существительное первого склонения, оканчивающееся на "а". Напечатайте это слово во всех падежах. 13. Сколько букв "у" в слове стоит на четных местах? 14. Замените в заданном слове все буквы "о" пробелами. 15. В тексте, состоящем из латинских букв и заканчивающемся точкой, подсчитайте количество гласных букв. 16. Даны два слова. Поменяйте местами буквы этих слов, занимающие одинаковые по- зиции. 17. Заданы фамилия, имя и отчество учащегося, разделенные пробелом. Напечатайте его фамилию и инициалы. 18. Вычеркните z-ю букву слова. 19. Дан текст, в котором слова разделены пробелами. а) сколько слов в тексте? б) найдите самое длинное слово текста (длина текста 100 символов). 20. Задан текст, состоящий из слов, которые разделены одним или несколькими пробе- лами. Сформируйте новый текст, включив в него слова заданного, разделенные только одним пробелом. 21. Сложное слово состоит из двух частей одинаковой длины и соединительной глас- ной. Найдите обе части этого слова. 22. Вычеркните из заданного слова все буквы, совпадающие с его последней буквой. 23. Вычеркните из слова X те буквы, которые встречаются в слове Z.
180 Глава 8 24. Подсчитайте число различных букв в слове. 25. Составьте программу подсчета сколько раз в тексте встречается заданный фрагмент (цепочка символов). Например, в тексте "банан упал на барабан" фрагмент "ба" встречается 2 раза. 26. Составьте программу, которая по числу<1000, написанному арабскими цифрами, формирует его название. 27. Составьте программу, которая по названию числа<1000, написанному на русском (английском) языке, формирует его цифровую запись. 28. Даны два слова. Составьте программу, определяющую можно или нет из букв слова А составить слово В. 29. Составьте программу перевода строки строчных русских букв в прописные. 30. Составьте программу, вычеркивающую каждую третью букву слова X. 31. Составьте программу подсчета числа одинаковых букв, стоящих на одних и тех же местах в словах X и Y. 32. Составьте программу, выясняющую, на гласную или согласную букву оканчивается слово X. 33. Составьте программу вычисления суммы мест, на которых в слове х стоят буквы “в” и ”п”. 34. Составьте программу шифрования текстового сообщения. Можно использовать та- кой способ шифрования. Шифровальщик задает ключ шифровки — целое число, который определяет величину смещения букв русского алфавита, например: ключ = 3, тогда в тексте буква "а" заменяется на "г" и т. д. Используются все буквы русского алфавита. Е считается дважды. 35. Составьте программу дешифрования текстового сообщения, зашифрованного про- граммой из задачи 30.
Глава 9. МАССИВЫ * Рассмотренные ранее простые типы определяют различные множества атомар- ных (неразделимых) значений. В отличие от них структурные типы задают множе- ства сложных значений, каждое из которых образует совокупность нескольких зна- чений другого типа. В структурных типах выделяют регулярный тип (массивы). 9.1. ОПИСАНИЕ ТИПА “МАССИВ” С понятием “массив” приходится сталкиваться при решении научно-техни- ческих и экономических задач обработки совокупностей большого количества зна- чений. В общем случае массив — это структурированный тип данных, состоящий из фиксированного числа элементов, имеющих один и тот же тип. Название регулярный тип (или ряды) массивы получили за то, что в них объе- динены однотипные (логически однородные) элементы, упорядоченные (урегули- рованные) по индексам, определяющим положение каждого элемента в массиве. В качестве элементов массива можно использовать и любой другой ранее опи- санный тип, поэтому вполне правомерно существование массивов записей, масси- вов указателей, массивов строк, массивов массивов и т.д. Элементами массива мо- гут быть данные любого типа, включая структурированные. Тип элементов массива называется базовым. Особенностью языка Паскаль является то, что число элемен- тов массива фиксируется при описании и в процессе выполнения программы не ме- няется. Элементы, образующие массив, упорядочены таким образом, что каждому эле- менту соответствует совокупность номеров (индексов), определяющих его место- положение в общей последовательности. Доступ к каждому отдельному элементу осуществляется путем индексирования элементов массива. Индексы представляют собой выражения любого скалярного типа, кроме вещественного. Тип индекса оп- ределяет границы изменения значений индекса. Для описания массива предназна- чено словосочетание array of (массив из).
182 Глава 9 Синтаксическая диаграмма регулярных типов имеет следующий вид: Исходя из синтаксической диаграммы, формат записи будет таким: type <имя типа> = array[тип индекса] of <тип компонента>; var Идентификатор,. . . > : <имя типа>; Массив может быть описан и без представления типа в разделе описания типов данных: var Идентификатор,...> : array[тип индекса] of <тип компонента>; Пример. type Klass = (KI, К2, КЗ, К4) ; Znak = array[1..255] of char; var Ml: Znak; {Тип Znak предварительно описан в разделе типов} М2: array[1..6O] of integer; {Прямое описание массива М2} М3: array[1..4] of Klass; Mas: array[1..4] of integer; Если в качестве базового типа взят другой массив, образуется структура, кото- рую принято называть многомерным массивом. Пример. type Vector = array[1..4] of integer; Massiv = array[1..4] of Vector; var Matr : Massiv; Ту же структуру можно получить, используя другую форму записи: var Matr : array[1..4,1..4] of integer; Если в такой форме описания массива задан один индекс, массив называется одномерным, если два индекса'— двумерным, если п индексов — п-мерным. Од- номерный массив соответствует понятию линейной таблицы (вектора), двумерный — понятию прямоугольной таблицы (матрицы, набору векторов). Размерность ог- раничена только объемом памяти конкретного компьютера. Одномерные массивы
Массивы 183 обычно используются для представления векторов, а двумерные — для представле- ния матриц. Пример. var VectorZ: array[1..4Q] of real; {Одномерный массив из 40 эле- ментов } MatrU : array[1..8,1..8] of byte; {Двумерный массив 8x8 эле- ментов } Trilf : array[1..4,1..5,1..8] of integer; {Трехмерный массив} Для описания массива можно использовать предварительно определенные кон- станты: const G1 = 4; G2*= 6; var MasY : array[1..G1,1..G2] of real; Элементы массива располагаются в памяти последовательно. Элементы с меньшими значениями индекса хранятся в более низких адресах памяти. Много- мерные массивы располагаются таким образом, что самый правый индекс возраста- ет самым первым. Например, если имеется массив А:аггау[1..5,1..5] of integer; то в памяти элемен- ты массива будут размещены по возрастанию адресов: А[1,1] А[1,2] А[1,5] А[2,1] А[2,2] А[5,5] Контроль правильности значений индексов массива может проводиться с по- мощью директивы компилятора R. По умолчанию директива R находится в пас- сивном состоянии {$R—}. Перевод в активное состояние вызывает проверку всех индексных выражений на соответствие их значений диапазону типа индекса. Существует различие между регулярными типами в языке Паскаль и массива- ми в некоторых других языках программирования, заключающееся в том, что в Паскале количество элементов массива всегда должно быть фиксировано, т. е. оп- ределяться при трансляции программы. Это считается недостатком языка, так как не во всех программах можно заранее предсказать необходимый размер массива (который может определяться в зависимости от тех или иных условий, возникаю- щих в процессе исполнения). В программах, обрабатывающих массивы, помимо использования для определения размера массива предварительно определенных констант иногда используется прием, позволяющий имитировать работу с массива-
184 Глава 9 ми переменной длины, который заключается в следующем: в разделе описания констант предварительно определяют возможное максимальное значение размера массива, а затем в программе запрашивают текущее значение размера и использу- ют это значение далее при заполнении и обработке массива. 9.1.1. Действия над массивами Для работы с массивом как единым целым используется идентификатор масси- ва без указания индекса в квадратных скобках. Массив может участвовать только в операциях отношения "равно", "не равно" и в операторе присваивания. Массивы, участвующие в этих действиях, должны быть идентичны по структуре, т. е. иметь одинаковые типы индексов и одинаковые типы компонентов. Например, если мас- сивы А и В описаны как var А, В : array[1..20] of real; то применение к ним допустимых операций даст следующий результат: Выражение Результат А=В True, если значение каждого элемента массива А равно соответствующему значению элемента массива В АоВ True, если хотя бы одно значение элемента массива А не равно значению соответствую- щего элемента массива В А:=В Все значения элементов массива В присваиваются соответствующим элементам массива А. Значения элементов массива В остаются неизменны 9.1.2. Действия над элементами массива После объявления массива каждый его элемент можно обработать, указав идентификатор (имя) массива и индекс элемента в квадратных скобках. Например, запись Mas[2], VectorZ[10] позволяет обратиться ко второму элементу массива Mas и десятому элементу массива VectorZ. При работе с двумерным массивом указыва- ются два индекса, с n-мерным массивом — п индексов. Например, запись MatrU[4,4] делает доступным для обработки значение элемента, находящегося в четвертой строке четвертого столбца массива MatrU. Индексированные элементы массива называются индексированными перемен- ными и могут быть использованы так же, как и простые переменные. Напри- мер, они могут находиться в выражениях в качестве операндов, использоваться в операторах for, while, repeat, входить в качестве параметров в операторы Read, Readin, Write, Writein; им можно присваивать любые значения, соответствующие их типу. Рассмотрим типичные ситуации, возникающие при работе с данными типа array. Для этого опишем три массива и четыре вспомогательные переменные:
Массивы 185 var A,D : array[1..4] of real; В : array[1..10,1..15] of integer; I, J, К : integer; S : real; Инициализация (присваивание начальных значении) массива заключается в присваивании каждому элементу массива одного и того же значения, соответст- вующего базовому типу. Наиболее эффективно эта операция выполняется с помо- щью оператора for, например: for I := 1 to 4 do A[I] := 0; Для инициализации двумерного массива обычно используется вложенный оператор for, например: for I 1 Ц 10 do for J := 1 to 15 do B[I,J] := 0; Паскаль не имеет средств ввода-вывода элементов массива сразу, поэтому ввод и вывод значений производится поэлементно. Значения элементам массива можно присвоить с помощью оператора присваивания, как показано в примере инициализации, однако чаще всего они вводятся с экрана с помощью оператора Read или Readln с использованием оператора организации цикла for: for I:=l to 4 do Readln(A[I]) ; Аналогично значения двумерного массива вводятся с помощью вложенного оператора for: for I := 1 to 10 do for J := 1 to 15 do Readln (B[I,JJ); В связи с тем что использовался оператор Readln, каждое значение будет вво- диться с новой строки. Можно ввести и значения отдельных элементов, а не всего массива. Так, операторами Read(A[3]); Read(B[6,9]); вводится значение третье- го элемента вектора А и значение элемента, расположенного в шестой строке девя- того столбца матрицы В. Оба значения набираются на одной строке экрана, начи- ная с текущей позиции расположения курсора. Вывод значений элементов массива выполняется аналогичным образом, но ис- пользуются операторы Write или Writein: for I := 1 to 4 do Writein (A[I]); {Вывод значений массива A} или for I := 1 to 10 do for J := 1 to 15 do Writein (B[I,J]); {Вывод значений массива В} Копированием массивов называется присваивание значений всех элементов одного массива всем соответствующим элементам другого массива. Копирование
186 Глава 9 можно выполнить одним оператором присваивания, например А:= D; или с помо- щью оператора for: for I := 1 to 4 do A[I] := D[I]; В обоих случаях значение элементов массива D не изменяется, а значения эле- ментов массива А становятся равными значениям соответствующих элементов массива D. Очевидно, что оба массива должны быть идентичны по структуре. Иногда требуется осуществить поиск в массиве каких-либо элементов, удовле- творяющих некоторым известным условиям. Пусть, например, надо выяснить, сколько элементов массива А имеют нулевое значение. Для ответа на этот вопрос введем дополнительную переменную К и воспользуемся операторами for и if: К := 0; for I := 1 to 4 do * if A[I] = 0 then К := К + 1; После выполнения цикла переменная К будет содержать количество элементов массива А с нулевым значением. Перестановка значений элементов массива осуществляется с помощью до- полнительной переменной того же типа, что и базовый тип массива. Например, так запишется фрагмент программы, обменивающий значения первого и пятого эле- ментов массива А: Vs: = А[5]; {Vs — вспомогательная переменная} А[5]:= А[1]; А[1]:= Vs; Упражнение 1. Составим программу, которая формирует одномерный массив тремя способами (по формуле, случайным образом, вводом элементов с клавиатуры) и выводит полученный в каждом случае массив на экран. В начале программы опишем переменные: массив Massiv из десяти вещественных чи- сел и целую переменную I, которая будет выполнять функции параметра цикла и использо- ваться для указания номера (индекса), определяющего местоположение элемента в массиве. Описание переменных будет представлено следующим образом: var Massiv : array [1..10] of real; I: integer; Основная часть программы будет представлена тремя отдельными частями, каждая из которых иллюстрирует разный способ формирования одномерного массива. Цервый фраг- мент — формирование одномерного массива по формуле. Так как инициализация массива для всех его элементов выполняется одинаковым образом, т. е. операция повторяющаяся и число повторений заранее известно, то она может быть записана оператором цикла с пара- метром. Если каждый элемент массива равен утроенному значению его порядкового номера (индекса), то этот фрагмент программы можно записать так: for I:=l to 10 do Massiv[I]:= 1*3;
Массивы 187 Во второй части программы формирование одномерного массива выполним, задавая значение каждого элемента результатом случайной функции Random(I). Для использования этой функции подключим в программу стандартный модуль Crt командой: uses Crt. Эта часть программы также выполняется циклически, поэтому инициализацию массива зададим циклическим оператором for, в теле которого выполняется вычисление случайного числа функцией Random(Count), после чего это значение присваивается очередному I-му элементу массива. Randomize; for I:=l to 10 do Massiv[I]:=Random(I) ; В третьей части программы формирование одномерного массива выполняется повто- ряющимся вводом с клавиатуры значений каждого его элемента, поэтому он может быть за- писан следующим образам: for I:=l to 10 do begin Write(* Введите ',I,'-й элемент массива *); Readin(Massiv[I]); end; Для того чтобы просмотреть значения созданного массива, необходимо выводить их на экран. Поскольку это требуется делать три раза, то оформим вывод значений элементов мас- сива в виде отдельной процедуры, которая получает из точки вызова основной программы фактическое значение локальной переменой Msg — сообщение о том, каким способом сформиррваны значения элементов массива, печатает его на экране и выводит значения всех элементов массива. Текст процедуры может быть записан так: procedure Out_Massiv(Msg:string); {Процедура вывода массива на экран} var J : integer; begin Writein (Msg) ; for J:=l to 10 do Writein(J,'-й элемент массива —’,Massiv[J]:5); Writein; end; Для вызова процедуры после каждой'самостоятельной части программы запишем ее имя, а в качестве фактического параметра-значения укажем текст сообщения, выводимого ею перед печатью значений массива, например: Out Massiv(’одномерный массив, элементы которого = 1*3’). В целом текст программы будет выглядеть следующим образом: program Demo_massivl; uses Crt; var Massiv : array [1..10] of real; I : integer;
188 Глава 9 procedure Out_Massiv(Msg:string); {Процедура вывода массива на экран} var J : integer; begin Writein (Msg) ; for J:=l to 10 do Writein(J,'-й элемент массива —',Massiv[J]:5); Writein; end; begin {Основная программа} for I:=l to 10 do {Формирование одномерного массива по формуле } Massiv[!]:= 1*3; * {Вызов процедуры вывода массива на экран с передачей ей параметра- значения текстового сообщения} Out_Massiv('одномерный массив, элементы которого =1*3'); Randomize; {Формирование одномерного массива случайной функцией Random(I)} for I:=l to 10 do Massiv[I]:=Random(I); Out_Massiv('одномерный массив случайных значений функции Random(20)'); for I:=l to 10 do {Формирование одномерного массива вводом с клавиатуры} begin Write('Введите ',1,'-й элемент массива '); Readln(Massiv[I]); end; Out_Massiv('одномерный массив, введенный с клавиатуры'); end. Запустите интегрированную среду программирования. Введите текст программы Demo massivl и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. После того как компиляция выполнится успешно, проверьте действие программы. Упражнение 2. Составим программу, которая формирует одномерный массив вводом с клавиатуры, находит в массиве элементы, заданные пользователем, подсчитывает их коли- чество и выводит номер первого найденного элемента. В разделе описания констант укажем значение константы Count=30. В программе зна- чение этой константы определяет количество элементов массива. Употребление константы в описании размеров массива предпочтительнее, так как в случае изменения размеров массива вам не нужно будет вносить изменения во весь текст программы, а достаточно только один раз указать в разделе описания констант новое значение константы Count. В связи с этим описание массива зададим так: М : array [1..Count] of Byte;
Массивы 189 Введем переменные целого типа: N — значение искомого элемента; А — но- мер первого элемента массива, значение которого равно N; В — количество таких элементов в массиве; I — переменная, выполняющая функции параметра цикла и одновременно служащая указателем номера очередного элемента массива. Массив элементов опишем в разделе констант как упорядоченный набор целых данных типа byte (2,2,3,4,5,2,7,8,9,2). Тогда раздел описания констант и переменных в про- грамме будет таким: const Count = 10; М : array [1..Count] of byte=(2,2,3,4,5,2,7,8,9,2) ; var N, А, В, I : Byte; В начале программы распечатаем на экране значения элементов массива: Writeln('Исходный массив:'); for I := 1 to Count do Write(M[I]:3,' '); Writeln; Writeln; В связи с тем, что ни один подходящий элемент еще не найден, присвоим переменным А и В значение 0. Затем выведем на экран приглашение на ввод значения искомого элемента и считаем это значение с клавиатуры. На Паскале это запишется следующим образом: Write('Введите значение элемента массива для поиска >’); Readln(N); % Поиск элемента массива, значение которого равно введенному числу N, выполняется в циклическом сравнении значений всех элементов от первого до последнего со значением числа N, поэтому запишем его в виде цикла с параметром. Оператор if M[I] = N then-выпол- няет сравнение значения очередного элемента массива с заданным значением N. Если усло- вие M[I] = N выполняется, то счетчик числа найденных элементов В увеличивается на еди- ницу. Так как нам требуется найти номер первого элемента, т. е. при первом выполнении условия M[I] = N запомнить номер данного элемента, то можно записать: if В = 0 then А := I. Тогда блок поиска нужного элемента можно записать так: for I := 1 to Count do if M[I] = N then begin if В = 0 then A := I; В := В + 1; end; В заключительной части программа должна вывести на экран сообщение о том, что в массиве нет искомых элементов, или сообщение о количестве элементов массива, имеющих значение, равное N, и напечатать номер первого такого элемента. Это можно записать сле- дующим образом: if В=0 then Writeln(* Нет таких элементов в массиве') else
190 Глава 9 begin Writein('Количество элементов массива, имеющих значение • ,N, ’ - ’ , В) ; . Writein ('Первый элемент — ', Air- end; В целом текст программы будет таким: program Find_Elem; {Поиск элемента в массиве} const Count = 10; М : array [1..Count] of byte=(2,2,3,4,5,2,7,8,9,2); var N, А, В, I : Byte; i_ * begin Writein('Исходный массив:'); for I := 1 to Count do Write(M[I]:3,' '); Writein; Writein; A := 0; {Нет элемента с таким значением) В := 0; {Пока не найдено ни одного элемента} Write('Введите Значение элемента массива для поиска >'); Readln(N); for I := 1 to Count do {Поиск элемента, значение которого =N) if M[I] = N then begin if В = 0 then A := I; {Запомнить номер первого элемента, равного N) В := В + 1;{Увеличить число найденных элементов на 1} end; if В=0 then Writein('Нет таких элементов в массиве') else begin Writein('Количество элементов массива, имеющих значение ' ,N, ' - ' , В) ; Writein('Первый элемент — ', А) ; end; end. Запустите интегрированную среду программирования. Введите текст программы Find Elem и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того Как компиляция выполнится успешно, проверьте действие программы, зада- вая различные значения искомых элементов, например: 2,5,9. Упражнение 3. Составим программу, которая формирует одномерный массив, выпол- няет поиск элемента с заданными свойствами и удаляет его из массива. В разделе описания укажем количество элементов массива Count и зададим целые зна- ‘ чения элементов массива:
Массивы 191 Count = 10; {Размер массива} А : array [1..Countjof byte=(33,43,2,33,90,78,60,33,33,21); Назовем элемент, который нужно найти и удалить из массива, именем X. Решение за- дачи сводится к поиску в массиве элемента, значение которого равно указанному, удале- нию его из массива и сжатию массива, т. е. переписыванию элементов массива от позиции, следующей за удаленным элементом, влево на одну и обнулению самого правого элемента в массиве. Для циклического просмотра массива используем цикл с параметром и для его ор- ганизации введем переменную I. Так как в массиве может быть несколько удаляемых эле- ментов, то целесообразно, удаляя эти элементы, полученный вследствие сжатия массива "хвост" массива обнулить отдельным фрагментом программы, поэтому введем целую пере- менную J — параметр цикла для смещения хвоста. Для подсчета количества неудаленных элементов массива введем целую переменную N. В начале программыЬзапишем блок вывода массива на экран компьютера, затем запрос на ввод значения удаляемого элемента и считывание его с клавиатуры в переменную X. Это запишется следующим образом: Writein(•Исходный массив {Вывод массива на экран} for X:=l to Count do Write(a[I],' '); Writein; Write('Введите удаляемый элемент : ' ) ; Readln(X); Перед поиском указанного элемента в массиве установим, что ни один элемент еще не просмотрен (1:=0), ни один элемент массива не удален и поэтому размер удаляемого "хво- ста" массива J:=0, а количество неудаленных элементов в массиве равно исходному N:=Count. Повторяющуюся процедуру поиска в массиве удаляемого элемента запишем в виде оператора повтора repeat, в котором циклически сравнивается значение очередного (1-го) элемента массива со значением переменной X. Если условие А[1]=Х выполняется, то все элементы, расположенные правее I-го, сдвигаются влево на один. При этом I-й элемент за- меняется I+1-м и т. д. Так как один элемент удален из массива, то оператором N:=N—1 из рассмотрения исключается 1 элемент массива (он входит теперь в "хвост" массива, кото- рый после удаления всех искомых элементов нужно будет обнулить). Поиск и удаление эле- мента массива запишется следующим образом: repeat {Просматривать все элементы массива} I:—1+1; while A[I]=X do {Если очередной элемент массива совпадает с указанным} begin J:=I; repeat {Сдвинуть все элементы от J+1 и до конца массива влево на 1 позицию) A[J]:=A[J+1]; J:=J+1; until J>=N; N:=N—1; {Исключить из рассмотрения 1 элемент массива}
192 Глава 9 end; until I>=N; {Завершен просмотр массива из оставшихся элементов} После завершения поиска всех удаляемых элементов массива необходимо обнулить "хвост", в котором хранятся копии последнего элемента массива, записанные туда при по- следовательном сдвиге элементов влево при удалении. Эта операция запишется так: if N<Count then {Обнуление хвоста} for I:=N+1 to Count do A[I]:=O; В заключительной части программы запишем вывод на экран измененного массива сле- дующим образом: Writeln('Полученный массив :')/{Печать полученного массива} for I:=l to Count do Write(A[IJ,' '); Writeln; В целом текст программы решения задачи поиска и удаления элемента массива запи- шется таким образом: program Del_Elem; {Удаление указанного элемента массива} const Count = 10; {Размер массива} A:array [1..Count] of byte=(33,43,2,33,90,78,60,33,33,21); var X, {Элемент, который надо удалить} J, {Параметр цикла для смещения хвоста} I, {Параметр цикла поиска) N : byte; {Количество ненулевых элементов} begin Writeln(*Исходный массив :'); {Вывод массива на экран} for I:=l to Count do Write(a[I],' '); Writeln; Write('Введите удаляемый элемент : '); Readln(X); N:=Count; J:=0; I:=0; {Инициализация} repeat {Просматривать все элементы массива} I:=1+1; while A[I]=X do {Если очередной элемент массива совпадает с указанным} begin J:=I; repeat {Сдвинуть все элементы от J+1 и до конца массива влево на 1 позицию} A[J]:=A[J+1]; J:=J+1; until J>=N; N:=N—1; {Исключить из рассмотрения 1 элемент массива} end; until I>=N; {Завершен просмотр массива из оставшихся элементов}
Массивы 193 if N<Count then {Обнуление хвоста} for I:=N+1 to Count do A[I]:=0; x Writein('Полученный массив {Печать полученного массива} for I:=l to Count do Write(A[I],' ’); Wtiteln; end. Запустите интегрированную среду программирования. Введите текст программы Del_Elem и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, проверьте действие программы. Упражнение 4. Составим программу, которая формирует одномерный массив случай- ных чисел, выполняет поиск максимального элемента массива, а затем выводит на экран его значение и порядковый номер в массиве. В разделе описания запишем размер массива Count=20, опишем массив целых чисел М следующим образом: М : array [1..Count] of byte. Используем целые переменные для хране- ния значений максимального элемента массива — Мах, его индекса — Numer^Max; В начале программы присвоим элементам массива значения случайных целых чисел и выведем список элементов на экран: Randomize; {формирование одномерного массива случайной функцией Random(Count*2)} for I := 1 to Count do begin M[I] := Random(Count*2) + 1; Write'); end; Writein; Перед началом поиска максимального элемента допустим, что его первый элемент и яв- ляется максимальным элементом, а его индекс указывает позицию максимального элемента в массиве. Это запишется так: Мах := М[1]; {Считать 1-й элемент максимальным} Numer_Max:=1; {Запомнить номер максимального элемента} Повторяющийся просмотр массива с поиском максимального элемента, начиная со вто- рого, выполняется оператором повтора с параметром, который одновременно указывает ин- декс очередного элемента. Сравнение очередного элемента массива с максимальным осуще- ствляется оператором if M[I] > Max then. Если очередной элемент массива больше, чем мак- симальный, то следует считать его значение максимальным и запомнить его индекс. Данный фрагмент программы запишется таким образом: for I := 2 to Count do {Проверить все элементы, начиная со второ- го} f begin if M[I] > Max then {Если очередной (I-й) элемент массива больше чем Мах} begin Мах := М[1]; {то считать максимальным I-й элемент} 7-116
194 Глава 9 Numer_Max:=1; {и запомнить его порядковый номер} end; end; В заключительной части программы запишем вывод результата поиска максимального элемента массива: Writein('Максимальный элемент — Мах); Writein('Он расположен на ', Numer_Max,' месте'); Полный текст программы получится таким: program Max_Elem; {Поиск максимального элемента массива} const Count = 20; var ь М : array [1..Count] of byte; Max, I, Numer_Max : byte; begin Randomize; {Формирование одномерного массива случайной функцией Random(Count*2) и вывод массива на экран} for I := 1 to Count do begin M[I] := Random(Count*2) +1; Write(M[I],' '); end; Writein; Max := M[l]; {Считать 1-й элемент максимальным} Numer_Max:—1; {Запомнить номер максимального элемента} for I := 2 to Count do {Проверить все элементы, начиная со второ- го} begin if M[I] > Max then {Если очередной (I-й) элемент массива больше чем Мах} begin t Max := M[I]; {To считать максимальным I-й элемент} Numer_Max:—I; {И запомнить его порядковый номер} end; end; Writeln('Максимальный элемент — ', Мах) ; Writein('Он расположен на ', Numer_Max,' месте'); end. Запустите интегрированную среду программирования. Введите текст программы Max Elem и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, несколько раз запустите программу на исполнение. Просматривая экран пользователя, убедитесь, что всякий раз в программе
Массивы 195 генерируется новый упорядоченный набор данных — массив М.и в этом массиве определя- ется значение максимального элемента и его порядковый номер. Упражнение 5. Дополните программу, чтобы в ней выполнялся поиск минимального элемента и определялся его индекс. Откомпилируйте ее и проверьте действие. 9.2. СОРТИРОВКА МАССИВОВ Сортировка — один из наиболее распространенных процессов современной об- работки данных. Сортировкой называется распределение элементов множества по группам в соответствии с определенными правилами. Например, сортировка эле- ментов массива, в результате которой получается массив, каждый элемент которо- го, начиная со второго, не больше стоящего от него слева, называется сортировкой по невозрастанию. ь Упражнение 6. Составим программу, которая выводит несортированный массив целых чисел на экран, затем выполняет его сортировку по невозрастанию и выводит отсортирован- ный массив на экран. Рассмотрим три метода сортировки одного и того же массива М из 20-ти целых чисел. Использование одного массива позволит объективно сравнил эффективность разных мето- дов. В связи с этим описание массива во всех примерах выполним следующим образом: const Count = 20; М: array [1..Count] of byte=(9,11,12,3,19,1,5,17,10,18,3,19,17,9,12, 20,20,19,2,5); Для сравнения эффективности разных способов сортировки введем целую переменную А, значение которой будет равно числу итераций (повторов просмотра массива). Для наблю- дения текущего состояния массива после каждой перестановки элементов будем выводить его на экран: for I> := 1 to Count do Write(' ', M[L]); Writeln('Число итераций = ',A) ; 9.2.1. Линейная сортировка (сортировка отбором) Идея линейной сортировки по невозрастанию заключается в том, чтобы, после- довательно просматривая весь массив, отыскать наибольшее число и поместить его на первую позицию, обменяв его с элементом, который ранее занимал первую по- зицию. Затем просматриваются все остальные элементы массива и выполняется аналогичная операция по отбору из рассматриваемой части массива максимального элемента и обмену местами этого элемента и первого в рассматриваемой части и т.д. Введем в разделе описания следующие целые переменные: I — для указания позиции первого элемента в рассматриваемой части массива; J — для указания позиции очередного сравниваемого с ним элемента;
196 Глава 9 N — для временного хранения значения первого элемента для обмена значе- ниями с максимальным из рассматриваемой части массива; L — параметр цикла при выводе текущего значения элементов массива в про- цессе сортировки для наблюдения происходящих в массиве наблюдений; А — переменная, значение которой будет равно числу перестановок элементов. Вначале запишем вывод исходного массива на экран: Writein('Исходный массив:'); for I := 1 to Count do WriteC M[I]); Writein; Перед началом сортировки установим значение счетчика итераций А, равное 0. Для сортировки организуем два цикла for. Внешний цикл с параметром I, ука- зывающим позицию первого элемента в неотсортированной части массива, и внут- ренний цикл с параметром J, указывающим позищАо очередного, сравниваемого с первым, элемента неотсортированной части массива. Сравнение элементов запи- шем оператором: if M[I] < M[J] then begin N := M[I]; M[I] := M[J]; M[J] := N; end; Если условие M[I] < M[J] выполняется, т. e. в неотсортированной части масси- ва найден элемент, больший, чем первый, то обменять местами эти элементы. Об- мен осуществляется следующим образом: сначала значение М[1] запоминается в переменной N, затем значение элемента массива из J-й позиции записывается в 1-ю позицию, а после этого в J-ю позицию массива записывается значение переменной N. Для наблюдения изменений состояния массива после каждой перестановки зада- дим цикл вывода значений всех элементов массива и напечатаем текущее значе- ние числа перестановок элементов А следующим образом: for I> := 1 to Count do Write(* ’, M[L]); Writein('Число итера- ций=', A) ; Полный текст программы линейной сортировки массива М’по невозрастанию может быть записан так: program Sort_Lin; {Линейная сортировка по невозрастанию} const Count = 20; М:array [1..Count] of byte=(9,11,12,3,19,1,5,17,10,18,3,19,17,9, 12,20,20,19,2,5); var I, J, N, L : Byte; A : integer; begin
Массивы 197 Writein('Исходный массив:') ; for I := 1 to Count do Write(’ ’ , M[I]); Writein; A:=0; for I := 1 to Count — 1 do {Изменять размер неотсортированной части массива} for J:=1+1 to Count do {Сравниваем поочередно I-й элемент неотсортированной части массива со всеми от I+1-го до конца} begin А:=А+1; if M[I] < M[J] then {Если в неотсортированной части массива нашли элемент, больший, чем I-й, то обменять их местами} begin * N := М[1]; {Запомнить на время значение М[1] } M[I] := M[J] ; M[J] := N; end; for L := 1 to Count do Write(' ', M[L])*; Writein('Число ите- раций =' ,A) ; end; end. Запустите интегрированную среду программирования. Введите текст програм- мы Sort Lin и запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, запустите программу на исполнение, наблюдая в пошаговом режиме текущие значения пере- менных: I, J, N, M[I], M[J], а также просматривая на экране пользователя изменен- ный за один проход внутреннего цикла массив. По последнему значению А опреде- ляем, что для данного массива сортировка по невозрастанию пузырьковым мето- дом выполняется за 190 итераций. 9.2.2. Сортировка методом пузырька Один из самых популярных методов сортировки — "пузырьковый" метод ос- нован на том, что в процессе исполнения алгоритма более "легкие" элементы мас- сива постепенно "всплывают". Особенностью данного метода является сравнение не каждого элемента со всеми, а сравнение в парах соседних элементов. Алгоритм пузырьковой сортировки по убыванию состоит в последовательных просмотрах снизу вверх (от начала к концу) массива М. Если соседние элементы таковы, что выполняется условие, согласно которому элемент справа больше элемента слева, то выполняется обмен значениями этих элементов. Текст программы сортировки массива по невозрастанию можно записать в та- - ком варианте:
198 Глава 9 program Sort_Puz; {Сортировка массива "пузырьковым" методом по не- возрастанию} const Count = 20; М:array [1..Count] of byte=(9,11,12,3,19,1,5,17,10,18,3,19,17,9,12, 20,20,19,2,5); var I, J, K, L : Byte; A : integer; begin Writeln('Исходный массив:'); for I := 1 to Count do Write(M[I] , ' '); Writeln; A:=0; * for I := 2 to Count do {Сортировка "пузырьковым" методом по не- возрастанию} begin for J:=Count downto I do begin A:=A+1; if M[J—1]<M[J] then {Если элемент справа больше элемента слева> то "вытеснить" его влево — пузырек "всплывает"} begin K:=M[J—1]; {Обмен значениями этих элементов} M[J-1]:=M[J]; M[J]:=К; {Печатать текущее состояние массива после каждой перестанов- ки} for L := 1 to Count do WriteC ’,M[L]); Writeln('Число итераций =',A); end; end; end; {Завершение сортировки} end. Запустите интегрированную среду программирования. Введите текст програм- мы Sort Puz и запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, запустите программу на исполнение, наблюдая в пошаговом режиме текущие значения пере- менных: I, J, К, M[J], M[J—1], всего массива М, а по окончании цикла J просматри- ; вая измененный за один проход массив. Как видно по результатам наблюдения, ; оператором повтора for с параметром J выполняется циклический просмотр эле- ; ментов массива от последнего до второго, и при этом элементы, большие, чем "со- 1 седи" слева, смещаются при обмене к началу массива, а меньшие — вправо — j "всплывают" в конец массива. Каждый просмотр фиксируется счетчиком просмот- ;
Массивы 199 ров — увеличением на единицу значения переменной А. После каждого просмотра- сравнения элементов массива просматриваемый участок массива уменьшается на один элемент, так как из рассмотрения исключается самый левый — самый боль- шой элемент. Такое уменьшение просматриваемого участка выполняется внешним циклом for с параметром I. По последнему значению А определяем, что для данного массива сортировка по невозрастанию пузырьковым методом выполняется за 170 итераций. 9.2.3. Метод быстрой сортировки с разделением Оба выше рассмотренных метода достаточно просты и наглядны, но не очень эффективны. Значительно быстрее работает алгоритм сортировки К.Хоора, кото- рый называют сортировкой с разделением или "быстрой сортировкой". В основу алгоритма положен метод последовательного дробления массива на части. Рас- смотрим пример программы, реализующей этот метод. program Quck_sort; {Быстрая сортировка по невозрастанию дроблением массива на части} uses Crt; const Count=20; M : array [1..Count] of byte=(9,11,12,3,19,1,5,17,10,18,3,19,17,9, 12,20,20,19,2,5); var I,A : integer; procedure Quicks(First,Last:integer); var I,J,X,W,L:integer; begin I:=First; {Левая граница массива — первый элемент} J:=Last; {Правая граница массива — последний элемент} Х:=М[(First+Last) div 2]; {Определить середину массива} repeat while M[I]>X do I:=I+1; while X>M[J] do J:=J—1; A:=A+1; {Увеличить число итераций обмена элементов} if I<=J then begin {Обменять местами элементы} W:=M[i]; M[I]:=M[J]; M[J]:=W; I:=1+1; J:=J—1; {Печатать текущее состояние массива после каждой перестановки} for L := 1 to Count do WriteC ',M[L]); Writein('Число итераций =',A);
200 Глава 9 end; until I>J; if First<J then QuickS(First,J) ; {Рекурсивный вызов процедуры QuickS} if KLast then QuickS(I,Last) ; {Рекурсивный вызов процедуры QuickS} end; {Конец сортировки} begin {Начало основной программы} ClrScr; Writein('Исходный массив:'); Writein; for I:=l to Count do Write (M[I]:3,' '); Writein; A:=0; QuickS (1,Count)7 {Вызов процедуры сортировки QuickS} Writein ('Отсортированный массив:*); Wri^eln; for I:=l to Count do Write (M[I]:3,' '); Writein; end. Запустите интегрированную среду программирования. Введите текст програм- мы Quck sort и запишите файл на диск под соответствующим именем, а затем от- компилируйте его. После того как компиляция выполнится успешно, нажатием клавиши F7 запустите программу на исполнение в пошаговом режиме с заходом в процедуру QuickS и пронаблюдайте изменения текущих значений переменных I, J, M[I], M[J], First, X, Last. В момент вызова процедуры просматривайте экран поль- зователя с текущим состоянием сортируемого массива или установите в окне просмотра имя массива М для наблюдения за изменением состояния массива в про- цессе сортировки. После первого вызова процедуры QuickS в исходном массиве: 9 11 12 3 19 1 5 17 10 18 3 19 17 9 12 20 20 19 2 5 будет определена его середина (10-й элемент) и переменной X присвоено значение М[10], т. е. 18. После этого массив делится на две части: 19 1112 3 19 15 1710 18 и 3 19 17 9 12 20 20 9 2 5 Далее выполняется обмен элементами по следующему правилу: При просмотре левой части массива слева направо выполняется поиск такого элемента массива, что М[1]>Х, затем при просмотре правой части справа налево отыскивается такой элемент, что М[1]<Х. Выполняется обмен местами данных эле- ментов, пока все элементы слева от середины, удовлетворяющие условию М[1]>Х, не будут обменены с элементами, расположенными справа от середины и удовле- творяющими условию М[1]<Х. В результате этого цолучим массив из двух частей следующего вида: 9 11 123 19 1 5 17 10 183 19 179 12 20 20 19 2 5 Число итераций =1 1920 123 19 1 5 17 10 183 19 179 12 20 11 9 2 5 Число итераций =2 1920 203 19 1 5 17 10 183 19 179 12 12 11 9 2 5 Число итераций =3 19 20 20 19 19 1 5 17 10 18 3 3 17 9 12 12 11 9 2 5 Число итераций =4 19 20 20 19 19 18 5 17 10 1 3 3 17 9 12 12 11 9 2 5 Число итераций =5
Массивы 201 Далее рекурсивно левая часть в свою очередь дробится на две части, и вызыва- ется процедура для сортировки левой части (с 1-го по 6-й элемент) 20 20 19 19 19 18 5 17 10 1 3 3 17 9 12 12 11 9 2 5 Число итераций =7 2020 19 19 19 185 17 10 1 33 179 12 12 11 9 2 5 Число итераций =8 После того как левая часть массива отсортирована, опять рекурсивно вызыва- ется процедура, в которой определяется середина данной части массива, и выпол- няется обмен элементов. Массив становится таким: 20 20 19 19 19 185 17 10 1 33 179 12 12 11 9 2 5 Число итераций =9 20 20 19 19 19 18 5 17 10 1 3 3 17 9 12 12 11 9 2 5 Число итераций =10 Далее опять рекурсивно вызывается процедура для сортировки, пока в каждой из частей останется по одному элементу. Затем рекурсивноьвызывается процедура для аналогичной сортировки правой части (с 7-го по 13-й элемент). Результат последовательных этапов сортировки мас- сива отображается так: 20 20 19 19 19 185 17 10 1 33 179 12 12 11 9 2 5 Число итераций =11 20 20 19 19 19 18 17 17 10 1 3 3 5 9 12 12 11 9 2 5 Число итераций =12 20 20 19 19 19 18 17 17 10 1 3 3 5 9 12 12 11 9 2 5 Число итераций =13 20 20 19 19 19 18 17 17 10 9 3 3 5 9 12 12 11 1 2 5 Число итераций =14 20 20 19 19 19 18 17 17 10 9 11 3 5 9 12 12 3 1 2 5 Число итераций =15 2020 19 19 19 18 17 17 109 11 12 5 9 12 3 3 1 2 5Число итераций =16 20 20 19 19 19 18 17 17 10 9 11 12 12 9 5 3 3 1 2 5 Число итераций =17 20 20 19 19 19 18 17 17 10 9 11 12 12 9 5 3 3 1 2 5 Число итераций=18 20 20 19 19 19 18 17 17 12 9 11 12 10 9 5 3 3 1 2 5 Число итераций =19 20 20 19 19 19 18 17 17 12 12 11 9 10 9 5 3 3 1 2 5 Число итераций=20 20 20 19 19 19 18 17 17 12 12 11 9 10 9 5 3 3 1 2 5 Число итераций =21 20 20 19 19 19 18 17 17 12 12 11 9 10 9 5 3 3 1 2 5 Число итераций =22 20 20 19 19 19 18 17 17 12 12 11 10 9 9 5 3 3 1 2 5 Число итераций=23 20 20 19 19 19 18 17 17 12 12 11 10 9 9 5 5 3 1 2 3 Число итераций=24 20 20 19 19 19 18 17 17 12 12 11 10 9 9 5 5 3 1 2 3 Число итераций =25 20 20 19 19 19 18 17 17 12 12 11 10 9 9 5 5 3 1 2 3 Число итераций =26 20 20 19 19 19 18 17 17 12 12 11 10 9 9 5 5 3 3 2 1 Число итераций =27 По последнему значению А определяем, что для данного массива сортировка по невозрастанию выполняется за 27 итераций. Как видно из приведенных примеров, алгоритм "быстрой" сортировки дает лучшие результаты, чем пузырьковый метод, однако следует учесть, что в некото- рых случаях это преимущество снижается. Например, если применить эту програм- му для сортировки массива М, элементы которого имеют значения 29,27,24,3,23,19,19,18,1, 17,15,13,1,0,9,9,8,6,6,5, то сортировка будет выполнена за 28 итераций, т. е. время процесса и число итераций даже увеличатся, несмотря на то, что исходный массив в большей степени упорядочен, в то время как сортировка линейным методом — 190 итераций для обоих массивов, пузырьковым — 166 (для первого массива— 170). Измените строку с описанием массива на:
202 Глава 9 М:array[1..Count] of byte=(29,27,24,3,23,19,19,18,1,17,15,13,1, 0, 9, 9, 8, 6, 6, 5); и проверьте действие программ сортировок по всем рассмотренным методам на примере нового массива. 9.3. БИНАРНЫЙ ПОИСК В УПОРЯДОЧЕННЫХ МАССИВАХ Едва ли не самой внушительной демонстрацией эффективности применения компьютеров являются задачи, в которых осуществляется поиск информации в не- котором списке. Ранее в упражнениях 2 — 4 мы использовали метод линейного (последовательного) поиска, суть которого заключается в последовательном про- смотре всего списка элементов массива и сравнении значения очередного эле- мента с заданным для поиска. Этот метод поиска вполне эффективен на неупорядо- ченных массивах данных, а на предварительно отсортированных массивах значи- тельно эффективнее выполнять поиск другими методами. Например, метод линей- ного поиска практически бесполезен при поиске информации в массивах большого размера, так как занимает много времени. Одним из эффективных методов поиска в больших отсортированных массивах является бинарный поиск, или поиск методом деления пополам. В основе этого ме- тода лежит идея целенаправленных последовательных догадок о предполагаемом местоположении искомого элемента. Такой метод можно проиллюстрировать на примере шуточного совета, как поймать в Африке льва: “Для начала разделим Аф- рику пополам. Ясно, что лев находится только в одной половине. Та половина, в которой находится лев, снова делится пополам и т. д. Площадь Африки приблизи- тельно 30 млн. км2. Следовательно, после примерно 45 делений мы получим пло- щадку, не превышающую 1 м2. Теперь на такой площадке льва поймать нетрудно”. Аналогично выполняется бинарный поиск элемента в упорядоченном массиве. Вместо просмотра подряд всех элементов массива делим его пополам. Так как мас- сив отсортирован, то, сравнивая искомый элемент со значением среднего элемента массива, мы в состоянии сделать вывод, может ли быть элемент с таким значением в массиве и в какой половине он находится, т. е. определить область дальнейше- го поиска. Затем делится пополам та часть массива, в которой находится элемент, и так до тех пор, пока рассматриваемая часть массива получится состоящей из одно- го элемента. Упражнение 8. Составим программу, которая выполняет бинарный поиск заданного элемента в отсортированном массиве целых чисел, элементы которого имеют значения: 20,20,19,19,19,18,17,17,12,12,11,10,9,9,5,5,3,3,2,1. Введем в разделе описания констант размер массива Count=20 и опишем массив целых чисел: М : array[1..Count]of byte=(20,20,19,19,19,18,17,17,12,12,11,10,9, 9,5,5,3,3,2,1). В разделе описания переменных опишем следующие переменные:
Массивы 203 N — для хранения значения искомого элемента; I — для указания очередного элемента массива; First — указание индекса элемента—левой границы рассматриваемой части массива; Last — указание индекса элемента — правой границы рассматриваемой части массива; Found — логическая переменная, в которой будет записываться результат поиска; А — переменная, значение которой будет равно числу перестановок элементов. В начале программы запишем вывод исходного массива на экран, затем вывод запроса искомого элемента, после чего считывание его значения в переменную N. Перед началом поиска обнулим счетчик повторений А, установим левую и правую гра- ницы рассматриваемой части массива, а переменной Found присвоим значение False — эле- мент пока не найден. Данный фрагмент программы запишется так: А:=0; Fiijst := 1; Last := Count; Found:=False; Повторяющуюся процедуру бинарного поиска запишем с помощью оператора повтора repeat. Первый шаг бинарного поиска — деление исходного массива на две части можно за- писать так: I := (First + Last) div 2. Условие поиска запишем оператором if M[I] = N then Found:=True Если условие M[I] = N выполняется, то искомый элемент найден, переменной Found присваивается значение True, и управление передается за пределы оператора if. Если это ус- ловие не выполняется, то оператором if M[I] > N then First := 1+1 else Last := I—1; проверяется условие M[I] > N. Если оно выполняется, значит значение искомо! > элемента расположено в правой части массива, поэтому левую границу части массива переносим в позицию 1+1 (First:=I+l), иначе элемент нужно искать в левой части массива, а, значит пра- вую границу части массива переносим в позицию I—1 (Last:=I—1). Затем нужно записать увеличение счетчика повторений при поиске А:=А+1. Оператор повтора завершим записью условия окончания (Found) or (First>Last). Если это условие выполнится, т. е. если найдется искомый элемент или будет просмотрен весь массив, цикл завершится. Этот фрагмент про- граммы можно записать так: repeat {Повторять поиск} I := (First + Last) div 2; {Разделить на две части} if M[I] = N then Found:=True else begin if M[I] > N then First := 1+1 {Искать элемент в правой части} else Last := I—1; {Искать элемент в левой части} end;
204 Глава 9 А:=А+1; {Увеличить счетчик числа итераций} until (Found) or (First>Last); {Завершить, если найдется иско- мый элемент или будет просмотрен весь массив} В заключительной части программы запишем условие вывода результата поиска. Если значение переменной Found = True, то выведем на экран сообщение о том, какую позицию в массиве занимает искомый элемент, иначе выведем сообщение о том, что такого элемента в массиве нет. После чего выведем сообщение о том, сколько просмотров массива выполня- лось в процессе поиска. В целом текст программы будет записан следующим образом: program Find_Bin; const Count = 20; M : array[1..Count] of byte = (20,20,19,19,19,18,17,17,12,12,11,10,9,9,5,5,3,3,2,1); var N, I, First, Last : Byte; A : integer; Found : boolean; . begin Writeln('Исходный массив:'); for I := 1 to Count do Write(M[I]:3,' '); Writeln; Writeln; Write('Введите значение элемента массива для поиска >'); Readln (N) ; А:=0; First := 1; Last := Count; Found:=False; {Элемент не найден} repeat {Повторять поиск} I := (First + Last) div 2; {Разделить на две части} if М[1] = N then Found:“True else begin if M[I] > N then First := 1+1 {Искать элемент в правой части} else Last := I—1; {Искать элемент в левой части} end; А:=А+1; {Увеличить счетчик числа итераций} until (Found) or (First>Last); {Завершить, если найдется искомый элемент или будет просмотрен весь массив} if Found then Writeln('Искомый элемент ' ,N,' в массиве занимает ',I,'-ю позицию*) else Writeln('В массиве нет искомого элемента ',N);
Массивы 205 Writein(’Поиск выполнен за * ,А, ' итераций*); end. Запустите интегрированную среду программирования. Введите текст программы Find_Bin и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, запустите программу на исполнение в пошаговом режиме и наблюдайте изменения текущих значений переменных First, Last, I, М[1], а также значение выражений M[I] > N и (Found) or (First>Last). Обратите внимание на то, что бинарный поиск в сортированных массивах выполняется за небольшое число итера- ций. Упражнение 9. Составим программу, которая формирует двумерный массив случай- ных чисел и вычисляет значение среднего арифметического его элементов, больших, чем 20. Решение задачи сводится к последовательному перебору всех элементов массива с вы- числением суммы тех элементов, значение которых больше, чем 20, а по окончании их сум- мирования — к вычислению частного полученной суммы и количества элементов массива, удовлетворяющих условию суммирования. В разделе описания программы опишем квадратный двумерный массив, указав в разде- ле описания констант количество строк Strok=15 и количество столбцов Stolb=15, а в разде- ле описания переменных массив целых чисел: D : array(1..Strok,1..Stolb] of integer Для указания положения элемента в массиве введем переменные: I — номер строки, в которой расположен элемент; J— номер столбца, в котором расположен элемент. Для подсчета количества элементов массива, больших, чем 20, введем целую перемен- ную К, а для подсчета их суммы, а затем и значения среднего арифметического введем ве- щественную переменную S. Для заполнения массива случайными целыми числами приме- ним функцию Random, и поэтому в начале описания запишем подключение стандартного модуля Crt, в котором она записана. Блок описания запишется так: uses Crt; const Strok=15; Stolb=15; var D : array[1..Strok,1..Stolb] of integer; I, J, К : integer; S : real; Так как массив D двумерный, то фрагмент присваивания случайных значений элемен- там массива запишем в виде двух вложенных циклов for: Randomize; for I:= 1 to Strok do for J:= 1 to Stolb do D[I,J]Random(99); Внешний цикл изменяет номер строки, внутренний цикл — номер столбца. Данный фрагмент программы выполняется следующим образом: параметр I внешнего цикла прини- мает первоначальное значение 1, затем выполняется полный цикл изменения параметра J внутреннего цикла, и при этом элементам массива D[1,J] присваиваются значения результа-
206 Глава 9 тов вычислений функции Random(99). Аргумент 99 указывает интервал значений генерируе- мых случайных чисел от 0 до 99. После завершения внутреннего цикла параметр внешнего цикла I увеличивается на 1, проверяется условие I<= Strok, и если оно выполняется, то вы- полняется тело внешнего цикла — внутренний цикл, т. е. выполняется полный цикл изме- нения параметра J внутреннего цикла при 1=2, и при этом элементам массива D[2,J] при- сваиваются значения результатов вычислений функции Random(99). И т. д., пока завершит- ся полный цикл изменений параметра внешнего цикла I от 1 до Strok. В результате всем эле- ментам массива D будет присвоено значение результатов вычислений функции Random(99). Перед просмотром массива обнулим значения переменных S и К. Просмотр массива за- пишем в виде двух вложенных циклов, и так же, как и при инициализации массива, пара- метр внутреннего цикла будет определять номер столбца, в котором расположен элемент массива, а параметр внешнего цикла — номер строки. Поскольку тело и внешнего, и внут- реннего цикла включает больше одного оператора, то в hx записи используем составной оператор. В составном операторе тела внутреннего цикла выполняется печать элементов массива по столбцам в I-й строке и осуществляется последовательный поиск элементов мас- сива, удовлетворяющих условию D[I,J]>20. Если элемент массива удовлетворяет данному условию, то значение суммы увеличивается на величину значения этого элемента, а счетчик числа таких элементов увеличивается на единицу. Для перехода к печати элементов масси- ва, расположенных в следующей строке, применим оператор Writein после тела внутренне- го цикла. Данный фрагмент программы будет записан следующим образом: for I:= 1 to Strok do begin for J:= 1 to Stolb do begin Write(D[I, J] :2, • ') ; if D[I,J]>20 then begin S:= S+ D[I,J]; {Суммирование подходящих элементов} K:=K+1; end; end; Writeln; end; После просмотра всех элементов массива для вычисления значения среднего арифмети- ческого его элементов, больших, чем 20, запишем оператор присваивания S:=S/K. В заключение программы запишем оператор вывода сообщения о результатах вычисле- ния, для чего используем стандартную процедуру Writein с форматом 5:3 для вывода значе- ния переменной S. Полный текст программы решения данной задачи запишется таким образом: program Sum_Mas_2; uses Crt; const Strok=15; Stolb=15;
Массивы 207 var D : array[1..Strok,1..Stolb] of integer; I, J, К : integer; S : real; begin Randomize; {Генерация массива случайных чисел} for I:= 1 to Strok do for J: = 1 to Stolb do D[I,J]:=Random(99) ; S: = 0; K: = 0; for I:= 1 to Strok do {Вычисление суммы элементов массива>20} begin for J:= 1 to*Stolb do {Просмотр всех элементов в I-й строке} begin Write(D[I,J] :2,• '); if D[I,J]>20 then {Если найден элемент D[I,J]>20} begin S:= S+ D[I,J]; {Суммирование подходящих элементов} K:==K+1; {Увеличение счетчика подходящих элементов} end; end; Writeln; {Переход к печати массива в следующей строке экрана} end; S:=S/K; {Расчет среднего арифметического} Writeln(’Среднее арифметическое элементов массива, больших 20 = ',S:5:3); Readln; end. Запустите интегрированную среду программирования. Введите текст программы Sum_Mas_2 и запишите файл на диск под соответствующим именем, а затем откомпилируй- те его. После того как компиляция выполнится успешно, пронаблюдайте работу программы в пошаговом режиме с оператора S:=0 с просмотром в окне просмотра текущих значений переменных I, J, К и S. Установите на строке программы if D[I,J]>20 then контрольную точ- ку останова и запустите программу на исполнение до точки останова, нажав клавишу F4. После этого, нажимая F8, наблюдайте за изменениями значений переменных и выводом элементов массива на экран. Упражнение 10. Составим программу, которая формирует двумерную матрицу случай- ных чисел, имеющую N строк и N столбцов, а затем формирует два одномерных массива, причем в один записываются элементы матрицы, расположенные на главной диагонали и выше, в другой — элементы матрицы, лежащие ниже главной диагонали и выводит все мас- сивы на экран. Алгоритм решения задачи состоит в том, что, последовательно просматривая элементы матрицы, необходимо определить, расположены ли они на главной диагонали и выше или
208 Глава 9 лежат ниже главной диагонали. Главной диагональю матрицы считается цепочка элементов, для которых номер строки и номер столбца одинаковы. Если очередной рассматриваемый элемент матрицы расположен на главной диагонали или выше, то записываем его в первый одномерный массив, иначе — во второй. В разделе описания модулей uses запишем использование модуля Crt для обращения к его стандартной функции Random. В разделе описания констант запишем размер матрицы: количество строк — Strok=10, а так как матрица квадратная, то количество строк равно ко- личеству столбцов Stolb=Strok. Матрицу опишем как А : аггау[1..Strok, 1.. Stolb] of integer. Одномерные массивы назо- вем В и С и опишем их одинаковым образом: В,С : array[l..Strok*6] of integer. Так как коли- чество элементов матрицы, расположенных на главной диагонали или выше, не более 60 % от числа всех элементов матрицы, то длину одномерных массивов определим значением вы- ражения Strok*6. Для указания положения элемента в матрице используем целые переменные I и J. Для подсчета количества элементов матрицы, расположенных на главной диагонали и выше, введем целую переменную X, а для подсчета количества элементов матрицы, расположен- ных ниже главной диагонали — целую переменную Y. Эти же переменные будут служить для указания позиции элемента при записи в соответствующий одномерный массив. В начале программы запишем вызов стандартной функции очистки экрана ClrScr, за- тем, как в предыдущей программе, опишем присваивание случайных значений элементам матрицы с выводом их значений на печать. Перед началом просмотра матрицы обнулим значения переменных X и Y. Просмотр матрицы запишем двумя циклами: внешний цикл будет указывать номер строки, а внутрен- ний цикл — номер столбца матрицы, на пересечении которых расположен элемент. Для проверки расположения элемента матрицы на главной диагонали или выше используем вы- ражение J >=1. Если значение этого выражения True, значит элемент A[I,J] удовлетворяет условию отбора для первого одномерного массива, поэтому увеличим счетчик таких эле- ментов X на 1 и запишем его в позицию X одномерного массива B[X]:=A[I,J]; если значе- ние выражения J >=I False, то увеличим счетчик элементов, расположенных в матрице ниже главной диагонали, Y на 1 и запишем данный элемент в позицию Y одномерного массива C[Y]:=A[I,J]. Поиск подходящих элементов матрицы й формирование одномерных массивов запишем так: for I:= 1 to Strok do for J:= 1 to Stolb do if J >=I then {Элемент на главной диагонали и выше} begin Х:= Х+1; {Найден еще один подходящий элемент} В[Х]:= A[I,J];{Записать элемент в массив В под номером X} end else {Элемент расположен ниже главной диагонали} begin Y: = Y+1; {Найден еще один подходящий элемент} - C[Y]:= A[I,J];{Записать элемент в массив С под номером Y} end;
Массивы 209 В заключительной части программы выведем на экран полученные массивы В и С. Полный текст программы будет таким: program Preobr_Mas_2; uses Crt; const Strok=10; Stolb=Strok; var A : array[1..Strok,1..StolbJ of integer; В, C : array[1..Strok*6] of integer; I, J, X, Y: integer; begin ClrScr; i Randomize; {Генерация массива случайных чисел} for I:= 1 to Strok do begin for J:= 1 to Stolb do {Генерировать значения элементов I-й строки массива} • , begin A[I,J]:=Random(99); Write(A[I,J]:2,' •); {Вывести элемент J-го столбца строки массива на экран} end; Writeln; {Перейти к печати следующей строки массива} end; Writeln; end; Х:= 0; {Количество элементов, расположенных на главной диаго- нали и выше} Y:= 0; {Количество элементов, расположенных ниже главной диа- гонали} for I:= 1 to Strok do for J:= 1 to Stolb do if J >=I then -{Элемент на главной диагонали и выше} begin Х:= Х+1; {Найден еще один подходящий элемент} В[Х]:= A[I,J]; {Записать элемент в массив В под номером X} end else {Элемент расположен ниже главной диагонали} begin Y:= Y+1; {Найден еще один подходящий элемент} C[Y]:» A[I,J]; { Записать элемент в массив С под номером Y}
210 Глава 9 end; Writein('Элементы, расположенные на главной диагонали и выше: '); for I:= 1 to X’do Write(В[I]:2,' '); Writein; Writein('Элементы, расположенные ниже главной диагонали:'); for I:= 1 to Y do Write(C[I]:2,' ’); Writein; end. Запустите интегрированную среду программирования. Введите текст программы Preobr_Mas_2 и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. После того как компиляция выполнится успешно, пронаблюдайте работу про- граммы в пошаговом режиме с оператора Х:=0 с просмотром текущих значений перемен- ных I, J, X, Y, A[I,J], В[Х], C[Y]. Установите на данной строке программы контрольную точ- ку останова и запустите программу на исполнение до точ^и останова, нажав клавишу F4. После этого, нажимая F8, наблюдайте за изменениями значений переменных и формирова- нием одномерных массивов В и С. 9.4. ТРАНСПОНИРОВАНИЕ МАТРИЦЫ Транспонированной матрицей называется матрица, у которой столбцы соот- ветствуют строкам исходной квадратной матрицы. При этом элементы, располо- женные на главной диагонали исходной и транспонированной матриц, одни и те же. же. Операция транспонирования сводится к обмену элементов матрицы, располо- женных симметрично главной диагонали. Например: Исходная матрица Транспонированная матрица 30 82 84 90 37 30 49 94 86 91 49 11 88 20 68 82 11 7 69 33 94 7 40 84 45 84 88 40 69 5 86 69 69 74 51 90 20 84 74 10 91 33 5 10 71 37 68 45 51 71 Упражнение 11. Составим программу, которая формирует матрицу случайных чисел и транспонирует ее. В блоке описания программы запишем размер матрицы Strok=Stolb=10. Матрицу опи- шем как массив целых чисел А : аггау[1.. Strok, L.Stolb] of integer. Для указания позиции эле- мента в матрице введем целые переменные I, J. Для временного хранения значения элемента матрицы при обмене в процессе транспонирования введем целую переменную С. Присваивание значений элементам матрицы и вывод исходной матрицы на экран можно записать, как в предыдущей программе. Транспонирование матрицы запишем вложенными операторами повтора for. Параметр внешнего цикла I, изменяясь от 1 до Strok, задает просмотр элементов в l-й строке матри- цы. Для каждого значения I выполняется полный цикл изменений параметра внутреннего цикла J, который задает просмотр элементов в I-й стррке с I+1-го столбца до Stolb. Транспо-
Массивы 211 пирование матрицы — обмен симметричных относительно главной диагонали элементов — выполняется с использованием вспомогательной переменной С. Переменной С присваивает- ся значение текущего элемента матрицы A[I,J], затем текущему элементу матрицы присваи- вается значение элемента A[J,I], симметричного ему относительно главной диагонали, об- мен завершается присваиванием первоначального значения элемента A[I,J], хранимого в пе- ременной С, элементу A[J,I] и т. д. , пока будут просмотрены элементы всех столбцов во всех строках матрицы, как записано в следующем фрагменте программы: for I: = 1 to Strok do {Просмотр всех строк матрицы} for J:= 1+1 to Stolb do {Просмотр всех элементов в строке, расположенных выше главной диагонали} begin {Обмен значений элементов, симметричных относительно главной диагонали} C:=A[I,J]; A[I, J] :=A[J,I] ; A[J,I]:=С; end; В заключительной части программы выведем на экран полученную в результате транс- понирования матрицу. Полный текст программы будет таким: program Transp_Mat; uses Crt; const Strok=10; Stolb=Strok; var A : array[1..Strok,1..Stolb] of integer; I, J, C : integer; begin ClrScr; Randomize; {Генерация исходной матрицы случайных чисел} Writeln(’Исходная матрица:'); for I:= 1 to Strok do begin for J:= 1 to Stolb do {Генерировать значения элементов I-й строки массива} begin A[I,J]:“Random(99); Write (A[I,J]:2,’ ’); {Вывести элемент J-го столбца I-й строки массива на экран} end; Writeln; end; {Транспонирование матрицы} for I: = 1 to Strok do {Просмотр всех строк матрицы}
212 Глава 9 for J:= 1+1 to Stolb do {Просмотр всех элементов в строке, расположенных выше главной диагонали} begin {Обмен значений элементов, симметричных относительно главной диагонали} C:=A[I,J]; A[I,J]:=A[J,I]; A[J,I]:=С; end; Writein('Результат транспонирования матрицы:'); for I:= 1 to Strok do begin for J:= 1 to Stolb do begin * Write(A[I,Л:2, ' •); end; Writein; end; end. Запустите интегрированную среду программирования. Введите текст программы TranspMat и запишите файл на диск под соответствующим именем, а затем откомпилируй- те его. После того как компиляция выполнится успешно, пронаблюдайте работу программы в пошаговом режиме на участке транспонирования, для чего, установив курсор на строке программы for I:= 1 to Strok do, установите на ней контрольную точку останова и запусти- те программу на исполнение'до точки останова, нажав клавишу F4. После этого, нажимая F8, наблюдайте за изменениями значений переменных I, J, С, значениями элементов матри- цы A[I,J] и A[J,I], По окончании работы программы просмотрите на экране пользователя ис- ходную и транспонированную матрицы, сравните элементы, расположенные симметрично главной диагонали. Упражнение 12. Составим программу, которая формирует двумерный массив (возраст и ФИО учащихся), выполняет поиск учащихся по критерию возраста (старше 16 лет) и вы- водит на экран сообщение о результатах поиска и список учащихся старше 16 лет. Особенность реализации в том, что все элементы массива должны иметь одинаковый тип, и поэтому и фамилии и возраст учащихся объявим величинами типа string. Для преоб- разования значения возраста учащегося из типа string в величину целочисленного типа бу- дем использовать стандартную функцию Vai, поэтому для обращения к ней в разделе описа- ния модулей uses запишем использование модуля Crt. В блоке описания программы запишем размер массива. Strok=5, т. е. в данный массив можно записать данные о пяти учащихся. Двумерный массив данных типа строка из Strok строк и двух столбцов опишем следующим образом: FIO : array[1..Strok,1..2] of string[20] Для указания порядкового номера данных об учащемся в массиве введем целую пере- менную I. Целочисленное значение возраста учащегося, полученное функцией Vai при пре- образовании возраста, записанного в массиве в виде строки, обозначим X. Переменную Cod
Массивы 213 введем для возврата кода ошибки преобразования. Введем логическую переменную Yes для хранения результата поиска учащихся в массиве. Тогда блок описания программы будет записан таким образом: uses Crt:; const: Strok=5; {Количество учащихся в массиве} var FIO : array[1..Strok,1..2] of string[20]; I, X, Cod : integer; Yes : boolean; Поиск учащихся в массиве выполним одновременно с вводом данных. Перед началом ввода установим переменной Yes значение False — в массиве пока нет учащихся, возраст которых удовлетворяет условию поиска. Ввод фамилии и возраста учащихся запишем с помощью цикла for, параметр которого I будет указывать номер строки массива, в которую записываются данные об учащемся. Фа- милию очередного учащегося считаем с клавиатуры с помощью процедуры Readln в эле- мент массива FIO[I,1]. Контроль правильности ввода возраста учащегося запишем с помощью цикла repeat, ус- ловием окончания которого является корректный ввод данных. Корректность ввода данных выполним с помощью функции Vai, возвращающей в случае некорректного ввода ненуле- вое значение переменной Cod, и проверим целочисленное значение возраста учащегося X на неотрицательность. Это можно записать следующим образом: if (Cod о 0) or (Х<0) then \Угке1п('Ошибка в записи возраста ')• Цикл ввода возраста учащегося завершится, если во введенной строке возраста будут только цифры и возраст не будет отрицательным. Это ус- ловие запишем следующим образом: until (Cod=0) and (XX)). Если это условие не выполня- ется, то на экран выводится сообщение об ошибке и запрос на повторный ввод возраста. Блок ввода данных с контролем корректности ввода возраста запишем так: repeat {Контроль ввода возраста учащегося} Write('Введите возраст -го учащегося >'); Readln(FIO[1,2]); Vai(FIO[I,2], X, Cod); if (Cod <> 0) or (X<0) then Writein('Ошибка в записи возраста '); until (Cod=0) and (X>0); Проверку условия поиска учащихся старше 16 лет запишем таким образом: if Х>16 then Yes:=True. Если условие Х>16 выполняется, то логической переменной Yes присваивается значение True. После завершения внешнего цикла ввода данных об учащихся в массив запишем про- верку условия not Yes. Если оно выполняется, значит в массиве нет учащихся старше 16 лет, иначе на экран оператором повтора for выводится список всех учащихся из массива, удовле- творяющих данному условию. Полный текст программы будет записан так: program Fio_Pupil; uses Crt; const Strok=5; {Количество учащихся в массиве}
НЕМ» к» anqfr ЛЙЫВДЕЧ HQ мМ? йМЭДЭДг ^вдвМИз: вГ’»эа.- . ><|^^яышк«шшкиёляря» „«игам'«*икайай|и ТЖВЖ < d№ Shs®5*"' '» ' .Stair * ;ta Г ”' . ж ГЗВВ ast-- jaaasas..•.-jwk ^--л^йраии-Л " е '4ЭД1,-»‘вждакцршв^ й: $?««“• ЧЯ»-* 43 Й© ** . . ПЖЭДЖйЯКЖиТм ЖИйег :i »Н?йи^“ ••<, л««акя^Щ» - & ж \ .- а"' ^w:jmjmm5«m» В.. шап *£j¥ s«;kS81 |||1№йВ^м^1ЙМ№№^ “' :1®‘ ^.^-Лг"Ж!И®Ж^---®й*з^г Хл<й«Ш1ЖИ^«>>№№| жяввивге^-£: гж.Л жЛйг ix№. №>ё& й Шм ни«ав”®йп5’< ’i®"'W s^fflSasa' :И®вЭЬ.-8 Ч. гтям^г^щимяж*£31»'* dBtasCEbj«>Cll-tt» Н le©Sw^<3S»a^^sws®sssB згхздавж" Ш> :йjc В:. ,.L„* &'зт£ШНШ^{£:.Я:ЛЗУ1®1» !5®атякния«яцря а»аад«и»аилЖ и ''.к' йййййЗ"; ~- эвяий». ____ . __________ !Яв^пйк&»е «гаедрде;j к,;.;„" ~a, >:'i^R'3«K“'»ws -~~~“--у,й^~-:-~^^--— *“7™- - 8ЩЙ? *я®язз - ; ,.L.^f^F‘ :Я“С2 .гИ^--;||г^~»~эг<........... ;Ж"’" “T^gjKjfc,
Массивы 215 3. Что такое индекс? Каким требованиям он должен удовлетворять? 4. Особенности расположения элементов массива в памяти ЭВМ. Особенности распо- ложения в памяти элементов многомерных массивов. 5. Каким образом задается описание массива, что в нем указывается? 6. Общие и отличительные черты одномерных, двумерных и n-мерных массивов. 7. В каких операциях могут участвовать массивы и какие к ним при этом предъявляют- ся требования? 8. Каким образом в Паскале задается обращение к элементу массива? 9. Почему при описании массивов предпочтительнее употреблять константы, а не ука- зывать размеры массива в явном виде? 10. Что называют инициализацией массива, и зачем она применяется? 11. Что называется контрольной точкой останова? Как задать ее положение? Как запус- тить исполнение программы до контрольной точки останова? 12. Что называется сортировкой массива? Какие методы сортировки вы знаете, опиши- те их существенные отличия. 13. Как задать имена переменных или выражения для.просмотра их значений в окне просмотра при исполнении программы по шагам? 14. Что вы понимаете под поиском? Отличительные черты линейного и бинарного по- иска? В каких случаях целесообразнее использовать линейный или бинарный поиск? 15. Что называется главной (побочной) диагональю матрицы? Что такое транспониро- вание матрицы? Задания. 1. Введите с клавиатуры в массив X пять целочисленных значений, выведите их в одну строку через запятую; получите для массива среднюю арифметическую. 2. Введите с клавиатуры пять целочисленных элементов массива X. Выведите на экран значения корней и квадратов каждого из элементов массива. 3. Создайте массив из пять фамилий и выведите их на экран столбиком, начиная с по- следней. 4. Создайте массив из пяти фамилий и выведите на экран те из них, которые начинают- ся с определенной буквы, которая вводится с клавиатуры. 5. Дан одномерный массив. Вставьте в него элемент L в позицию К. 6. Введите с клавиатуры целочисленные элементы матрицы 3x3, выведите исходную матрицу на экран. Умножьте каждый элемент матрицы на 3 и выведите результат на экран. 7. Создайте двумерный массив (20x15) целых чисел и найдите сумму всех его нечетных элементов. 8. Введите с клавиатуры целочисленные элементы матрицы 3x3 и вычислите сумму эле- ментов каждого столбца. 9. Создайте матрицу 5x5, значение каждого элемента которой равно сумме номера строки и столбца, на пересечении которых он находится, и вычислите сумму элементов каж- дой строки.
216 Глава 9 10. Создайте массив из 15 целочисленных элементов и определите среди них мини- мальное значение. 11. Создайте двумерный массив X, имеющий четыре строки и три столбца и найдите в нем максимальный по абсолютному значению элемент, а также укажите номер строки и столбца, содержащие этот элемент. Например, в массиве 2 1 3 —4 0 8 7 5 1 —3 1 0 максимальный по абсолютному значению элемент = 8, находится он во второй строке третьего столбца. 12. Введите массив (не более 20) и определите, есть Ли в нем элементы с одинаковыми значениями. 13. Напишите программу анализа значений температуры больного за сутки: определите минимальное и максимальное значение, среднюю арифметическую. Замеры температуры проводятся шесть раз и результаты вводятся с клавиатуры в массив Т. 14. Дана матрица А, имеющая N строк и N столбцов. Сформируйте два одномерных массива. В один перешлите четные, а в другой — нечетные элементы матрицы. Выведите на экран все массивы. 15. Создайте двумерный массив вещественных чисел, имеющий 10 строк и 15 столбцов, выведите его на экран. Затем разделите каждый элемент массива на среднее арифметиче- ское значение элементов строки, в которой они расположены и результат выведите на экран. 16. Создайте матрицу из 15 строк и 15 столбцов. Вычислите произведение суммы эле- ментов главной диагонали на сумму элементов L-й строки. 17. Сожмите одномерный массив, удалив предшествующие минимальному элементу. 18. Найдите в одномерном массиве элементы, сумма которых максимальна, затем уда- лите все элементы, предшествующие тому элементу, индекс которого наибольший из двух. 19. Вычислите сумму элементов двумерного массива, индексы которых составляют в сумме заданное число К. 20. Создайте массив "шахматная доска". 21. Создайте одномерный массив, элементами которого являются суммы положи- тельных элементов строк матрицы. 22. Найдите сумму элементов столбца и строки массива, на пересечении которых нахо- дится максимальный элемент. 23. Найдите сумму элементов массива ниже главной диагонали, произведение не рав- ных нулю элементов выше главной диагонали и количество элементов в главной диагонали, попадающих в интервал [—1;1]. 24. Найдите судому минимальных элементов главной и побочной диагонали. 25. Найдите длину наибольшего отрезка, соединяющего две точки с координатами, за- данными таблицей F(2,N).
Массивы 217 26. На плоскости своими координатами задано 40 точек. Найдите расстояние до са- мой удаленной (от начала координат) точки. 27. Задан список областных центров России. Присвойте переменной t название города с максимальным числом букв. 28. Сечение крыши имеет форму полукруга с радиусом R м. Требуется сформировать таблицу, содержащую длины опор, устанавливаемых через каждые R/5 м. 29. Таблица содержит 100 номеров выигрышных билетов. Проверьте, является ли билет с номером N выигрышным. 30. С 8 до 20 ч температура воздуха измерялась ежечасно. Известно, что в течение этого времени температура понижалась. Определите, в котором часу была впервые отмече- на отрицательная температура. 31. В поликлинику цоступили сведения о проживающих на обслуживаемой улице в ви- де таблиц, каждая из которых содержит номер дома, общее число жильцов дома, число де- тей, число пенсионеров. Всего поступили три такие таблицы. Требуется сформировать одну линейную таблицу, содержащую сведения о трех домах. 32. Дан список учеников класса и отметки каждого из учеников за выполнение двух контрольных работ. Требуется: а) подсчитать число учеников, выполнивших Первую работу на 5; б) подсчитать число учеников, выполнивших хотя бы одну работу на 5; в) подсчитать число учеников, выполнивших обе работы на 5; г) подсчитать число учеников, выполнивших вторую работу на 4 и 5; д) подсчитать число учеников, выполнивших обе работы на 4 и 5; е) найти число учеников, выполнивших обе работы на 5, число учеников, выполнив- ших обе работы на 4, и число учеников, не выполнивших обе работы; ж) найти число учеников, написавших хотя бы одну из двух работ на 5, и число учени- ков, не написавших хотя бы одну работу; з) вывести список учеников, выполнивших первую работу на 5; и) вывести список учеников, ие выполнивших ни одной работы; к) вывести список учеников, не выполнивших хотя бы одну работу. 33. В расписании движения поездов по станции Масловка указаны: номера поездов, пункты следования, время прибытия и отправления, направления следования (южное, север- ное, западное, восточное). Сколько поездов следует в каждом из направлений? 34. Даны список футбольных команд высшей лиги России и количество очков, набран- ных каждой командой в чемпионате России. Известно, что нет команд с равным числом оч- ков. Какая из команд стала чемпионом? Составьте список команд, набравших более 15 оч- ков. 35. Задана таблица названий товаров, выпускаемых заводом. Определите, повторяется ли в этой таблице название первого товара, и, если повторяется, удалите название первого товара из таблицы.
218 Глава 9 36. Задан список фамилий брокеров товарной биржи из N человек. Обменяйте местами фамилии брокеров: первого и последнего, второго и предпоследнего, третьего от начала и третьего от конца и т.д. 37. Заданы две таблицы. Одна содержит наименование услуг, выполняемых в доме бы- та, а другая — расценки за эти услуги. Удалите из обеих таблиц все, что предшествует услу- ге, цена которой Р руб. 38. Слейте две линейные таблицы А и В в новую таблицу С, поставив элементы табли- цы А на нечетные места, а элементы таблицы В - на четные. 39. В линейном массиве найти максимальный элемент. Вставьте порядковый номер максимального элемента за ним, передвинув все оставшиеся на одну позицию вправо. 40. В линейном массиве найдите индексы тех двух элементов, сумма которых макси- мальна среди сумм всевозможных пар. Удалите все элементы массива, предшествующие то- му элементу, индекс которого наибольший из двух найденных. Преобразованный массив выведите на экран. 41. В квадратной таблице обменяйте местами элементы строки и столбца, на пересече- нии которых находится min из положительных элементов. 42. Наименьший элемент каждой строки прямоугольной таблицы, начиная со второй, замените наибольшим элементом предшествующей строки. 43. Даны две таблицы из N слов различной длины. Упорядочите слова по возраста- нию их длин. 44. Заданы стоимость различных деталей, выпускаемых мастерской, и их названия. От- сортируйте их по стоимости и по алфавиту. 45. Задана таблица из N чисел. Сколько треугольников можно составить из этих чисел? Найдите треугольник с максимальной площадью. 46. Даны список футбольных команд высшей лиги России и количество очков, набран- ных каждой командой в чемпионате России. Известно, что нет команд с равным числом оч- ков, а две команды, набравшие наименьшее число очков, покинут высшую лигу. Какие это команды? 47. Требуется раскодировать зашифрованный текст, содержащий 150 букв. Правило шифрования известно: вместо "правильной" буквы была указана буква, стоящая в алфавите через две буквы после "правильной". 48. Ведомость на зарплату представлена как две таблицы. Одна содержит фамилии ра- ботников цеха, а вторая — их зарплату за текущий месяц. Найдите фамилию работника, зарплата которого наименее отклоняется от средней зар- платы всех работников за текущий месяц. Найдите фамилии двух работников с наибольшей зарплатой. Удалите из ведомости на зарплату сведения о работнике, зарплата которого минималь- на. 49. Заданы три таблицы: две линейные таблицы А и В и прямоугольная таблица С. В линейной таблице А содержатся фамилии учеников 11 класса, в прямоугольной — их оцен- ки по 10 предметам за первое полугодие, в линейной таблице В — названия этих предметов. Составьте список неуспевающих учеников.
Массивы 219 Удалите из таблицы С все оценки ученика, имеющего больше трех двоек, а из списка А его фамилию. Считая список учеников упорядоченным по алфавиту, внести в него строку: Иванов Николай 5435424535 Определите, по какому предмету самый высокий средний балл. Найдите любых трех учеников с наибольшим средним баллом по всем предметам. Составьте список учеников в порядке убывания их среднего балла. 50. Создайте двумерный массив целых чисел. Удалите из него строку и столбец, на пе- ресечении которых расположен минимальный элемент. 51. Дан целочисленный одномерный массив А из 10 элементов. Найдите наименьшее число К элементов, которые нужно исключить из последовательности А[ 1], А[2],...,А[10], чтобы осталась возрастающая последовательность. 52. Дан одномерный массив целых чисел. Найдите, сколько раз в нем повторяется са- мое частое число. 53. Дан одномерный массив из 10 целых чисел. Подсчитайте количество различных чи- сел в нем. 54. Дан одномерный массив из 10 целых чисел. Подсчитайте наибольшее число одина- ковых идущих подряд в нем чисел. 55. Сформируйте одномерный массив вещественных чисел, элементы которого являют- ся расстояниями, пройденными телом при свободном падении на землю за 0,1,2,..., 10 с. 56. В доме проживают 70 семей. Найдите номер квартиры, в которой проживает самая многочисленная семья. 57. Дан одномерный массив целых чисел. Проверьте, является ли он упорядоченным по убыванию. 58. Дан двумерный массив, содержащий фамилии учащихся и номера их телефонов. По фамилии учащегося найдите номер его телефона. 59. Дана матрица целых чисел размером 10 х12. Напечатайте индексы всех ее седловых точек. (Седловой точкой называется элемент, который является наименьшим в своей строке и наибольшим в своем столбце или, наоборот, наибольшим в своей строке и наименьшим в ' своем столбце.) 60. Даны две матрицы. Получите их произведение. 61. Сформируйте и выведите на экран тю строкам трехмерный массив целых чисел раз- мером 5x5x5. Найдите максимальный элемент и укажите его положение в массиве. 62. Составьте программу, проверяющую, можно ли, меняя местами элементы одно- мерного массива А, получить одномерный массив В. 63. Дана квадратная таблица размером N*N. Составьте программу формирования по- следовательности В, элементами которой являются элементы таблицы А, расположенные под главной диагональю. В полученной последовательности найдите три наименьших эле- мента. Если количество элементов между первым и вторым элементом совпадает с количе- ством элементов между вторым и третьим, то поменяйте их местами, сохранив порядок сле- дования (без использования дополнительного массива), преобразованный линейный массив выведите на экран в строку.
220 Глава 9 64. Составьте программу, проверяющую, образуют ли элементы двумерного массива магический квадрат (в магическом квадрате суммы чисел по всем вертикалям, всем гори- зонталям и двум диагоналям одинаковы). 65. Составьте программу, отыскивающую среди пар элементов одномерного массива М те, разность между элементами которых есть величина, наибольшая для данного массива. 66. Составьте программу, меняющую местами элементы матрицы симметрично побоч- ной диагонали. 67. Составьте программу упорядочения по возрастанию элементов каждой строки дву- мерного массива С. 68. Составьте программу циклической перестановки столбцов двумерного массива К,, при которой i-й столбец становится i+1-м, а последний столбец становится первым. 69. Составьте программу вычисления суммы тех пояснительных элементов двумерно- го массива А, которые стоят в строках, не содержащих нулевых элементов. 70. Дан числовой массив Q из N элементов. Составьте программу вычисления методом деления отрезка [а,Ь] пополам корня уравнения Sinx*Sin3x = 0,5 с заданной точностью Е. В качестве А $1В выбрать пару рядом стоящих элементов массива Q. Если таких пар несколь- ко, то выберите ту из них, которая обеспечивает решение уравнения за минимальное число шагов. Найденный корень включите в массив между элементами пары, которая использова- лась для нахождения корня. В преобразованной последовательности элементы, стоящие до и после включенного корня, поменяйте местами, сохранив порядок их следования и оставив корень в качестве разделительного элемента. Замену произведите без дополнительного мас- сива. Преобразованную последовательность выведите на экран в строку.
Глава 10. МНОЖЕСТВА 10.1. ОПИСАНИЕ ТИПА МНОЖЕСТВО Множество — £го структурированный тип данных, представляющий собой набор взаимосвязанных по. какому-либо признаку или группе признаков объектов, которые можно рассматривать как единое целое. Каждый объект в множестве на- зывается элементом множества. Все элементы множества должны принадлежать одному из скалярных типов, кроме вещественного. Этот тип называется базовым типом множества. Базовый тип задается диапазоном или перечислением. Область значений типа множество — набор всевозможных подмножеств, составленных из элементов базового типа. В выражениях на языке Паскаль значения элементов множества указываются в квадратных скобках: [1,2,3,4], ['а'/Ь'/с'], ['а'..'/]. Если множество не имеет элементов, оно называется пустым и обозначается как [ ]. Ко- личество элементов множества называется его мощностью. Для описания множественного типа используется словосочетание set of (мно- жество из ... ). Синтаксическая диаграмма множественных типов имеет следующий вид: + тип Изображение множества: Исходя из синтаксической диаграммы, представим формат записи множествен- ных типов: type <имя типа> = set of <элемент1,...,элементп>; var
222 Глава 10 Идентификатор,... > : <имя типа>; Можно задать множественный тип и без предварительного описания: var Идентификатор,... > : set of <элемент1,...>; Пример: type Simply = set of ’a'. .'h' ; Number = set of 1..31; var Pr : Simply; N : Number; Letter : set of char; {Определение Множества без предвари- тельного описания в разделе типов} В данном примере переменная Рг может принимать значения символов латин- ского Алфавита от ‘а’ до ‘h’; N — любое значение в диапазоне 1..31; Letter — лю- бой символ. Попытка присвоить другие значения вызовет программное прерыва- ние. Количество элементов множества не должно превышать 256, соответственно номера значений базового типа должны находиться в диапазоне 0..255. Контроль диапазонов осуществляется включением директивы {$R+}. Объем памяти, зани- маемый одним элементом множества, составляет 1 бит. Объем памяти для пере- менной типа множество вычисляется по формуле: Объем памяти = (Max DIV 8) — (Min DIV 8) +1, где Мах и Min — верхняя и нижняя границы базового типа. 10.2. ОПЕРАЦИИ НАД МНОЖЕСТВАМИ При работе с множествами допускается использование операций отношения "=", "о", ">=", "<=", объединения, пересечения, разности множеств и операции in. Результатом выражений с применением этих операций является значение True или False. Операция "равно" (=). Два множества А и В считаются равными, если они состоят из одних и тех же элементов. Порядок следования элементов в сравнивае- мых множествах значения не имеет. Например: Значение А Значение В Выражение Результат 11,2,3,4] 11,2,3,4] А = В True [’а’/Ь'.'с'] 1'с','а'] А = В False ['a'.-'z'] ['z'-.'a'] А = В True
Множества 223 Операция "не равно" (о). Два множества А и В считаются не равными, если они отличаются по мощности или по значению хотя бы одного элемента. Например: Значение А Значение В Выражение Результат [1,2,3] [3,1,2,4] АОВ True [а1..1*] [’b'-.’zT] АОВ True ['с'-.Ч'] ['t'./c'] АОВ False Операция "больше или равно" ( >= ). Операция "больше или равно" ( >= ) используется для определения принадлежности множеств. Результат операции А >= В равен True, если все элементы множества В содержатся в множестве А. В противном случае результат равен False. Например:____________.__________________________________ Значение А Значение В Выражение Результат [1,2,3,4] [2,3,4] А>=В True ['a'-.'z'] ['b'-'t'] А>=В True fz'/x'/c'] ['с'.'х'] А>=В True Операция "меньше или равно" ( <= ). Эта операция используется аналогично предыдущей операции, но результат выражения А <= В равен True, если все эле- менты множества А содержатся в множестве В. В противном случае результат ра- вен False. Например:__________________________________________________ Значение А Значение В Выражение Результат [1,2,3] [1,2,3,4] А<=В True [•d'.’h*] ['z'..'a'] А<=В True ['a'.’v'] ['a','n','v'] А<=В True Операция in. Операция in используется для проверки принадлежности какого- либо значения указанному множеству. Обычно применяется в условных операто- рах. Например:_______________________________________________ Значение А Выражение Результат 2 if A in [1,2,3] then ... True V if A in ['a'..'n'] then ... False XI if A in [XO, XI, X2, X3] then... True При использовании операции in проверяемое на принадлежность значение и множество в квадратных скобках не обязательно предварительно описывать в раз- деле описаний. Операция in позволяет эффективно и наглядно производить слож- ные проверки условий, заменяя иногда десятки других операций. Например, выра- жение if (а=1) or (а=2) or (а=3) or (а=4) or (а=5) or (а=6) then... можно заменить бо- лее коротким выражением if a in [1 -.6] then....
224 Глава 10 Часто операцию in пытаются записать с отрицанием: X NOT in М. Такая запись является ошибочной, так как две операции следуют подряд; пра- вильная инструкция имеет вид NOT (X in м). Объединение множеств (+). Объединением двух множеств является третье множество, содержащее элементы обоих множеств. Например: Значение А Значение В Выражение Результат [1,2,3] [1,4,5] А + В [1,2,3,4,5] ['A'-'D'] ['E'-'Z'] А + В ['A’.-’Z'] [] [] А + В [] ь Пересечение множеств (*). Пересечением двух множеств является третье мно- жество, которое содержит элементы, входящие одновременно в оба множества. Например:________________________________________________ Значение А Значение В Выражение Результат [1,2,3] [1,4,2,5] А*В [1,2] ['A'.-'Z'] ['B'-'R'] А*В [•В’..-R-] [] [] А*В [] Разность множеств ( — ). Разностью двух множеств является третье множест- во, которое содержит элементы первого множества, не входящие во второе множе- ство. Например:__________________________________________________ Значение А Значение В Выражение Результат [1,2,3,4] [3,4,1] А —В [2] [-A-..-Z-] fD'.-'Z'] А —В ['А'-.'С'] [Х1,Х2,ХЗ,Х4] [Х4,Х1] А —В [Х2,ХЗ] Результат операций над двумя множествами можно наглядно представить с по- мощью закрашенных частей двух прямоугольников: Объединение Пересечение Разность Использование в программе данных типа set дает ряд преимуществ: значитель- но упрощаются сложные операторы if, увеличивается степень наглядности про- граммы и понимания алгоритма решения задачи, экономятся память, время компи- ляции и выполнения. Имеются и отрицательные моменты, основной из них — от-
Множества 225 сугствие в языке Паскаль средств ввода-вывода элементов множества, поэтому программист сам должен писать соответствующие процедуры. Упражнения Упражнение 1. Иллюстрацией описания множеств и операций над ними может слу- жить программа Dem_Mno, в которой описаны множества чисел D, DI, D2, D3. Затем они заполнены следующим образом: множество D1 — четными числами 2,4,6,8; множество D2 — числами 0,1,2,3,5; множество D3 — нечетными числами 1,3,5,7,9. После этого над мно- жествами выполнены операции объединения, разности и пересечения. program Dem_Mno; {Демонстрация операций над множествами} type Digits = set of 0. . 9; var * DI, D2, D3, D : Digits; begin Dl:=[2,4,6,8] ; D2:=[0..3,5]; D3:=[l,3,5,7,9]; D:=D1 + D2; D:=D + D3; D:=D - D2; D:=D * DI; end. {Заполнение множеств} {Объединение множеств D1 и D2} {Объединение множеств D и D3} {Разность множеств D и D2} {Пересечение множеств D и D1} Как видно из текста программы, сначала описан тип Digits = set of 0..9, затем описаны переменные D1,D2,D3,D этого типа. В первой части программы осуществляется заполнение множеств, а затем над множествами выполняются операции объединения, пересечения, раз- ности. Для проверки действия программы запустите интегрированную среду программирова- ния. Введите текст программы Dem Mno и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Так как в Паскале отсутствуют средства ввода-вывода элементов множества, то действие программы проверьте, исполняя ее по шагам и наблюдая текущие значения переменных D1,D2,D3,D в окне просмотра. Упражнение 2. Опишите множество М (1..50). Сделайте его пустым. Вводя целые чис- ла с клавиатуры, заполните множество 10 элементами. В разделе описания переменных опишем множество целых чисел от 1 до 50, перемен- ную X целого типа будем использовать для считывания числа-кандидата в множество, це- лую переменную I используем для подсчета количества введенных чисел. В начале программы применим операцию инициализации множества М:= [], так как оно не имеет элементов и является пустым. Заполнение множества элементами произведем с использованием оператора повтора for, параметр которого I будет указывать порядковый иомер вводимого элемента. Операцию заполнения множества запишем оператором присваивания М:=М+[Х]. Контроль заполнения
226 Глава 10 множества запишем с использованием операции проверки принадлежности in. Если условие X in М выполняется, выведем сообщение о том, что число X помещено в множество. Текст программы описания и заполнения множества будет таким: program Inpu_Mno; var М : set of 1.. 50; X, I : integer; begin M:= []; {M — пустое множество} for I:= 1 to 10 do begin Write('Введите ',I,' -й элемент множества: '); Readln(X); if (X in M) then {Если введенное число входит в множество М} begin Writein(X,* помещен в множество 1..50'); М:= М+[Х] ; end; end; Writein; end. Для проверки действия программы запустите интегрированную среду программирова- ния. Введите текст программы Inpu Mno и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Так как в Паскале отсутствуют средства ввода-вывода элементов множества, то действие программы проверьте, исполняя ее по шагам и наблюдая текущие значения переменных I, X и М в окне просмотра. Попробуйте задать значения чис- ла X больше 50, повторно задавать одинаковые значения X и проанализировать значения множества М. Упражнение 3. Описать множества гласных и согласных букв русского языка, опреде- лить количество гласных и согласных букв в предложении, введенном с клавиатуры. Зададим тип Letters — множество букв русского языка, затем опишем переменные это- го типа: Glasn — множество гласных букв, Sogl — множество согласных букв. Вводимое с клавиатуры предложение опишем переменной Text типа string. Для указания символа в стро- ке Text применим переменную I типа byte. Для подсчета количества гласных и согласных букв опишем переменные G и S. Блок описания программы запишется следующим образом: type Letters = set of ЛА’. . ; var Glasn, Sogl : Letters; Text : string; I : byte; G, S : byte; В начале программы запишем заполнение множеств гласных и согласных букв:
Множества 227 Glasn:=[•А’,'а',,Е’,'в','И','и’,'0',’о’,’У',’у','Э’,’э’,’Ю','ж>’,'Я Sogl:=['Б'..,Д’,'б,..'д',,Ж’,'ж','3’,'3',,К'..'Н',’к’..'н',’П'..'Т ’п’.. ’т'Ф'.. ’Д’, 'ф'.. 'щ', 'Ъ* ,’*'» ’Ь’, 'ь’ ] ; После этого запишем вывод приглашения на ввод предложения и считывание этого предложения в переменную Text Write('Введите предложение '); Readln(Text); Перед началом подсчета количества гласных и согласных, букв в предложении обнулим значения переменных G и S. Проверку принадлежности символов, составляющих предложение множествам гласных или согласных букв русского языка запишем с использованием оператора повтора for, пара- метр I которого, изменяясь от 1 до значения длины предложения, будет указывать порядко- вый номер символа в предложении. Принадлежность очередного символа предложения множеству гласных или согласных букв запишем операцией in. Если условие Text[I] in Glasn выполняется, то счетчик гласных букв G увеличивается на 1. Если выполняется условие Text[I] in Sogl, тогда увеличивается на 1 счетчик согласных букв S. Если не выполняется ни первое, ни второе условие, значит, очередной символ в предложении не является гласной или согласной буквой русского языка. Данный фрагмент программы запишется так: for I:=l to Length(Text) do begin if Text[I] in Glasn then G:=G+1; if Text [I] in Sogl tljen S:=S+1 end; В заключение программы запишем вывод сообщения о результатах подсчета гласных и согласных букв русского языка в предложении. В целом текст программы запишется таким образом: program Glasn_Sogl; {Подсчет гласных и согласных букв в предложе- нии} type Letters = set of ’А’..’я’; var Glasn, Sogl : Letters; Text : string; I : byte; G, S : byte; begin Glasn:=['A',’a’,'E','o',’И','И','O','o',’У’,’у','Э',’э','Ю','ю','Я Sogl:=['Б'..'Д',’б’..'д','Ж',’ж','3',’S’,'К'..'Н','к'..'и','П'..'Т ',,п'..'т','Ф'..'Щ,,'ф'..’щ',’Ъ’,,'ь','Ь','ь']; Write('Введите предложение '); Readln(Text); G:=0;
228 Глава 10 S:=O; for I:=l to Length(Text) do begin if Text[I] in Glasn then G:=G+1; if Text[I] in Sogl then S:=S+1 end; Writeln('B предложении "’,Text, ',G,’ гласных и ' ,S,’ со- гласных букв *); end. Запустите интегрированную среду программирования. Введите текст программы Glasn Sogl и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Проверьте действие программы, задавая различные предложения. Упражнение 4. Составить программу с контролем ввода данных, обеспечив ввод фами- лии, имени и отчества только на русском языке. В разделе описания переменных опишем множество Litera, включающее в себя всевоз- можные символы, переменную Name для хранения Ф.И.О., Ch для посимвольного ввода Ф.И.О. Переменная логического типа Rus будет указывать, принадлежит ли очередной вво- димый символ множеству букв русского языка. Для использования стандартной функции чтения кода нажатой клавиши ReadKey подключим модуль Crt. Блок описания запишется так: uses Crt; var Liters : set of char; Name : string; Ch : char; Rus : boolean; В начале программы запишем заполнение множества букв русского алфавита: Liters:—[* ','А' 'п’,'р'-.. ’я']; Затем запишем вывод запроса о вводе Ф.И.О.: Write(’Введите фамилию, имя, отчество '); Считывание всех символов, входящих в запись Ф.И.О., запишем с применением опера-' тора повтора repeat. Завершение цикла проверим по нажатии клавиши Enter (код 13). Считывание каждого символа вводимого текста Ф.И.О. запишем оператором повтора repeat, условием завершения которого зададим значение True переменной Rus. В теле опера- тора повтора сначала считаем в переменную Ch код нажатой клавиши, затем проверим, вхо- дит ли введенный символ в множество букв русского алфавита. Если условие Ch in Litera выполняется, то переменной Rus присваивается значение True, данный символ приклеивает- ся к строке Name и печатается в строке ввода, после чего управление передается на опера- тор until. Так как условие завершения цикла ввода символа на русском языке выполняется,, осуществляется переход к считыванию кода нажатой клавиши. Если очередной нажатой клавишей является Enter, то ReadKey возвращает код №#13.! Если в операторе if Cho#13 условие не выполняется, то управление передается за его пре-
Множества 229 делы, т. е. на оператор until Ch=#13, и ввод строки Name завершается, так как нажата клави- ша Enter. Данный блок программы запишется так: repeat {Считать всю строку Name} repeat}Считать один символ, входящий в множество Litera} Ch:=ReadKey; {Считать в Ch код нажаФой клавиши} if Ch<>#13 then begin Rus:=Ch in Litera; if Rus {Если символ входит в множество Litera) then begin Name:=Naye+Ch; {Приклеить введенный символ к Name) Write(Ch); {Напечатать введенный символ в строке ввода} end else {Код нажатой клавиши не входит в множество Litera} begin Writeln('Переключитесь в русский регистр*); Write('и введите Ваше имя '); end; end; until Rus;{Завершить ввод очередного символа на русском языке} until Ch=#13; {Завершить ввод строки Name т.к. нажата клавиша Enter} В заключительной части программы выполняется перевод курсора на следующую стро- ку и вывод текста "Здравствуйте,", после чего печатается значение переменной Name. Полный текст программы будет записан следующим образом: program FOI_Rus; {Ввод Ф.И.О. только на русском языке} uses Crt; var Litera : set of char; Name : string; Ch : char; Rus : boolean; begin Litera: = [' ' ,'A* .. 'n','p* 'я’]; Write('Введите фамилию, имя, отчество '); repeat {Считать всю строку Name} repeat {Считать один символ, входящий в множество Litera) Ch:=ReadKey; {Считать в Ch код нажатой клавиши} if Ch<>#13 then begin Rus:=Ch in Litera;
230 Глава 10 if Rus {Если символ входит в множество Litera) then begin Name:=Name+Ch; (Приклеить введенный символ к Name) Write(Ch); {Напечатать введенный символ в строке ввода) end else {Код нажатой клавиши не входит в множество Litera) begin Writein('Переключитесь в русский регистр'); Write('и введите Ваше имя ’); г end; ь end; until Rus;{Завершить ввод очередного символа на русском языке} until Ch=#13; {Завершить ввод строки Name т.к. нажата клавиша Enter) Writein; Writein('Здравствуйте, *,Name); end. Запустите интегрированную среду программирования. Введите текст программы FOI Rus и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Проверьте действие программы, пытаясь ввести латинские и русские символы. Пронаб- людайте в пошаговом исполнении значения переменных Ch, Name, Rus и выражений Ch=#13, Ch in Litera. Контрольные вопросы и задания Вопросы. 1. Что такое множество? Каким требованиям должны удовлетворять все элементы мно- жества? Преимущества использования типа множество. 2. Что такое базовый тип множества? Как он задается? 3. Какое множество называется пустым, как оно обозначается? 4. Как задается описание множественного типа? 5. Какие операции допустимы над множествами? Каков тип результатов выражений с применением операций над множествами? 6. Какие множества считаются равными, неравными? Имеет ли значение для сравнивае- мых множеств порядок следования элементов? 7. Для чего применяются операции "больше или равно”, "меньше или равно"? В чем их отличие? 8. Для чего применяется операция in? Особенности ее применения. 9. Что называется объединением множеств? 10. Что называется пересечением множеств?
Множества 231 11. Что называется разностью множеств? Задания. 1. Опишите множества Ml (1..10) и М2 (20..30). 2. Опишите множества R и L, содержащие русские и латинские буквы. 3. Опишите множество Pr (1..20) и поместите в него все простые числа в диапазоне 1..20. 4. Опишите множество Alf ('а'-.'я') и поместите в него гласные буквы. 5. Опишите множества Ml (1,2) и М2 (2,1). Сравните множества Ml и М2 на равенство. 6. Опишите множества Ml ('a'/b') и М2 (Ъ'/а’/с1). Сравните два этих множества на нера- венство. 7. Опишите множества Ml ('a'/b'.'c') и М2 ('а','с'). Сравните два этих множества по опе- рации >=. 8. Опишите множества Ml (1,2,3) и М2 (1,2,3,4). Сравните два этих множества по опе- рации <=. 9. Опишите множества Ml (1,2) и М2 (5,6). Получите результирующее множество М3= М1+М2. Определите, имеется ли в М3 элемент 7. 10. Опишите множества Ml (1,2,3,4) и М2 (3,4,1). Получите результирующее множест- во М3= Ml — М2. Определите, имеется ли в М3 элемент 2. 11. Опишите множества Ml (1,2,3) и М2 (1,4,2,5). Получите результирующее множест- во М3= М1*М2. Определите, имеются ли в М3 элементы 1 и 2. 12. Опишите множества R и L, содержащие русские и латинские буквы. В цикле вводи- те русские и латинские буквы и выводите соответствующее сообщение. Выход из цикла — введенная буква Z. 13. Опишите множество Pr (1..20) и поместите в него все простые числа в диапазоне 1..20. В цикле организуйте ввод чисел в диапазоне 1..20 и определите, простые они или нет. Выход из цикла — введенное значение, равное 99. 14. Имеется множество Lat ('a'./z1). Придумайте простейший способ для вывода на пе- чать аналогов его содержимого. 15. Составьте программу вычисления суммы мест, на которых в слове X стоят гласные буквы.
Глава 11. ЗАПИСИ ь Компьютеры широко используются в различных информационно-поисковых системах (адресное бюро, телефонная справочная служба, и т.п.). В реальных ин- формационных системах приходится обрабатывать и хранить большие объемы дан- ных. Как было показано в гл. 9, при решении научно-технических и экономических задач обработки совокупностей большого количества значений используются мас- сивы. Но при работе с массивами основное ограничение заключается в том, что каждый элемент массива должен иметь один и тот же тип данных. Именно поэто- му, решая задачу на формирование двухмерного массива данных о фамилиях уча- щихся и их возрасте, мы объявляли и фамилии, и возраст учащихся величинами ти- па string, а для преобразования значения возраста учащегося из типа string в вели- чину целочисленного типа использовали стандартную функцию Vai. Решение зада- чи получалось громоздким, затрачивалось время на преобразование типа. Иногда для решения задач, в которых возникает необходимость хранить и об- рабатывать совокупности данных различного типа, используются отдельные масси- вы для каждого типа данных, а для установления соответствия между ними вводят- ся соответствующие индексы. Итак, реальные данные об объектах часто описываются величинами разных ти- пов. Например, товар на складе описывается следующими величинами: наименова- ние, количество, цена, наличие сертификата качества и т. д. В этом примере наиме- нование — величина типа string, количество — integer, цена — real, наличие серти- фиката качества можно описать величиной типа boolean. Для записи комбинации объектов разных типов в Паскале применяется комбинированный тип данных — запись. Запись представляет собой наиболее общий и гибкий структурированный тип данных, так как она может быть образована из неоднотипных компонентов и в ней явным образом выражена связь между элементами данных, характеризующими ре- альный объект.
Записи 233 11.1. ОПИСАНИЕ ТИПА ЗАПИСЬ Запись — это структурированный тип данных, состоящий из фиксированного числа компонентов одного или нескольких типов. Определение типа записи начи- нается идентификатором record и заканчивается зарезервированным Словом end. Между ними заключен список компонентов, называемых полями, с указанием идентификаторов полей и типа каждого поля. Формат: type <имя типа> = record Идентификатор поля>:<тип компонента>; * Идентификатор поля>:<тип компонента> end; var Идентификатор,. . .> : Имя типа>; Пример: type Car = record Number : integer; {Номер} Marka : string[20]; {Марка автомобиля} FIO : string[40]; {Фамилия, инициалы владельца} Address : string[60] {Адрес владельца) end; var М, V : Car; В данном примере запись Саг содержит четыре компонента: номер, название марки машины, фамилию владельца и его адрес. Доступ к полям записи осуществ- ляется через переменную типа “запись”. В нашем случае это переменные М и V типа Саг. Идентификатор поля должен быть уникален только в пределах записи, однако во избежание ошибок лучше делать его уникальным в пределах всей программы. Объем памяти, необходимый для записи, складывается из длин полей. Значения полей записи могут быть использованы в выражениях. Имена отдель- ных полей не применяются по аналогии с идентификаторами переменных, посколь- ку может быть несколько записей одинакового типа. Обращение к значению поля осуществляется с помощью идентификатора переменной и идентификатора поля, разделенных точкой. Такая комбинация называется составным именем. Напри- мер, чтобы получить доступ к полям записи Саг, надо записать: М.Number, М. Marka, M.FIO, М.Address Составное имя можно использовать везде, где допустимо применение типа поля. Для присваивания полям значений используется оператор присваивания.
234 Глава 11 Пример: М.Number := 1678; М.Магка := 'ГАЗ - 24’; M.FIO ’Демьяшкин В.А.’; М.Address := 'ул. Пушкина 12 — 31'; Составные имена можно использовать, в частности, в операторах ввода-выво- да: Read(М.Number, M.Marka, M.FIO, М.Address) ; Write(М.Number:4, M.Marka:7, M.FIO: 12, M.Address:25) ; Допускается применение оператора присваивания и к записям в целом, если они имеют один и тот же тип. Например, V := М; ь После выполнения этого оператора значения полей записи V станут равны зна- чениям соответствующих полей записи М. В ряде задач удобно пользоваться массивами из записей. Их можно описать следующим образом: type Person — record FIO : string[20]; Age : 1 .. 99; Prof : string[30] end; var List : array[1..50] of Person; Обращение к полям записи имеет несколько громоздкий вид, что особенно не- удобно при использовании мнемонических идентификаторов длиной более пяти символов. Для решения этой проблемы в языке Паскаль предназначен оператор with, который имеет следующий формат: with <переменная типа Вапись> do <оператор>; Один раз указав переменную типа запись в операторе with, можно работать с именами полей как с обычными переменными, т. е. без указания перед идентифи- катором поля имени переменной, определяющей запись. Пример: Присвоить значения полям записи Саг с помощью оператора with. with М do begin Number := 2347; Mirka := ’ГАЗ - 24'; FIO : == * Петров В. И. ' ; Address := 'ул.Остужева,5' end;
Записи 235 Паскаль допускает вложение записей друг в друга (т. е. поле в записи может быть в свою очередь тоже записью), соответственно оператор with тоже может быть вложенным: with RV1 do with RV2 do with RVn do ... что эквивалентно Записи with RV1, RV2, ..., RVn do ... Уровень вложения не должен превышать 9. Записи используются обычно при работе с динамическими структурами и для организации файлов на магнитных дисках. Записи могут служить также для описа- ния комплексных чисел, так как в языке Паскаль нет для этого специальных средств. В этом случае действительная и мнимая части комплексного числа явля- ются полями записи, например: type Complex = record RealPart: real; {Действительная часть) ImagePart: real; (Мнимая часть) end; var А, В, C : Complex; {А, В, С — переменные типа Complex) begin A.RealPart:= 6.3; A. ImagePart:= 1.9; 11.2. ЗАПИСИ С ВАРИАНТАМИ Записи, представленные выше, имеют строго определенную структуру. В неко- торых случаях это резко ограничивает возможности их применения. Поэтому в язы- ке Паскаль имеется возможность задать тип записи, содержащий произвольное число вариантов структуры. Такие записи называются записями с вариантами. За- писи с вариантами обеспечивают средства объединения записей, которые похожи, но не идентичны по форме. Они состоят из фиксированной и вариантной частей. , Использование фиксированной части не отличается от описанного ранее. Вари- антная часть формируется с помощью оператора case. Он задает особое поле запи- си — поле признака, которое определяет, какой из вариантов в данный момент бу- дет активизирован. Значением признака в каждый текущий момент выполнения программы должна быть одна из расположенных далее констант. Константа, слу- жащая признаком, задает вариант записи и называется константой выбора. Формат: type Rec = record
236 Глава 11 case <поле признака> : <имя типа> of <константа выбора1> : (поле,...:тип); <константа выборап> : (поле,...:тип) end; Компоненты каждого варианта (идентификаторы полей и их типы) заключают- ся в круглые скобки. У части case нет отдельного end, как этого следовало бы ожи- дать по аналогии с оператором case. Одно слово end заканчивает всю конструкцию записи с вариантами. Необходимо отметить, что количество полей каждого из ва- риантов не ограничено. Объем памяти, необходимый для записи с вариантами, складывается из объе- мов полей фиксированной части и максимального пс^ объему поля переменной час- ти. Пример: type Rec = record Number : byte; Code : integer; case Flag : boolean of True : (Pricel : integer); False : (Price2 : real) end; var PRec : Rec; Поля Number и Code расположены в фиксированной части записи, они доступ- ны в программе в любой текущий момент независимо от значения поля признака. Поле Pricel может использоваться только в том случае, если значение поля призна- ка Flag равно True. Поле Price2 доступно в противоположном случае, т. е. если зна- чение Flag равно False. При использовании записей с вариантами необходимо придерживаться сле- дующих правил: • все имена полей должны отличаться друг от друга по крайней мере одним символом, даже если они встречаются в разных вариантах; • запись может иметь только одну вариантную часть, причем вариантная часть должна размещаться в конце записи; • если поле, соответствующее какой-либо метке, является пустым, то оно запи- сывается следующим образом: <метка>: () ; Обобщая, можно записать синтаксическую диаграмму определения типа запи- си следующим образом:
Записи 237 Запись: * список полей Список полей: Фиксированная часть: Вариантная часть: Упражнения Упражнение 1. Составим программу, которая организует ввод следующих данных об учащихся: имя, фамилия, возраст, школа, класс и записывает их в массиве записей, а затем выводит сведения об учащихся по номеру записи, по номеру класса. Так как сведения об учащихся представляют собой данные разных типов, то для описа- ния сведений об учащемся в разделе типов введем тип Record Type — запись следующей структуры: Record_Type = Record Name : String[10];
238 Глава 11 : Byte; : Integer; : Byte SurName : String[20]; Age : Byte; School : Integer; Class : Byte end; где: Name — поле для записи имени учащегося (строка до 10 символов); SurName — поле для записи фамилии учащегося (строка до 20 символов); Age — поле для записи возраста (так как значение возраста не превысит 255, тип поля можно описать как byte); School — поле для записи номера школы — целого числа; Class — поле для записи номера класса — целого числа. В разделе описания переменных опишем массив Rec^rdArray, состоящий из 10 запи- сей об учащихся описанного выше типа Record Type: Record_Array : Array[1 .. 101 of Record_Type. Для указания номера элемента массива введем переменную NumberofArray, прини- мающую целые значения от 1 до 10. Для поиска учащихся введем целочисленную перемен- ную Class, которая будет принимать значения номера класса, заданного с клавиатуры. Для ввода данных об учащихся запишем процедуру, в которой выводятся запросы на ввод данных и считываются значения этих данных в соответствующие поля записи. Для об- ращения к полям записи используем точечную нотацию, т. е. указываем имя записи, а за- тем после записываем имя поля, например: Record_Array[Number_of_Array ] . Name. Процедура ввода данных в Number of Array -й элемент массива записей Record Array запишется так: procedure Input_Data; begin Writeln ('Введите данные №',Number_of_Array,' :'); Write (' . Ваше имя ? ') ; Readln (Record_Array[Number_of_Array].Name); Write (' Фамилия ? '); Readln (Record_Array[Number_of_Array].SurName); Write (' Ваш возраст ? ') ; Readln (Record_Array[Number_of_Array].Age); Write (' Школа ? ') ; Readln (Record_Array [Number_of,_Array] .Class) ; Write (' и класс ? '); Readln (Record_Array[Number_of_Array].School); Writeln end; Вывод на экран информации, хранящейся в одном элементе массива записей, запишем с использованием предложения with ... do, которым укажем имя текущей записи — Number of Array-й элемент массива записей Record_Array[Number_of_Array], вследствие
Записи 239 чего оно будет автоматически присоединено ко всем именам полей, упоминаемым в теле предложения with. Эта процедура запишется следующим образом: procedure Write_Data; begin{Начало процедуры вывода на экран содержимого текущей записи} with Record_Array[Number_of_Array] do begin{Teno предложения with Record_Array [Number_of_Array] do} Writeln Writeln Writeln Writeln Writeln end; (' Имя ',Name); (* Фамилия : *,SurName); (* Возраст : ',Age); (' Школа : School); (' Kjjacc Class) ; {Конец тела предложения with} end; {Конец процедуры} В первой части основной программы применим оператор повтора for для записи дан- ных о 10 учащихся в массив записей. В теле этого оператора вызывается процедура Input Data, которая вводит данные в NumberofArray-й элемент массива. for Number_of _Arгay:=1 to 10 do Input_Data; Writeln; Для вывода сведений об учащемся по номеру записи, присвоим значение 5 номеру за- писи NumberofArray в массиве данных и запишем вызов процедуры WriteData. Этот фрагмент программы можно записать так: Writein ('Вывожу данные о пятом ученике : ’); Writeln; Number_of_Array:=5; Write_Data; Для поиска учащегося по номеру класса запишем вывод запроса на ввод искомого но- мера класса и считывание его значения в переменную Class. После этого применяя оператор повтора for, изменяющий значение параметра Number of Array от 1 до 10, выполним про- смотр всех элементов массива записей, сравнивая значение переменной Class со значением соответствующего поля текущей записи. Если условие Record_Array[Number_of_Array].Class=Class выполняется, значит найдена запись об учащемся, удовлетворяющая условию поиска, поэтому после слова then запишем вызов процедуры Write Data, которая выведет значения полей данной записи на экран. Данный фрагмент программы запишется так: - Writeln ('Вывожу данные по номеру класса : ') ; Writeln; Writeln (' Введите номер класса : '); Readin (Class); for Number_of _Array:=1 to 10 do
240 Глава 11 If Record_Array[Number_of_Array].Class = Class then Write_Data; В целом текст программы, которая организует ввод данных об учащихся в массив запи- сей, а затем выводит сведения об учащихся по номеру записи, по номеру класса, будет запи- сан следующим образом: program Pupil; type Record_Type = Record {Описание типа записи} Name : String[10]; SurName : String[20]; Age : Byte; School : Integer; Class : Byte end; * var Record_Array : Array[l .. 10] of Record_Type; Number_of_Array : 1 .. 10; Class : Byte; procedure Input_Data; {Процедура ввода данных в массив записей} begin Writein ('Введите данные №',Number_of_Array,' :'); Write (* Ваше имя ? ') ; Readln (Record_Array[Number_of_Array].Name); Write (' Фамилия ? '); Readln (Record_Array[Number_of_Array].SurName); Write (' Ваш возраст ? '); Readln (Record_Array[Number_of_Array].Age); Write (' Школа ? '); Readln (Record_Array[Number_of_Array].Class) ; Write (' и класс ? ') ; Readln (Record_Array[Number_of_Array].School) ; Writein end; procedure Write_Data; {Процедура вывода на экран содержимого текущей записи} begin with Record_Array [Number_of__Array] do begin Writein (' Имя *,Name); Writein (' Фамилия ',SurName); Writein (• Возраст • ,Age) ; Writein (' Школа ',School); Writein (' Класс ',Class); end;
Записи 241 end; begin {Основная программа} for Number_of _Arгay:=1 to 10 do Input_Data; Writeln; Writeln ('Вывожу данные о пятом ученике : '); Writeln; Number_of_Array:=5; Wr i te_Data; Writeln ('Вывожу данные no номеру класса : *) ; Writeln; Writeln (' Введите номер класса : *); Readln (Classi; for Number_of _Array:=1 to 10 do If Record_Array[Number_of_Array].Class = Class then Wri te_Data; end. Для проверки действия программы запустите интегрированную среду программирова- ния. Введите текст программы Pupil и запишите файл на диск под соответствующим име- нем, а затем откомпилируйте его. Проверьте действие программы, исполняя ее по шагам с заходом в процедуры и наблюдая в окне просмотра текущие значения переменных Number_of_Array, Class, а также элементов массива Record_Array[Number_of_Array]. Упражнение 2. Составим программу, которая создает каталог компьютерных про- грамм и обеспечивает поиск программ по фамилии автора. Для описания сведений о компьютерных программах в разделе типов введем тип Prog_Type — запись следующей структуры: Prog_Type = Record Title : string[50]; Author : string[50]; Entry : integer; Firms : String[40] * end; где: Title — поле для записи названия программы (строка до 50 символов); Author — поле для записи фамилии автора (строка до 50 символов); Entry—поле для записи года разработки (целое число); Firma — поле для записи фирмы-разработчика (строка до 40 символов). В разделе описания переменных введем массив ProgKatalog из десяти записей описан- ного выше типа. Переменную Num Array, принимающую значения от 1 до 10, введем для указания на порядковый номер записи в массиве Prog Katalog. Для задания шаблона поиска введем переменную Author строкового типа. Результат поиска будем записывать в перемен- ную логического типа Yes Prog.
242 Глава 11 Как и в первой программе, используем для ввода и вывода данных специальные проце- дуры, поиск программы запишем аналогично поиску учащегося, в качестве условия поиска задав условие Prog_Katalog[Num_Array].Author = Author. Для краткости записи при обраще- нии к полям записи используем форму записи с предложением with. В целом текст программы будет записан следующим образом: program Kat_Prog; {Каталог компьютерных программ}' type Prog_Type = Record Title : string[50]; Author : string[50]; Entry : integer; Firma : String [40] end; var Prog_Katalog : Array[l .. 10] of Prog_Type; Num_Array : 1 .. 10; Author : string[50]; Yes_Prog : boolean; procedure Input_Data; {Ввод сведений о программе} begin Writeln ('Введите данные о ',Num_Array,'-й программе :'); with Prog_Katalog[Num_Array] do begin Write ('Название программы? '); Readln (Title); Readln (Author); Write ('Год разработки? *) ; Readln (Entry); Write ('Фирма ? '); Readln (Firma); Writeln end; end; procedure Write_Data(Num:integer); {Вывод сведений о программе на экран} begin Writeln('Программа № ’,Num); with Prog_Katalog[Num_Array] do begin Writeln('Название ',Title); Writeln('Фамилия автора : ',Author); Writeln('Год разработки : ',Entry); Writeln('Фирма ',Firma); end;
Записи 243 end; begin for Num_Array:=1 to 3 do {Ввод данных о программах} Input_Data; Writein; {Поиск программ по фамилии автора} Writein('Поиск программы по фамилии автора'); Writein; Write('Введите фамилию автора : '); Readln(Author); Yes_Prog:=False; {He найдено программ этого автора} for Num_Array:=l to 10 do if Prog_Katalog[Num_Array].Author = Author then begin {Если*программа найдена, то напечатать сведения о ней} Write_Data(Num_Array); {Вызов процедуры вывода сведений о записи с номером Num_Array на экран (Num_Array — параметр-значе- ние) } Yes_Prog:=True; {Программа данного автора есть в каталоге} end; if not Yes_Prog then Write('Нет программ автора ',Author); end. Изучите текст программы Kat Prog, затем запустите интегрированную среду програм- мирования, введите ее текст и запишите файл на диск под соответствующим именем, а за- тем откомпилируйте его. Проверьте действие программы, исполняя ее по шагам с заходом в процедуры и наблюдая в окне просмотра текущие значения переменных Num Array, Author, Yes Prog, а также элементов массива Prog Katalog [Num_Array]. Упражнение 3. Составим программу, которая создает каталог изданий в библиотеке, обеспечивает ввод данных о литературе, поиск и подсчет количества книг данного издания. Литературу в библиотеке можно разделить на три типа изданий: книги, журналы, газеты. Для описания сведений о типе изданий в разделе типов введем перечисляемый тип Type_Publ = (Book,Journal,Newspaper). Для описания сведений о литературе в разделе типов введем тип Liter. Для разного типа изданий в каталоге требуется хранить разную информацию, например: если для поиска книги нужно знать год издания, то для журнала, помимо года издания, нужно знать его но- мер, а для газеты — не только год, месяц, но и день выпуска. В связи с необходимостью хранения разной информации в структуре записи Liter наряду с неизменной частью — поля- ми Title и Author, в которых отображается название публикации и фамилия автора будет ва- риантная часть, отражающая дату издания по-разному в зависимости от типа издания. За- пись Liter будет иметь следующую структуру: Liter = record Title : string[50]; Author : string[50]; case V : Type_Publ of {Начало вариантной части} Book (YearB : integer);
244 Глава 11 Journal (Num : 1..12; YearJ : 1900..2000); Newspaper: (Day : 1..31; Month : 1..12; YearN : integer); end; Запись вариантной части начинается служебным словом case, после которого записано поле признака выбора вариантов V, которое может принимать значения Book, Journal или Newspaper. Альтернативы вариантной части помечаются допустимыми значениями поля- признака Book, Journal и Newspaper. Справа от метки — допустимого значения поля при- знака в круглых скобках зададим списки полей, присущих данному варианту. Например, для издания типа Book (книга) предусмотрено хранение года издания (поле YearB), для издания типа Journal (журнал) — номера (поле Num) и года изданде (поле YearJ), для издания типа Newspaper (газета) — дня (поле Day), месяца (поле Month) и года выпуска (поле YearN). В разделе описания констант зададим значение максимального числа записей в катало- ге Count = 10. В разделе описания переменных введем массив записей Katalog[l .. Count] типа Liter. Для указания порядкового номера записи в массиве Katalog опишем переменную Num Array, принимающую значения от 1 до Count. Переменную логического типа Yes Liter введем для хранения значения результата поиска литературы. Для ввода вида из- дания с помощью стандартной процедуры Read введем целочисленную переменную Vybor. Переменную Edition типа Type Publ введем для задания типа искомого издания. Количест- во изданий искомого типа опишем целочисленной переменной Count Find. Раздел описания констант и переменных можно записать так: const Count = 10; var Katalog Array[1 .. Count] of Liter; Num_Array : 1 .. Count; Yes_Liter : boolean; Vybor : byte; Edition : Type_Publ; Count_Find : integer; Действие программы можно записать в виде трех процедур: ввод данных, поиск нужно- го издания и вывод сведений о найденных изданиях на экран. Процедура ввода данных в каталог литературы может быть записана так: procedure Input_Data; {Процедура ввода данных о литературе} begin Writeln; Writeln ('Введите данные о литературе ’,Num_Array,’ :'); Write('Введите число, указывающее вид издания:'); Write('l — книга, 2 — журнал, 3 — газета : '); Readln (Vybor) ; case Vybor of {Присваивание полю признака конкретного значения) 1 : Katalog[Num_Array] .V:=Book;
Записи 245 2 : Katalog[Num_Array].V: «Journal; 3 : Katalog[Num_Array].V:«Newspaper; end; with Katalog[Num_Array] do begin Write (* Фамилия автора? '); Readln (Author); Write ('Название ? '); Readln (Title); case V of {Начало вариантной части записи данных} Book : begin Wri*te ( * Год издания ? ’ ) ; Readln (YearB); end; Journal : begin Write ('Номер ? ’) ; Readln (Num); Write ('Год издания ? ’); Readln (YearJ); . end; Newspaper : begin Write ('Дата издания: День ? ') ; Read (Day); Write ('Месяц ? '); Read (Month); Write ('Год ? '); Readln (YearN); end; end; end; > end; В начале процедуры записан запрос о типе издания, и в переменную Vybor считывает- ся с клавиатуры целочисленное значение. Затем в зависимости от значения переменной Vybor (1,2 или 3) полю признака V присваивается конкретное значение типа издания Book, Journal или Newspaper. Затем описан запрос и ввод инвариантных данных для любых изда- ний: фамилия автора и название издания. При этом для краткости при обращении к полям записи используем форму с предложением with. Вариантная часть записи начинается словом case и строго отвечает структуре вариант- ной части записи в описании типа Liter. Соответствующие типу издания сообщения на ввод
246 Глава 11 данных и считывание с клавиатуры значений запишем в виде составных операторов справа от меток, имеющих значения конкретных типов издания (Book, Journal или Newspaper). Процедура вывода информации об издании на экран компьютера по структуре анало- гична: сначала выводится сообщение из неизменной части записи, а затем в зависимости от значения поля признака Katalog[Num_Array].V выводятся данные о дате издания. Текст этой процедуры можно записать так: procedure Write_Data; begin Writein; Writein('Литература № ',Num_Array) ; with Katalog[Num_Array] do begin Writein('Название *,Title); * Writein('Фамилия автора : ',Author); case V of {Начало вывода вариантной части записи данных} Book : begin Writein (Тод издания : *,YearB); end; Journal : begin Write ('Номер : ',Num); Writein (Тод издания : ', Year J) ; end; Newspaper : begin Writein ('Дата издания: День: ',Day,' Месяц: '.Month,' Год: ',YearN); end; end; end; end; В начале процедуры поиска нужного издания после запроса типа искомого издания и считывания с клавиатуры значения переменной Vybor присвоим переменной Edition типа Type Publ значения Book, Journal или Newspaper. Затем присвоим переменной YesLiter значение False и установим количество найденной литературы равным нулю. Поиск литературы заданного типа издания запишем оператором повтора for, управляю- щая переменная Num Array которого, изменяясь от 1 до Count, указывает порядковый но- мер записи Liter в массиве Katalog. Оператор условия if в теле цикла сравнивает значение поля признака V очередной записи со значением переменной Edition, в которой записан ис- комый тип издания. Если условие Katalog[Num_Array].V = Edition выполняется, то перемен- ной Yes Liter присваивается значение True, счётчик количества найденной литературы дан- ного типа издания Count Find увеличивается на 1, и вызывается процедура Write Data, вы- водящая сведения из текущей записи на экран, и т. д., пока не будут просмотрены все запи- си массива Katalog. Если при просмотре всего массива ни одной записи указанного типа из- дания не будет обнаружено, то значение переменной Yes Liter останется False.
Записи 247 В заключительной части процедуры в зависимости от значения переменной YesLiter будет выведено итоговое сообщение о результатах поиска. Если значение переменной Yes Liter останется False, то на экран будет выведено сообщение 'В библиотеке нет такой литературы', иначе будет напечатано количество литературы в библиотеке такого типа изда- ний. Текст данной процедуры будет записан так: procedure Find_Liter; {Поиск литературы по типу издания} begin Writeln('Поиск литературы по типу падания'); Writeln; Write('Введите число, указывающее вид издания:'); Write('l — книга, 2 — журнал, 3 — газета : '); Readln (Vybor) ; case Vybor of * {Задать тип издания} 1 : Edition:=Book; 2 : Edition:=Journal; 3 : Edition:=Newspaper; end; Yes_Liter:=False; {He найден ни один экземпляр издания} Count_Find:=0; for Num_Array:=1 to Count do if Katalog[Num_Array].V = Edition then begin Yes_Liter:=True; {Есть такое издание в библиотеке} Count_Find:=Count_Find+l; Write_Data; end; if not Yes_Liter then Writeln('В библиотеке нет такой литерату- ры') else Writeln('Всего в библиотеке *,Count_Find,' таких изда- ний') ; end; В тексте основной части программы запишем ввод данных с использованием оператора повтора for, в теле которого вызывается процедура InputData. После ввода данных запишем вызов процедуры поиска нужного издания. Процедура вывода информации об издании на экран компьютера вызывается из процедуры поиска. Полный текст программы, создающей каталог изданий в библиотеке и обеспечивающей ввод данных, поиск и подсчет количества книг данного издания, будет записан следующим образом: program Kat_Libr; {Каталог литературы в библиотеке} type Type_Publ = (Book,Journal,Newspaper); Liter = record Title : string[50]; Author: string[50];
248 Глава 11 case V : Type_Publ of {Начало вариантной части записи} Book (YearB : Integer); Journal : (Num : 1..12; YearJ : 1900..2000); Newspaper: (Day : 1. . 31 ,* Month : 1..12; YearN : Integer); end; const Count = 10; var Katalog : Array[1 .. Count] of Liter; Num_Array : 1 .. Count; Yes_Liter : boolean; * Vybor : byte; Edition : Type_Publ; Count_Find : Integer; procedure Input_Data; {Процедура ввода данных о литературе} begin Writeln; Writeln ('Введите данные о литературе ' ,Num_Array,' :'); Write('Введите число, указывающее вид издания:'); Write(Ч — книга, 2 — журнал, 3 — газета : '); Readln(Vybor); case Vybor of 1 : Katalog[Num_Array].V:=Book; 2 : Katalog[Num_Array] . V:=Journal; 3 : Katalog[Num_Array]. V: =Newspaper ; end; with Katalog[Num_Array] do begin Write ('Фамилия автора? '); Readln (Author); Write ('Название ? '); Readln (Title); case V of {Начало ввода данных в вариантную часть записи} Book : begin Write ('Год издания ? '); Readln (YearB); end; Journal : begin Write ('Номер ? *); Readln (Num);
Записи 249 Write ('Год издания ? '); Readln end; Newspaper : begin (YearJ); Write ('Дата издания: День ? '); Read (Day) ; Write ('Месяц ? ') ; Read (Month); Write (Тод ? •); Readln end; end; , ь end; (YearN) ; end; procedure Write_Data; begin Writein; Writein('Литература № ',Num_Array); with Katalog[Num_Array] do begin Writein('Название ',Title); Writein('Фамилия автора : ',Author); case V of {Начало вывода на экран вариантной части записи} Book : begin Writein ('Год издания : ',YearВ); end; Journal : begin Write ('Номер : ' ,Num) ; Writein ('Год издания : ',YearJ); end; Newspaper : begin Writein ('Дата издания: День: ',Day,' Месяц: ',Month,' Год: ',YearN); end; end; end; end; procedure Find_Liter; {Поиск литературы по типу издания} begin Writein(’Поиск литературы по типу издания'); Writein; Write('Введите число, указывающее вид издания:'); Write('1 — книга, 2 — журнал, 3 — газета : ');
250 Глава 11 Readln(Vybor); case Vybor of {Задать тип издания} 1 : Edition:—Book; 2 : Edition:—Journal; 3 : Edition:—Newspaper; end; Yes_Liter:=False; {He найден ни один экземпляр издания} Count_Find:=0; for Num_Array:=1 to Count do if Katalog[Num_Array].V — Edition then begin Count Find:=Count Find+1; — ь Wrx te_Data; Yes_Liter:=True; {Есть такое издание в библиотеке} end; if not Yes_Iiiter then Writeln('В библиотеке нет такой литерату- ры’) else Writeln('Всего в библиотеке ',Count_Find,' таких пада- ний') ; end; begin {Основная программа} for Num_Array:=1 to Count do Input_Data; {Вызов процедуры ввода данных} Writeln; Find_Liter; {Поиск литературы} end. Запустите интегрированную среду программирования, введите текст программы Kat Libr и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Проверьте действие программы, исполняя ее по шагам с заходом в процедуры и наблю- дая в окне просмотра текущие значения переменных Num Array, Yes Liter, Vybor, Edition, Count Find, а также элементов массива записей Katalog. Особенно тщательно пронаблюдай- те за изменением значения поля-признака Katalog[Num_Array].V и обращением в связи с этим к разным частям вариантного компонента записи Liter. Контрольные вопросы и задания Вопросы. 1. Почему запись называют комбинированным типом данных? 2. Как определяется тип записи? Что называется полем записи? 3. Какие требования предъявляются к идентификаторам поля в записи? 4. Чем определяется объем памяти, требуемый для размещения записи? 5. Что такое составное имя поля записи? Из каких частей оно состоит и как записывает- ся?
Записи 251 6. Зачем при обращении к полю записи используется предложение with? 7. Как вы понимаете вложение записей? Каков максимально допустимый уровень вло- жения? Приведите примеры вложения записей. 8. Зачем применяются записи с вариантами? Из каких частей состоит запись с вариан- тами? 9. Что называется полем признака? Для чего оно записывается в операторе case? 10. Как записываются компоненты каждого варианта записи? 11. Какие правила следует соблюдать при использовании записей с вариантами? Задания. 1. Опишите запись с именем типа Karta, содержащую следующие поля: • номер измерения^тип integer); • значение (тип real). Переменную, определяющую запись, назовите Z. 2. Опишите запись с именем типа Doc, содержащую следующие поля: • номер строки документа (тип integer); • текст строки (тип string). Переменную, определяющую запись, назовите S. 3. Опишите запись с именем типа Tovar, содержащую информацию'о хранящемся на складе товаре: • код товара (тип integer); • наименование товара (тип string); • цену (тип real). Переменную, определяющую запись, назовите Tov. 4. Опишите запись с именем типа Graf, содержащую данные, необходимые для по- строения графика из 40 точек: • название графика (тип string); • 40 значений (тип integer). Переменную, определяющую запись, назовите X. 5. Опишите запись с именем типа Baza, содержащую информацию для школьной базы данных: • личный номер ученика (тип integer); • ФИО (тип string); • год рождения (тип integer); • адрес (тип string). Переменную, определяющую запись, назовите Inf. 6. Опишите запись с именем типа Systema, содержащую информацию о планетах сол- нечной системы: • номер планеты по удалению от Солнца (тип integer);
252 Глава 11 • название планеты (тип string); • объем (real); • диаметр (real); • удаленность от Земли (real). Переменную, определяющую запись, назовите Planeta. 7. Опишите запись с именем типа Sport, содержащую информацию о лучших спортив- ных достижениях школы по легкой атлетике: • название вида (тип string); • фамилия рекордсмена (тип string); • дата установления рекорда (запись Dat, состоящая из полей Day, Month, Year); • сообщение о результате (real). * Переменную, определяющую запись, назовите Rec. 8. Опишите запись с именем типа Geotnetr, содержащую информацию об оценках уче- ников вашего класса по геометрии: • ФИО (тип string); • оценки за девять месяцев max по 20 оценок в месяц. Переменную, определяющую запись, назовите Dig. 9. Опишите запись с именем типа Rasp, содержащую информацию о движении элек- тропоездов из вашего города: • направление (тип string); • время отправления электропоездов (тип real). Переменную, определяющую запись, назовите R. 10. Опишите запись с именем типа Post, содержащую информацию в почтовой базе данных о подписчиках на газеты и журналы: • ФИО (тип string); • адрес (тип string). • 10 строк с названиями газет и журналов. Переменную, определяющую запись, назовите G. 11. Опишите запись с именем типа Boln, содержащую информацию в больничной базе данных о стационарных больных: • ФИО (тип string); • возраст (тип integer); • адрес (тип string) • дату поступления (тип string); • диагноз (тип string); • ФИО лечащего врача (тип string). Переменную, определяющую запись, назовите В.
Записи 253 12. Опишите запись с именем типа Tovar, содержащую информацию о хранящемся на складе товаре: • код товара (тип integer); • наименование товара (тип string); • цену (тип real). Переменную, определяющую запись, назовите Tov. Без помощи with присвойте значе- ние (10, 'туфли женские', 45200.00) полям одной из записей. 13. Опишите запись с именем типа Data, содержащую информацию о средней темпера- туре в хранилище за 30 дней: • номер месяца (тип integer); • температура (тип real). Переменную, определяющую запись, назовите Zamer. Без помощи with присвойте запи- си начальное значение: месяц 'июль' и температура для первого дня 9,5. 14. Опишите запись с именем типа Graf, содержащую данные, необходимые для по- строения графика из 40 точек: • название графика (тип string); • 40 значений (тип integer). Переменную, определяющую запись, назовите X. С помощью with присвойте полям за- писи следующие значения: название графика 'Y:=f(T)', значения первых трех точек: 5,7,9. 15. Опишите запись с именем типа Post, содержащую информацию в почтовой базе данных о подписчиках на газеты и журналы: • ФИО (тип string); • адрес (тип string). • 10 строк с названиями газет и журналов. Переменную, определяющую запись, назовите G. С помощью with присвойте следую- щие значения полям: 'Петров И.В.', 'г.Москва, ул.Горького, 5', 'Московский комсомолец', 'Спорт'. 16. Измените программу из упражнения 2 так, чтобы сведения об учащемся включали еще и адрес и был возможен поиск учащихся по фамилии. 17. Составьте программу, которая описывает массив записей — телефонный справоч- ник одноклассников — и обеспечивает ввод данных, поиск номера телефона по фамилии, подсчет и вывод списка всех абонентов по критерию “увлечение компьютерными играми”. В записи о каждом однокласснике содержатся следующие сведения: .фамилия, имя, телефон, хобби. 18. Составьте программу, которая описывает таблицу химических элементов, отобра- жая следующую информацию: название, символическое обозначение, массу атома, заряд атомного ядра, перечень основных химических свойств. Программа должна выполнять вы- вод данных о химическом элементе по указанному символическому обозначению, находить элемент с самой большой массой, с самым маленьким зарядом ядра. 19. Составьте программу, которая описывает массив записей жильцов дома, отображая в нем следующую информацию о каждом: номер квартиры, фамилия, имя, возраст, для лиц
254 Глава 11 старше 18 лет в зависимости от рода занятий (учеба, работа, пенсия) — запись места учебы, места работы и трудового стажа, для пенсионеров — год выхода на пенсию. Программа должна обеспечивать ввод данных, поиск квартиры с максимальным числом жильцов, по- иск самого юного и самого пожилого жильца, поиск студентов, пенсионеров. 20. Опишите, используя структуру записи, вступительные экзамены, на которых абиту- риенты сдавали три экзамена, а для поступления надо было набрать 12 баллов. Составьте программу, считывающую с клавиатуры результаты всех вступительных экзаменов и выво- дящую на экран следующую информацию: а) список абитуриентов, сдавших все три экзамена на 5; б) список абитуриентов, потерпевших неудачу на экзаменах; в) список абитуриентов, зачисленных в институт. 21. Опишите, используя структуру записи, школьный журнал. Предусмотрите в записи поля для хранения информации о фамилии учащегося, предмете, оценке. Составьте про- грамму, считывающую с клавиатуры данные об успеваемости учащихся класса и выводя- щую на экран сведения об отличниках класса, о средней успеваемости учащихся класса. 22. Опишите, используя структуру записи, школьный класс (фамилия, инициалы, дата рождения, месяц рождения, год рождения). Составьте программу, считывающую с клавиату- ры данные об учащихся класса и выводящую иа экран данные о днях рождения учащихся по месяцам, например: январь 12 Петров И.В. 23 Каменский С.А. 25 Костин А.В. февраль 5 Демин А.С. 11 Иванов Л.Т.
Глава 12. ФАЙЛЫ 12.1. ОБЩИЕ СВЕДЕНИЯ Большие совокупности данных, например сведения обо всех учащихся школы или телефонный справочник, удобно иметь записанными во внешней памяти в виде последовательности сигналов. Любой сколько-нибудь развитый язык программиро- вания должен содержать средства для организации хранения информации на внеш- них запоминающих устройствах и доступа к этой информации. В Паскале для этих целей предусмотрены специальные объекты — файлы. Файлом называется совокупность данных, записанная во внешней памяти под определенным именем. Целесообразность применения файлов диктуется следующими причинами. 1. Ввод больших объемов данных, подлежащих обработке, утомителен и требу- ет большого времени. Гораздо удобнее создать отдельный файл данных, который может быть подготовлен заранее и, самое главное, применяться неоднократно. 2. Файл данных может быть подготовлен другой программой, становясь, таким образом, связующим звеном между двумя разными задачами, а также средством связи программы с внешней средой. 3. Программа, использующая данные из файла, не требует присутствия пользо- вателя в момент фактического исполнения. 12.2. НЕКОТОРЫЕ СВЕДЕНИЯ О ФАЙЛОВОЙ СИСТЕМЕ MS DOS Каждый файл на диске имеет обозначение, которое состоит из двух частей: имени и расширения. В имени файла может быть от 1 до 8 символов. Расширение начинается с точки, за которой следуют от 1 до 3 символов, например: command.com, turbo.exe, autoexec.bat, turbo.tph, proba.pas. Имя файла — это любое выражение строкового типа, которое строится по правилам определения имени в MS DOS: • имя содержит до восьми разрешенных символов (прописные и строчные ла- тинские буквы, цифры и символы:!, @, #, %, л, &, (,), ’, ~, —,
256 Глава 12 • за именем может быть расширение — последовательность до трех разрешен- ных символов; расширение, если оно есть, отделяется от имени точкой. Максимальная длина полного имени файла не должна превышать 79 символов. Расширение имени файла, как правило, описывает содержание файла и не яв- ляется обязательным. Многие программы устанавливают расширение имени файла, по нему вы можете узнать, какая программа создала файл. Например: сот, ехе — готовые к исполнению программы; bat — командные (Batch) файлы; txt, doc — текстовые файлы; hip — файл справочной службы; pas — файл программы на языке Паскаль; ь asm — файл программы на языке Ассемблер; bak — копия файла, делаемая перед его изменением; pic, psx, tif — файлы, созданные с помощью графических редакторов или ска- неров и содержащие графическую информацию; dat — файл данных; arj — файл архива, созданный архиватором arj. Примечание: Пакетный файл — это стандартный текстовый файл, содержащий коман- ды DOS и имена программ. Пакетный файл, как и команда DOS, выполняется вводом его имени в ответ на подсказку DOS. Пакетные файлы часто применяются при разработке про- грамм, так как они экономят время и автоматизируют повторяющиеся этапы работы. Текстовые файлы созданы с помощью текстовых редакторов и содержат информацию, которую пользователь может прочитать на экране, отредактировать, напечатать на принте- ре; bak — файл — файл копии с тем же именем, что и у исходного, сделанной перед запи- сью обновленной версии файла на диск. Наличие такой копии позволяет восстановить со- держимое файла в случае его ошибочного изменения или удаления. Устройства в Турбо Паскале. Входящие в компьютер компоненты принято называть устройствами. Некоторые устройства компьютера наряду с файлами во внешней памяти Турбо Паскаль использует как файлы. При этом для указания этих устройств используются специальные имена (которые не могут иметь "настоя- щие" файлы), например: А:, В: — накопители на гибких магнитных дисках; С:, D:, Е: и т.д. — винчестер; CON — пульт управления (консоль), при выводе этим устройством является экран видемонитора, а при вводе — клавиатура; LPT1, LPT2, LPT3 — устройства, подключенные к параллельным портам 1,2,3 (портом называют многоразрядный вход или выход в устройстве. Чаще всего к порту LPT1 подключен принтер);
Файлы 257 PRN — принтер; COMI, COM2, COM3 — устройства, подключенные к последовательным пор- там 1,2,3; AUX — устройство, подключенное к асинхронному порту; NUL — "пустое" устройство. Примечание. Устройство Nul работает следующим образом: при чтении с него про- грамме сообщается о конце файла, а при выводе на него информация на самом деле никуда не выводится, но программе, которая делала вывод, сообщается, что вывод произошел ус- пешно. Неотъемлемой характеристикой каждого файла являются размер, дата и время создания файла. Размер файла определяется числом входящих в него байтов. Дата и время создания файл? изменяются при изменении файла. Имена файлов регистрируются на магнитных дисках в каталогах (или директо- риях). Каталог (directory) — специальное место на диске, в котором хранятся име- на файлов, сведения о размере файлов, дате и времени их последнего обновления, атрибуты (свойства) файлов. На магнитном диске может быть несколько каталогов. Имена каталогов задаются по таким же правилам, как имена файлов. Как правило, расширение имени для каталогов не используется. Если каталог X зарегистрирован в каталоге Y, то говорят, что X — подкаталог (или вложенный каталог), a Y — ро- дительский каталог. Каждый магнитный диск обязательно имеет главный (корневой) каталог, в котором зарегистрированы файлы пользователей и подкаталоги первого уровня, в которых, в свою очередь, зарегистрированы файлы пользователей, и подкаталоги второго уровня, и т.д. Так, структура оглавлений растет подобно дереву, как пока- зано на рис. 12.1. Главный (корневой) каталог диска Файлы Каталоги первого уровня главного каталога Файлы каталога Каталоги первого уровня второго уровня Файлы каталога второго уровня Рис. 12.1. Организация каталогов на диске Корневое оглавление содержит все главные “стволы”, а каждый ствол может содержать свои собственные ветви. “Листьями” такого дерева являются файлы. 9-116
258 Глава 12 Древообразная система оглавлений позволяет вам категоризировать файлы, поме- щая файлы с однотипными данными в каталог, зарезервированный для этого типа данных. Последовательность оглавлений, ведущая от корневого каталога к файлу, назы- вается путем. Путь к файлу в сочетании с именем представляет строку символов, полностью идентифицирующую файл, и называется спецификацией (или полным именем), например: E:\WORK\PASCAJL\TURBO.EXE или d:\program\proba.pas. Таким образом, обобщенный вид полного имени файла выглядит следующим обра- зом: Диск:\ИмяКаталога\...\ИмяКаталога\ИмяФайла, где идентификатор <Диск> задается литерой логического устройства от А: до Z:, и если он опущен, то подразу- мевается логическое устройство, принятое по умолчанию. Если опущены иденти- фикаторы <ИмяКаталога>, то по умолчанию принимается, что файл находится в те- кущем каталоге. 12.3. ОПИСАНИЕ ФАЙЛОВОГО ТИПА Любой файл имеет три характерные особенности. Во-первых, у него есть имя, что дает возможность программе работать одновременно с несколькими файлами. Во-вторых, он содержит компоненты одного типа. Таким компонентом может быть любой тип Турбо Паскаля, кроме файлового. Например, допускается файл запи- сей или файл строк, но нельзя создать "файл файлов". В-третьих, длина вновь создаваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти. Все это позволяет считать файлы од- ной из наиболее фундаментальных структур данных в Турбо Паскале. В большинстве случаев файлы состоят из текстовых строк, или записей. Для описания файла используется словосочетание file of. Синтаксическая диаграмма для файловых типов выглядит так: Для доступа к файлу описывается специальная файловая переменная, которая считается представителем файлов в Паскаль-программе (чаще всего ее обозначают как F). Если файл состоит из записей, дополнительно описывается переменная для доступа к полям записи (обозначим ее R). Формат: type < имя типа> = <тип компонентов:»; var < F> file of <имя типа>; < R> : <имя типа>;
Файлы 259 Файл можно представить как потенциально бесконечный список значений од- ного и того же (базового) типа. Все элементы файла считаются пронумерованными, начальный элемент имеет нулевой номер. Файл элемент 1 элемент 2 элемент 3 элемент 4 Текущий указатель В любой момент времени программе доступен только один элемент файла, на который ссылается текущий указатель (указатель обработки). Часто позицию размещения доступного элемента называют текущей позицией. Как правило, все действия с файлом (чтение из файла, запись в файл) произво- дятся поэлементно, причем в этих действиях участвует тот элемент файла, который обозначается текущим указателем. В результате совершения операций текущий указатель может перемещаться, настраиваясь на тот или>иной элемент файла. По способу доступа к элементам различают файлы последовательного и прямо- го доступа. Файлом последовательного доступа называется файл, к элементам которого обеспечивается доступ в такой же последовательности, в какой они запи- сывались. Файлом прямого^дОступа называется файл, доступ к элементам которо- го осуществляется по адресу элемента. Например, для поиска нужного элемента в последовательном файле необходимо, начиная с нулевого, перемещать указатель обработки до тех пор, пока он не будет указывать на искомый элемент, а при поис- ке нужного элемента в файле прямого доступа достаточно указать номер его пози- ции. При организации данных в файл последовательного доступа нельзя одновре- менно читать данные из файла и записывать данные в файл, так как для чтения не- которого элемента последовательного файла указатель обработки помещен на дан- ный элемент, а для записи нового элемента этот указатель одновременно должен быть в конце файла. Компилятор Турбо Паскаля поддерживает три типа файлов: текстовые, типизи- рованные, нетипизированные. Прежде чем рассмотреть каждый тип файла в от- дельности, следует познакомиться со стандартными средствами поддержки ввода- вывода в Турбо Паскале. Средства обработки файлов. Файловая система на Паскале наиболее полно использует возможности операционной системы по передаче данных. Каждому файлу в языке ставится в соответствие файловая переменная определенного типа, поэтому перед началом работы с файлом необходимо установить данное соответст- вие. Для этого в языке используется процедура Assign (var F; Name: string);
260 Глава 12 где F — переменная любого файлового типа, а строковое выражение Name содер- жит полное имя файла, удовлетворяющее требованиям операционной системы. Процедура Assign всегда предшествует другим процедурам работы с файлами, так как ставит в соответствие конкретному файлу на внешнем устройстве логиче- скую файловую переменную языка, к которой впоследствии будут обращаться все другие файловые процедуры. Недопустимо использование процедуры Assign для уже открытого файла. Это значит, что если было назначено имя конкретного набо- ра данных файловой переменной с помощью процедуры Assign, а затем этот файл был открыт, то, прежде чем использовать ту же файловую переменную для нового набора данных, необходимо с помощью процедуры Close закрыть этот файл. Для работы с файлом прежде всего необходимо его открыть. В языке Паскаль предусмотрены для этого две процедуры: t Reset(var F : file ); — открывает существующий файл; Rewrite(var F : file ); — создает и открывает новый файл. При описании обеих процедур параметр File означает файловую переменную любого типа. Открытие внешнего файла с помощью процедуры Reset в случае его отсутствия на диске может привести к ошибке при выполнении программы. По- добные ошибочные ситуации в операциях ввода-вывода позволяет отслеживать специальная функция lOresult. Пример: Стандартное открытие файла. Assign(F, '•); Reset(F); При назначении, файловой переменной пустой строки происходит автоматиче- ская ссылка на стандартный файл ввода, что в модуле SYSTEM соответствует уст- ройству CON. С открытием такого файла появляется возможность ввода данных с клавиатуры. Имеются некоторые отличия в использовании процедуры Reset при открытии различных типов файлов. В отношении текстовых файлов (тип Text) действие про- цедуры означает открытие файла только для чтения. Для нетипизированных фай- лов в описание процедуры добавляется еще один параметр RecSize типа word, кото- рый устанавливает длину записи для функций обмена с файлом. Процедура Reset для нетипизированного файла имеет вид: Reset(var F: file ; RecSize: word); Процедура Rewrite создает и открывает новый файл. Использование этой про- цедуры требует особого внимания. При попытке создать и открыть новый файл с именем уже существующего на диске набора данных действие процедуры Rewrite сведется к удалению этого набора и созданию нового пустого файла с тем же име- нем. При открытии новых нетипизированных файлов для задания длины записи в описание процедуры Rewrite добавляется дополнительный параметр RecSize типа word. В этом случае процедура имеет вид:
Файлы 261 Rewrite(var F: file ; RecSize: word); Если процедура Rewrite используется для текстового файла, то к открываемому новому набору данных в дальнейшем могут быть применимы только операции за- писи. Операция закрытия файла является логическим окончанием работы с любым открытым файлом. Для этого служит процедура Close(var F); Использование процедуры Close позволяет устранить связь файловой перемен- ной с внешним файлом, установленную с помощью процедуры Assign. Пример: Полная цепочка команд для создания простого текстового файла с именем WORK.TXT: var ь F: text; begin Assign(F, 'WORK.TXT'); Rewrite(F); Write(F, 'Простой текстовый файл'); Close(F); end. К языковым средствам обслуживания файлов необходимо отнести процедуры переименования и удаления неоткрытых файлов. Использование этих процедур не зависит от типа файла. Rename(var F; NewName : string); Процедура переименовывает неоткрытый файл F любого типа. Новое имя зада- ется строкой NewName. Erase(var F); Процедура удаляет неоткрытый внешний файл любого типа, задаваемый пере- менной F. Обе процедуры нельзя использовать для уже открытых файлов. В противном случае могут возникнуть нежелательные последствия со стороны операционной системы. Единственным стандартным шагом перед использованием процедур яв- ляется установка связи между внешним файлом с конкретным именем и файло- вой переменной. Операции удаления и переименования осуществляются только для реально существующих файлов, иначе возникает ошибка выполнения программы. Пример: Удаление или переименование файла. var F: file ; Ch: char; St: string; begin Write('Введите имя файла: ');
262 Глава 12 Readln(St); {Чтение имени) Assign(F, St); {Назначить имя файловой переменной) Write('Удалить файл (У), Переименовать(П), Выход(В)'); Readln(Ch); case Ch of 'У*, *y' : Erase(F); {Удаление- файла} 'П*,'п' : begin Write('Введите новое имя файла: '); Readln(St); Rename(F, St); {Переименование файла) end; •В','в' : Halt(l); end; {Case} end. * В приведенном примере альтернативный выбор тех или иных действий зависит целиком от того, что будет введено с клавиатуры. Этот вариант программы не по- зволяет обработать ошибочные ситуации в случае, если файла с именем St не суще- ствует на диске. Для того чтобы файловые операции выполнялись четко и без ошибок, необхо- димо использовать специальную функцию lOresult. Функция работает без парамет- ров и возвращает значение типа integer, представляющее статус последней выпол- ненной операции ввода-вывода. Использование этой функции в программах воз- можно лишь в том случае, если на время выполнения файловых операций отключе- на стандартная проверка операций ввода-вывода. Для этих целей используется ли- бо специальная опция в интегрированной системе, либо директива компилятора {$!}, которая может задаваться внутри текста программы. Пример: Вариант программы для проверки существования файла на диске. var F: file ; St: string; begin Write (' Введите имя файла : ') ;. Readln(St); St) ; {Отключить стандартную обработку ошибок) {Открыть файл) {Включить стандартную обработку ошибок) if lOresult — 0 then begin Writein('Файл существует и нормально открыт’); Close(F); {Закрыть файл) end Assign(F, {$!-) Reset(F); {$!+)
Файлы 263 else Writeln('Файла с именем '+St+' на диске нет'); end. После корректного выполнения операции ввода-вывода функция lOresult воз- вращает значение, равное нулю, в остальных случаях функция возвращает соответ- ствующий код ошибки. Рассмотренные операции ввода-вывода охватывают все типы файлов в Турбо Паскале и характеризуют взаимоотношения файловой и операционной систем. Примечание. Чтобы не загромождать текста программ примеров, в большинстве из них проверок на ошибки ввода-вывода не сделано. Для их локализации можно использо- вать стандартную функцию lOresult. ь 12.4. ТЕКСТОВЫЕ ФАЙЛЫ Текстовый файл можно рассматривать как последовательность символов, раз- битую на строки длиной от 0 до 256 символов. Для описания используется стан- дартный тип Text: var F: text; {F — файловая переменная} Каждая строка завершается маркером конца строки. На практике такой маркер представляет собой последовательность из двух символов: перевод строки chr(13) и возврат каретки chr(10). Эти два символа задают стандартные действия по управлению текстовыми файлами. Стандартно открываемые предопределенные файлы Input и Output в модуле System имеют тип Text. У текстовых файлов своя специфика. Специальные расширения стандартных процедур чтения Read и записи Write разрешают работать со значениями несим- вольного типа. Другими словами, последовательность символов автоматически преобразуется к значению того типа переменной, которая используется в файловых операциях. Вызов Read(F, Ww), где Ww — переменная типа word, осуществляет чтение из файла F последовательности цифр, которая затем интерпретируется в число, значение которого и будет присвоено переменной Ww. В случае если вместо после- довательности цифр идет любая другая последовательность символов, использова- ние такого оператора приводит к ошибке выполнения программы. Открытие текстового файла можно произвести двумя стандартными спосо- бами: • поставить в соответствие файловой переменной имя файла (процедура Assign), открыть новый текстовый файл (процедура Rewrite); • поставить в соответствие файловой переменной имя файла (процедура Assign), открыть уже существующий файл (процедура Reset).
264 Глава 12 Текстовый файл в силу своей специфики во время работы допускает какой-ли- бо один вид операции: чтение или запись. В связи с этим для работы с текстовыми файлами появляется еще одна процедура открытия файла: Append(var F : text); Эта процедура открывает уже существующий файл и позиционирует указатель обработки на конец файла. После такого открытия текстовый файл можно только дополнять информацией, начиная с конца строки. Ограничения, накладываемые на процедуру Append, такие же, как у процедур Reset и Rewrite. Для обработки текстовых файлов используются процедуры Read и Write, обес- печивающие соответственно чтение и запись одной строки и более в текстовый файл. Использование специальных разделителей строк файла позволило ввести в состав языковых средств процедуры: Readln, обеспечивающую те же действия, что и Read, и дополнительно — чтение до маркера конца строки и переход к новой строке; Writeln, обеспечивающую запись всех величин с обязательной установкой маркера конца строки в файл. Общий вид представления процедур следующий: Readln(var F : text; VI [ ,V2,. . .Vn] ) ; Writeln(var F : text; VI [ ,V2, . . .Vn] ) ; где VI ...Vn переменные разных типов. Возникает вопрос, в каких случаях использовать Read, а когда отдать предпоч- тение Readln? Процедура Read обеспечивает ввод данных общим потоком из од- ной строки, a Readln приводит к обязательному переходу к следующей строке текстового файла, т.е. ввод данных осуществляется из различных строк. Все вы- шесказанное в равной мере относится к операциям записи с помощью процедур Write и Writeln. При организации операций ввода-вывода используются специальные языковые средства в виде функций Eoln, Eof, SeekEoln, SeekEof. Функция Eoln(var F: text) возвращает булевское значение True, если текущая файловая позиция находится на маркере конца строки или вызов Eof(F) возвратил значение True. Во всех других случаях значение функции будет False. Функция Eof(var F: text) возвращает булевское значение True, если указатель конца файла находится сразу за последним компонентом, и False — в противном случае. Пример: Прочитать последовательность длиной шесть символов из первой строки текстового файла EXAMPLE.PAS. var F: text; St: string[6]; begin Assign(F,’EXAMPLE. PAS' ) ; {Файл EXAMPLE.PAS должен существо- вать }
Файлы 265 Reset (F) ; while not Eoln(F) do begin {Проверка конца строки} Read(F, St); Writeln(’St = ’, St); {Вывод на экран} end; Readln(F); {Переход к следующей строке} Close(F); end. Функция SeekEoln(var F: text) возвращает булевское значение True при дости- жении маркера конца строки, причем указатель файла пропускает все пробелы и знаки табуляции, предшествующие маркеру. В противном случае функция возвра- щает значение False. ь Функция SeekEof(var F: text) возвращает значение True, если указатель файла находится на маркере конца файла. Эта функция также пропускает все пробелы и знаки табуляции, предшествующие маркеру, и выполняет автоматический пропуск маркера конца строки. Характерным примером использования этих функций может служить чтение числовых величин из текстового файла, когда необходимо пропус- тить обработку разделяющих эти числа пробелов или знаков табуляции. Использование буфера ввода-вывода. Рассмотрим процедуру обмена инфор- мацией между программой и внешним текстовым файлом на диске. Для передачи данных используется буфер ввода-вывода в виде участка оперативной памяти, че- рез который осуществляются все операции обмена. Для языка Паскаль стандарт- ный буфер ввода-вывода имеет объем 128 байт. Каждому открытому файлу вместе с обработчиком назначается и свой буфер. Возникает задача оптимизации обраще- ний к внешним носителям информации. Например, процедура Writeln записывает все данные последовательно в буфер. Физическая запись на внешнее устройство происходит только тогда, когда инфор- мацией будет занят последний байт буфера. После записи на диск буфер освобо- ждается для приема следующей порции информации. Таким образом достигается компромисс между количеством и длительностью обращений к диску. Если бы ка- ждый вызов процедуры Writeln приводил на практике к физическому обращению к диску, то тратилось бы непозволительно много времени на позиционирование го- ловки чтения-записи для доступа к файлу. Специально введенная процедура Flush(var F: text) дает возможность связать запись с помощью процедур Write и Writeln непосредственно с физической запи- сью на диск. Вызов процедуры гарантирует, что все символы, переданные для за- писи, действительно в это же время будут записаны во внешний файл. Использо- вать процедуру можно для текстовых файлов, открытых только для записи проце- дурами Rewrite и Append. В прикладных программах процедуру используют редко, как правило, в тех случаях, когда необходимо подтверждение о физической запи- у си во внешний файл.
266 Глава 12 На практике обработка текстовых файлов сводится к считыванию всего файла в память, а затем к записи уже модифицированного файла на диск. В таких случаях с целью оптимизации времени обращения к диску целесообразно увеличение объе- ма буфера ввода-вывода. Процедура SetTextBuf(var F : text; var Buf[; size : word]); дает возможность на- значить свой буфер ввода-вывода Buf необходимого объема Size текстовому файлу F. Параметр Size при вызове может быть опущен. В этом случае размер буфера со- ответствует SizeOf(Buf). Назначение SetTextBuf файловой переменной F распро- страняется до следующего использования переменной новым внешним файлом. Пример: Назначение буфера ввода-вывода. var F: text; * Ch: char; Buf: array[l..2048] of char; {Буфер 2 Кбайта} begin Assign(F, ParamStr(1)); {Назначить имя файла из командной строки} SetTextBuf(F, Buf); {Установить файлу буфер 2 Кбайта} Reset(f); while not Eof(F) do begin {Вывести файл на экран} Read(F, Ch); Write(Ch); end; end. Потери информации при обмене будут исключены, если переназначение буфе- ров ввода-вывода будет осуществлено после вызова процедуры Assign либо сра- зу после процедур открытия файлов до любой операции обмена. Упражнение 1. Составим программу, обеспечивающую создание на диске текстового файла и запись в него текста. В разделе описания опишем тип fil: type fil = text. В разделе описания переменных запи- шем описания следующих переменных: F1 — переменная типа fil, которая будет выполнять функции файловой переменной, т. е. представлять текстовый файл в программе; Name — переменная типа string, которая будет принимать значение имени создаваемого файла (для записи до 8 символов имени, точки и 3 символов расширения требуется строка длиной 12 символов); Txt—переменная строкового типа, которая будет принимать значение вводимой с клавиатуры последовательности символов. Блок описания программы будет записан так: type fil “ text; var Fl : fil; Name : string[12];
Файлы 267 xt : string; В начале программы выведем запрос имени файла и считаем его значение в перемен- ную Name, затем, используя стандартную процедуру Assign, поставим в соответствие фай- лу с введенным именем переменную F1 и откроем вновь созданный файл процедурой Rewrite. Этот фрагмент программы запишем таким образом: Write('Введите имя файла для записи текста >’); Readln(Name); Writeln; Assign(Fl,Name); Rewrite(Fl); После открытия файла запишем приглашение на ввод текста для записи в файл. Считы- вание текста по строкам |апишем оператором повтора repeat, завершение ввода текста зада- дим условием until Txt=". Как только вместо строки символов будет введен пробел, т. е. на- жата клавиша Enter, цикл завершается и файл закрывается. Это можно записать следующим образом: Writeln('Вводите текст для записи (для окончания нажмите Enter):'); Writeln; repeat WriteC :>') ; Readln(Txt); Writeln(Fl,Txt); until Txt=''; Close(Fl); После завершения ввода текста в файл выведем на экран сообщение: Ввод окон- чен, нажмите Enter1. Для того чтобы окно исполнения программы (экран пользователя} не закрывалось, запишем процедуру Readln, которая будет ожидать нажатия Enter. Полный текст программы записи текстового файла на диск будет таков: program Write_txt_file; {Запись текстового файла на диск} type fil = text; var Fl : fil; Name : string[12]; Txt : string; begin Write('Введите имя файла для записи текста >'); Readln (Name) ; Writeln; Assign(Fl,Name); Rewrite(Fl);
268 Глава 12 Writeln('Вводите текст для записи (для окончания нажмите Enter):'); Writeln; repeat WriteC’; Readln(Txt) ; Writeln(Fl,Txt); until Txt=•’; Close(Fl); Writeln; Writeln('Ввод окончен, нажмите Enter'); Readln; * end. Запустите интегрированную среду программирования, введите текст программы Write txt file и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его и проверьте действие программы. Упражнение 2. Составим программу, которая обеспечивает выполнение следующих операций над текстовым файлом: считывает текст из текстового файла, добавляет в него текст, переименовывает, копирует и удаляет файл. В разделе описания программы дополнительно к переменным, аналогичным по назва- нию и назначению переменным в предыдущей программе, опишем файловую переменную FI New, которая будет представлять новый текстовый файл в программе; New_Name — пе- ременную типа string, принимающую значение имени нового файла; символьную перемен- ную Ch, которая будет принимать значения в зависимости от выбора пользователем опера- ции над файлами и служить селектором в операторе выбора case. Тело основной программы будет включать в себя записи вызовов процедур чтения текста из файла и добавления текста в конец файла, которые можно записать так: Inp_Text; {Вызов процедуры чтения текста из файла) App_Text; {Вызов процедуры добавления текста) Выбор процедур удаления, переименования или копирования файла запишем с исполь- зованием оператора выбора case: Write('Удалить файл (D), переименовать(R), скопировать(С), Вы- ход (Е) ’) ; Readln(Ch); case Ch of ’D','d’ : Erase_File; •R',’r’ : Rename_File; 'C',’c' : Copy_File; ’E’,’e’ : Halt(l^- end;
Файлы 269 В зависимости от выбора пользователя переменная Ch принимает значения 'D', 'd', *R', 'г', 'С, 'с' или 'Е', 'е', и вызывается та процедура, имя которой записано после соответствую- щей константы выбора. Процедуру чтения текста из файла запишем следующим образом: procedure Read_Text; {Считывание текста из файла} begin {$1—} {Директива компилятору на отключение стандартной про- верки операций ввода-вывода} repeat Write('Введите имя файла для считывания текста >') ; Readln(Name); Writein; Ь Assign(Fl,Name); Reset(Fl); if TOresultOO then begin Writein('Файл не найден...'); Close(Fl); end; Writein; until IOresult=0; {$!+} {Директива компилятору на включение стандартной проверки операций ввода-вывода} Writein('Файл '.Name,* :'); Writein; while not Eof(Fl) do begin Readln(Fl,Txt); Writein(Txt); end; Close(Fl); Writein; Write('Файл считан, нажмите Enter...'); Readln; end; (Конец процедуры} В начале записи данной процедуры указана директива компилятору на отключение стандартной проверки операций ввода-вывода, для того чтобы проверить операцию поиска файла с помощью специальной функции lOresult. Операцию открытия файла запишем в те- ле цикла repeat. Если результат, возвращаемый функцией, равен 0, то операция завершена успешно — файл открыт, и цикл завершается, если иначе, запишем вывод сообщения 'Файл
270 Глава 12 не найден...', закроем файл и повторим запрос на ввод имени файла и т. д. После записи цик- ла директивой {$!+} включим стандартную проверку операций ввода-вывода. Операцию вывода содержимого текстового файла на экран компьютера запишем с применением оператора повтора while. Условием продолжения цикла запишем недости- жение конца файла not Eof(Fl). Процедурой Readln(Fl,Txt) организуем считывание текста из файла по строкам, а процедура Writeln(Txt) будет построчно выводить текст на экран. Как только будет достигнут конец файла, т. е. условие not Eof(Fl) перестанет выполняться, цикл завершится и файл закроется. В завершение процедура выведет на экран сообщение 'Файл считан, нажмите Enter...'. Для добавления текста в конец файла применим стандартную процедуру Append, кото- рая открывает ранее созданный текстовый файл для добавления и позиционирует указатель обработки на конец файла. Ввод текста запишем с использованием оператора повтора repeat, как и в предыдущей программе. Полностью протЬдура добавления текста в конец файла может быть записана таким образом: procedure App_Text; (Добавление текста в конец файла} begin Append(Fl);(Открыть файл для присоединения нового текста} Writeln('Файл ',Name,' открыт для присоединения нового тек- ста ') ; Writeln('Вводите текст для записи (для окончания нажмите Enter) : ') ; Writeln; repeat Write(':>'); Readln(txt); Writeln(fl,txt); until txt=''; Close(Fl); Writeln; Writeln('Присоединение нового текста окончено, нажмите Enter...'); Readln; end; Для удаления файла применим стандартную процедуру Erase, дополнив ее выводом со- общения о результатах удаления. Текст процедуры удаления будет записан таким образом: procedure Erase_File; (Удаление файла} begin Erase(Fl); (Удаление файла} Writeln('Файл ',Name,' удален с диска, нажмите Enter...'); Readln; end;
Файлы 271 Для переименования файла используем стандартную процедуру Rename, дополнив ее вводом нового имени файла и выводом сообщения о результатах процедуры: procedure Rename_File; {Переименование файла} begin Write('Введите новое имя файла: '); Readln(New_Name) ; Rename(Fl,New_Name); {Переименование файла} Writein('Файл ',Name,* переименован в ',New_Name,*, нажмите Enter...'); Readln; end; Для копирования содержимого текстового файла в другой файл процедурой Reset .от- кроем исходный файл для чтения и процедурой Rewrite создадим новый файл-приемник. За- тем с использованием оператора while выполним чтение из файла-источника строки текста Txt и записи этой строки в файл-приемник. Окончание цикла запишем условием Eof(Fl), т. е. операция копирования завершится, как только указатель обработки позиционируется на ко- нец файла-источника. После этого файлы закрываются и выводится сообщение о результа- тах копирования. Текст этой процедуры запишем так: procedure Cppy_File; {Копирование текстового файла} begin Write(* Введите имя файла, в который копируется файл ', Name,' >’) ; Readln(New_Name); Reset(Fl); Assign(Fl_New,New_Name); Rewrite(Fl_New); while not Eof(Fl) do begin Readln(Fl,Txt); Writein(Fl_New,Txt); end; Close(Fl); Close (Fl_New) ; Writein; Write('Файл ',Name,' скопирован в ',New_Name,', нажмите Enter...'); Readln; end; .) Полный текст этой программы будет записан следующим образом: program Read_txt_file; {Пример обработки текстового файла}
272 Глава 12 type fil = text; var Fl, Fl_New : fil; Name, New_Name : string[12]; Txt : string; Ch : char; procedure Read_Text; (Считывание текста из файла} begin {$1—} (Директива компилятору на отключение стандартной про- верки операций ввода-вывода} repeat Write('Введите имя файла для считывания текста >') ; Readln(Name); Writeln; Assign(Fl,Name); Reset(Fl); if ZOresultOO then begin Writeln('Файл не найден...'); Close(Fl); end; Writeln; until IOresult=0; {$!+} (Директива компилятору на включение стандартной проверки операций ввода-вывода} Writeln('Файл ',Name,' :') ; Writeln; while not Eof(Fl) do begin Readln(Fl,Txt); Writeln(Txt); end; Close(Fl); Writeln; Write('Файл считан, нажмите Enter...'); Readln; end; procedure App_Text; {Добавление текста в файл} begin
Файлы 273 Append(Fl); {Открыть файл для присоединения нового текста} Writeln('Файл ’,№une,* открыт для присоединения нового тек- ста *) ; Writeln('Вводите текст для записи (для окончания нажмите Enter) : ') ; Writeln; repeat Write(’:>’) ; Readln(txt); Writeln(fl,txt); until txt®''; Close(Fl); i Writeln; Writeln(* Присоединение нового текста окончено, нажмите Enter...’); Readln; end; / * • procedure Erase_File; {Удаление файла} begin Erase(Fl); {Удаление файла} Writeln('Файл *,Name,* удален с диска, нажмите Enter...*); Readln; end; - procedure Rename_File; {Переименование файла} begin Write('Введите новое имя файла: ’); Readln(New_Name); Rename(Fl,New_Nana); {Переименование файла} Writeln ('Файл ' ,№me, * переименован в * ,№w_Name,', нажмите Enter... *); Readln; end; procedure Copy_File; {Копирование текстового файла} begin Write('Введите имя файла, в который копируется файл ',Name,' >') ; Readln(New_Name); Reset(Fl); Assign(Fl_New,New_Name); Rewrite(Fl_New);
274 I Глава 12 while not Eof(Fl) do begin Readln(Fl,Txt); Writeln(Fl_New,Txt); end; Close(Fl); Close(Fl_New); Writeln; Write('Файл ',Name,* скопирован в ' ,New_Name,', нажмите Enter...'); Readln; end; begin (Начало основной программы) * Inp_Text; App_Text; Write('Удалить файл (D), переименовать(R) , скопировать(С), Вы- ход (E) ') ; Readln(Ch); case Ch of ! 'D','d' : Erase_File; j 'R','r' : Ren^me_File; ( 'C','c' : Copy_File; < •E','e' : Halt(l); | end; j end. Запустите интегрированную среду программирования, введите текст программы Read txt file и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его и проверьте действие программы в пошаговом режиме с заходом в процедуры. Пронаблюдайте изменения значений переменных Ch и Txt, выражения not Eof(Fl). 12.5. ТИПИЗИРОВАННЫЕ ФАЙЛЫ К типизированным файлам относятся файлы строго определенного типа. Чаще : всего это файлы, состоящие из записей. Они применяются для создания различных : баз данных. Стандартное задание в программе такой файловой переменной осуще- : ствляется следующим образом: type FileRec — record end; var F : file of FileRec;
Файлы 275 Если в текстовых файлах содержимое рассматривается как наборы символов, подготовленные специальным образом с учетом общепринятых соглашений о представлении текстовой информации, то в типизированных файлах их содержи- мое рассматривается как последовательность записей определенной структуры. Единицей измерения такого набора данных является сама запись. Длина записи оп- ределяется как SizeOf(FileRec). Так как длина любого компонента типизированного файла строго постоянна, это дает возможность организовать прямой доступ к лю- бому компоненту по его порядковому номеру, поэтому типизированные файлы час- то называют файлами прямого доступа. Seek(var F; NumRec: Longint) Процедура устанавливает текущую файловую переменную F на запись с номе- ром NumRec; F — файЬоАая переменная для типизированных и нетипизированных наборов данных. При открытии типизированного файла текущая позиция для рабо- ты с ним установлена на начало первой записи, которая по принятым соглашениям имеет номер 0, т. е. номер физической записи на единицу меньше номера логиче- ской записи. Это небольшое несоответствие в номерах может служить причиной возникновения ошибок чтения-записи, что в результате может привести к наруше- нию целостности важной информации. Положение усугубляется тем, что неверное позиционирование на запись с помощью процедуры Seek, как правило, не приводит к каким-либо видимым ошибкам ввода-вывода, на которые всегда можно отреаги- ровать. Исключение составляют ситуации, когда нет доступа к файлу, файл не от- крыт или назначено позиционирование на несуществующую запись. Такие ситуа- ции обрабатываются с помощью функции lOresult. Типизированные файлы позволяют организовать работу в режиме чтения-запи- си. Эта возможность играет решающую роль при определении, каким типам фай- лов отдать предпочтение для большинства прикладных задач. Информация в ти- пизированных наборах данных представлена в том же виде, как и в памяти маши- ны во время выполнения программы, поэтому не надо отслеживать управляющие последовательности типа конец строки или возврат каретки. Для работы с файлами прямого доступа дополнительно можно использовать следующие средства: Truncate(var F) Процедура уничтожает все компоненты файла F, начиная с места текущего по- ложения файлового указателя. FilePos(var F) : Longint Функция возвращает для файла F текущую файловую позицию (номер записи, на которую она установлена) в виде значения типа Longint FileSize(var F) : Longint и его размер (количество записей) в виде значения типа Longint.
276 Глава 12 Для пустого файла вызов FileSize возвращает значение 0. Локализация ошибок при обращении к внешним носителям для обоих функций производится через lOresult. Для того чтобы очередная запись могла быть записана в конец типизированно- го файла, необходимо перевести текущую файловую позицию в конец файла. Ко- гда создается новый файл, это происходит автоматически после формирования ка- ждой очередной записи. Если файл уже создан и файловая позиция, установленная по Seek, находится где-нибудь в начале файла (FilePos(F) < FileSize(F)), то в конец файла ее позволяет перевести вызов Seek (F, FileSize (F)) ; В каждом файле число логических и физических записей совпадает, а при по- зиционировании номер физической записи на единицу меньше номера логической записи. * Когда записи располагаются в неотсортированном по фамилиям порядке, по- иск необходимо осуществлять последовательно по всему файлу. Такое расположе- ние записей противоречит здравому смыслу и требует значительных расходов ре- сурсов системы для поиска нужной записи. Любая программная система по мани- пулированию базами данных всегда имеет в своем составе средства упорядочения записей по ключу. Примечание. Ключом называется совокупность знаков, идентифицирующая запись в файле. Ключ сортировки — одно или несколько полей в записи файла, по содержимому ко- торых осуществляется упорядочение его записей, например список учащихся в классном журнале отсортирован по ключу фамилия. На практике это выражается в создании так называемых индексных файлов по отношению к главному файлу данных. Индексные файлы содержат номера записей главного файла, отсортированньрс по конкретному ключу. Такое построение позво- ляет экономить время обращения к внешним носителям, так как во всех перемеще- ниях при сортировке участвуют записи малой длины, содержащие номера записей главного файла в соответствии с ключом сортировки. Упражнение 3. Составим программу, которая создает на диске файл данных "телефон- ного справочника", обеспечивает ввод и изменение данных, поиск номера телефона по фа- милии абонента. В блоке описания программы определим типы: StFio = string[20] — тип фамилия; StPhone = string! 10] — тип номер телефона. Информацию об абонентах будем записывать в файл записями типа ReeBook, в кото- рых будут следующие поля: Fio типа StFio (в нем записывается фамилия абонента) и Phone типа StPhone (записывается номер телефона). Для обращения к типизированному файлу опишем переменную BookFile типа file запи- сей ReeBook, для доступа к полям записи опишем переменную Work типа ReeBook. Пере- менную Vid типа byte введем для выбора пункта меню главной программы. Переменную
Файлы 277 End Menu типа boolean введем для организации повторения показа меню. Переменную Name типа string используем для хранения значения имени файла данных справочника. Подключим стандартный модуль Crt для использования некоторых стандартных проце- дур и функций (например, очистка экрана ClrScr). Текст блока описания программы будет записан следующим образом: uses Crt; type StFio = string[20]; StPhone = string[10]; ReeBook = record {Запись сведений об абоненте} Fio : StFio; {Фамилия} Plfone: StPhone; {Номер телефона} end; var BookFile : file of ReeBook; {Переменная для файла с записями ReeBook} Work : ReeBook; {Переменная для доступа к записям} Vid : byte; End_Menu : boolean; Name : string[12]; Текст главной программы будет записан так: begin {Основная программа} ClrScr; End_Menu:=False; repeat {Повторять показ меню, пока End_Menu=False} Writeln(•*** Телефонный справочник ***•); Writeln(' Выберите вид работы:'); Writeln(* 1 — создание нового файла *); Writeln(' 2 — просмотр списка справочника'); Writeln(* 3 — изменение записи *); Writeln(’ 4 — дополнение справочника'); Writeln(' 5 — поиск абонента *); Writeln(' 0 — завершение работы'); Write(' Ваш выбор :'); Readln(Vid); > case Vid of {Вызов разных процедур в зависимости от вида рабо- ты} 1 : Create_Book_Phone; 2 : QutputAllRec; 3 : UpdateRec;
I 278 Глава 12 i 4 : AddRecToEnd; j 5 : FindFio; ) 0 : End Menu:=True; ! end; j Writein(* Для продолжения нажмите Enter'); j Readln; J ClrScr; ; until End_Menu; {Больше не выводить меню} end. В начале программы очищается экран, переменной EndMenu присваивается значение j False, и затем на него с помощью оператора повтора repeat выводится текст меню из шести ' пунктов. В заключение списка вариантов меню выводится запрос и считывается значение ? переменной Vid, задаваемое пользователем в соответствии с избранным видом работы со J справочником. В зависимости от значения селектора Vid оператор выбора case осуществляет выбор, т. е. выбирают соответствующую значению константы выбора процедуру (например, если s Vid=2, то вызывается процедура OutputAllRec для вывода всего содержимого справочника). 1 После выполнения процедуры управление программой передается в конец оператора case, и, так как значение переменной End Menu не равно True, оператор повтора repeat выполняется j вновь, очищая экран и выводя список меню, и так до тех пор, пока пользователь выберет за- вершение работы. В результате этого переменной Vid будет присвоено значение 0, из-за че- го в операторе case переменной End Menu будет присвоено значение True, и цикл repeat за- вершится. В начале процедуры создания нового файла данных справочника запишем вызов проце- дуры задания имени файла данных NameFile. Процедуру NameFile необходимо будет за- писать выше по тексту программы. Создание нового файла данных произведем с использо- ванием стандартной процедуры Rewrite. Ввод записей об абонентах опишется с использова- нием оператора повтора for, параметр Ind которого, изменяясь от 1 до числа записей Count, будет указывать порядковый номер записи с данными абонента в файле. Добавление одной записи в файл данных сделаем в виде процедуры AddRec, которую также нужно будет запи- сать выше в тексте программы. В окончании процедуры организуем вывод сообщения о результатах создания файла. Для измерения размера файла в записях используем функцию FileSize. Текст данной проце- дуры можно записать так: procedure Create_Book_Phone; {Создание нового файла данных} var Ind, Count : integer; begin Name_File; Assign(BookFile, Name); (Открыть новый файл для записи} Rewrite(BookFile), ; Writein('Создание записей файла ',Name);
Файлы 279 Write('Введите число записей в справочнике'); Readln(Count); for Ind := 1 to Count do AddRec; Writeln(* Создание файла данных телефонного справочника заверше- но') ; Writeln('Файл данных имеет ',FileSize(BookFile),' записи'); Close(BookFile); end; Процедура NameFile, которая вызывается для задания имени файла, запишется сле- дующим образом: procedure Name_Fiie.; {Ввод имени файла данных} begin Write('Введите имя файла данных телефонного справочника >'); Readln(Name); end; Процедура добавления одной записи в файл справочника будет записана таким обра- зом: procedure AddRec; {Добавление записи в файл} begin Writeln(' Ввод записи № ',FilePos(BookFile)+1); {+ 1 — ука- зывает номер физической записи (к номеру логической записи добав- ляется 1) } with Work do begin Write('Введите фамилию: '); Readln(Fio); {Ввод фамилии} Write(* Введите номер телефона :'); Readln(Phone); {Ввод номера телефона} Write(BookFile,Work); {Записать в файл значение переменной Work: фамилию и номер телефона} end; end; Для сокращения записи при обращении к полям Fio Vi Phone переменной типа запись Work используем предложение with Work do. Для вывода на экран всех записей файла сначала, используя функцию lOresult, выпол- ним проверку, есть ли данный файл на диске. Если функция lOresult возвращает значение, отличное от 0, то на экран выводится сообщение о том, что данного файла на диске нет, иначе позиционируем указатель на первую запись Seek(BookFile, 0), а затем, используя оператор повтора while, запишем вызов процедуры вывода на экран одной записи
280 Глава 12 OutputRec. Условием окончания цикла while запишем выражение Eof(BookFile). Как только оно выполнится, цикл завершится. Данные всех записей файла выведены на экран. Процедуру вывода всех записей файла данных справочника запишем в следующем ви- де: procedure OutputAllRec; {Вывод всех записей файла на экран} begin Name_File; Assign(BookFile, Name); {$1—} {Отключить стандартную обработку ошибок} Reset(BookFile); {$!+) {Включить стандартную обработку ошибок} if lOresult = О then * begin Seek(BookFile, О); {Установка на первую запись} Writeln('*** Вывод телефонного справочника из файла ’,Name,' ***'); 'while (not Eof(BookFile)) do OutputRec; end else Writeln(* Файла с именем '+Name+' на диске нет'); ! end; Вывод текущей записи (записи, на которой позиционирован указатель) запишем в виде процедуры OutputRec с использованием предложения with Work do. Для вывода на экран Но- мера записи применим функцию FilePos(BookFile). procedure.OutputRec; {Вывод текущей записи на экран} begin Read(BookFile, Work); with Work do begin Write('Запись № FilePos(BookFile),' '); Writeln('ФИО: ',Fio,' телефон: ',Phone); end; end; Для изменения записи файла сначала, используя функцию lOresult, запишем проверку, есть ли данный файл на диске. Если функция lOresult возвращает значение, отличное от О, то на экран выводится сообщение о том, что данного файла на диске нет, иначе запишем вы- вод запроса о номере изменяемой записи. После считывания номера записи позиционируем указатель на данной записи и, используя процедуру OutputRec, выводим данные этой записи из файла на экран. Задание нового значения полей этой записи запишем вызовом процедуры
Файлы 281 AddRec. В разделе описания переменных опишем локальную переменную NumRec типа Longint, которая будет принимать значение номера изменяемой записи. Текст процедуры из- менения записи файла по указанному номеру таков: procedure UpdateRec; var NumRec : Longint; begin Name_File; Assign(BookFile, Name); {Открыть новый файл для записи} {$1—} Reset(BookFile); {$!+) * if lOresult = 0 then begin Write('Укажите номер изменяемой записи :'); Readln (NumRec) ; Seek(BookFile, NumRec—1); {Установка файловой позиции по указанному номеру записи} Writein('— старое значение записи : '); OutputRec; {Вывод записи и переход на следующую} Seek(BookFile, NumRec—1) ; {Возврат на прежнюю позицию} Writein('Задаем новое значение ',NumRec,' записи'); AddRec; Close(BookFile) ; end else Writein('Файла с именем '+Name+' на диске нет'); end; Для добавления записи в конец файла сначала, используя функцию lOresult, запишем проверку, есть ли данный файл на диске. Если результат функции IOresult=0, то, используя стандартную процедуру Seek(BookFile, FileSize(BookFile)), записываем установку указателя в конец файла и вызов процедуры добавления записи AddRec. Затем запишем вывод сооб- щения о новом размере файла и закрываем файл. Текст данной процедуры можно записать так: procedure AddRecToEnd; begin Name_File; Assign(BookFile, Name); (Открыть новый файл для записи} ' {$!-}
282 Глава 12 Reset(BookFile); {$!+) if lOresult = 0 then begin Seek(BookFile, FileSize(BookFile)); (Установка текущей пози- ции в конец файла} AddRec; Writeln('Измененный файл данных имеет FileSize (BookFile),' записи *); Close(BookFile); end else i Writeln('Файла с именем * +Name+* на диске нет'); end; В процедуре поиска номера телефона опишем локальные переменные: Maska типа StFio, которая будет принимать значение фамилии искомого абонента; Rez Find типа boolean, которая будет принимать значения True или False в зависимости от результатов по- иска; CountRec типа integer, значение которой будет равно числу записей с такой фамилией. После записи запроса имени файла данных справочника и проверки есть ли он на дис- ке, в случае если файл есть, запишем запрос о фамилии искомого абонента. Перед поиском присвоим значения Rez_Find:=False и CountRec:=0. Просмотр всех записей файла данных при поиске запишем, используя оператор повтора while. Условие завершения поиска — Eof(BookFile) — достижение конца файла. Для сокра- щения обращения к именам полей записи Work используем предложение with Work do. Если значение поля Fio очередной записи совпадает со значением переменной Maska, то перемен- ной Rez_Find присвоим значение True (абонент найден), значение переменной CountRec уве- личим на 1 и напечатаем сообщение о фамилии и номере телефона найденного абонента. Текст процедуры FindFio запишется следующим образом: procedure FindFio; {Поиск номера телефона по фамилии абонента} var Maska: StFio; Rez_Find : boolean; CountRec: integer; begin Name_File; Assign(BookFile, Name); {$!-} Reset(BookFile); {$!+) if lOresult = 0 then
Файлы 283 begin Write(* Введите фамилию для поиска: '); Readln (Maska) ; Rez_Find:=False; {Не найден абонент} CountRec:=0; while (not Eof(BookFile)) do {Просмотреть все записи до конца файла} begin Read(BookFile, Work); with Work do if Pos(Maska, Fio) <> 0 then begin t {Найдена запись абонента с указанной фамилией} Rez_Find:=True; Inc(CountRec) ; Writeln('Фамилия: *,Fio,' телефон: ',Phone); end; end; if Rez_Find then Writeln(*Число записей для ',Maska,' = ',CountRec) else Writeln('B справочнике нет абонентов с фамилией ',Maska); Close(BookFile); end else Writeln('Файла с именем '+Name+' на диске нет'); end; Полный текст программы обслуживания телефонных справочников (создание, измене- ние, добавление, поиск номера телефона абонента) будет записан таким образом: program BookPhone; {Телефонный справочник} uses Crt; type StFio = string[20]; StPhone = string[10]; ReeBook = record {Запись сведений об абоненте} Fio : StFio; {Фамилия} Phone: StPhone;{Номер телефона} end; var
284 Глава 12 | BookFile : file of ReeBook; {Переменная для файла с записями । ReeBook} { Work : ReeBook; {Переменная для доступа к записям} Vid : byte; End_Menu : boolean; Name : s tiring [ 12 ] ; • / procedure Name_File; {Ввод имени файла данных} begin Write('Введите имя файла данных телефонного справочника >'); Readln(Name); end; * procedure AddRec; {Добавление записи в файл} begin Writein(' Ввод ваписи № ',FilePos(BookFile)+1); with Work do begin Write('Введите фамилию: '); Readln(Fio); {Ввод фамилии} Write('Введите номер телефона :'); Readln(Phone); {Ввод номера телефона} Write(BookFile,Work); {Записать в файл значение переменной Work: фамилию и номер телефона} end; end; procedure Create_Book_Phone; {Создание нового файла данных} var Ind, Count : integer; begin Name_File; Assign(BookFile, Name); (Открыть новый файл для записи} Rewrite(BookFile); Writein('Создание записей файла ',Name); Write('Введите число записей в справочнике'); Readln(Count); for Ind := 1 to Count do AddRec; Writein('Создание файла данных телефонного справочника заверше- но') ; Writein('Файл данных имеет ',FileSize(BookFile),' записи');
Файлы 285 Close(BookFile); end; procedure OutputRec; {Вывод текущей записи на экран} begin Read(BookFile, Work); With Work do begin Write('Запись № ',FilePos(BookFile), ' : '); Writeln('ФИО: *,Fio,' телефон: ',Phone); end; end; procedure OutputAllRec; {Вывод всех записей файла на экран} begin Name_File; Assign(BookFile, Name); {$!-} {Отключить стандартную обработку ошибок} Reset(BookFile); {$!+} { Включить стандартную обработку ошибок} if lOresult = О then begin Seek(BookFile, 0); {Установка на первую запись} Writeln('*** Вывод телефонного справочника из файла ',Name,' ***') ; while (not Eof(BookFile)) do OutputRec; end else Writeln('Файла с именем '+Name+' на диске нет'); end; procedure UpdateRec; var NumRec : Longlnt; begin Name_File; Assign(BookFile, Name); {Открыть новый файл для записи} {$!-) Reset(BookFile); {$!+} if lOresult = 0 then
286 Глава 12 begin Write('Укажите номер изменяемой записи :'); Readln(NumRec); Seek.(BookFile, NumRec—1); {Установка файловой позиции по указанному номеру записи} Writeln('— старое значение Записи : *); OutputRec; {Вывод записи и переход на следующую} Seek(BookFile, NumRec—1); {Возврат на прежнюю позицию} Writeln('Задаем новое Значение ',NumRec,' Записи'); AddRec; Close(BookFile); end l else Writeln('Файла с именем '+Name+' на диске нет'); end; procedure AddRecToEnd; begin Name_File ; Assign(BookFile, Name); {Открыть новый файл для записи} {$!-) Reset(BookFile); {$!+) if lOresult = 0 then begin ’Seek(BookFile, FileSize(BookFile));{Установка текущей пози- ции в конец файла} AddRec; Writeln('Измененный файл данных имеет ', FileSize(BookFile),' записи'); Close(BookFile); end else Writeln('Файла с именем '+Name+' на диске нет'); end; procedure FindFio; {Поиск номера телефона по фамилии абонента} var BookFile: file of RecBook; Work: ReeBook; ‘ Maska: StFio; Rez_Find : boolean;
Файлы 287 CountRec : integer; begin Name_File; Assign(BookFile, Name); {$!-) Reset(BookFile); {$!+) if lOresult — 0 then begin Write('Введите фамилию для поиска: '); Readln (Maska) ; Rez_Find:=FalSe; CountRec:=0; while (not Eof(BookFile)) do {Просмотреть все записи до конца файла} begin Read(BookFile, Work); with Work do if Pos(Maska, Fio) <> 0 then begin {Найдена запись абонента с указанной фамилией} Rez_Find:=True; Inc(CountRec); Writein('Фамилия: ',Fio,' телефон: ',Phone); end; end; if Rez_Find then Writein('Число Записей для ',Maska,' = ',CountRec) else Writein('В справочнике нет абонентов с фамилией *,Maska); Close(BookFile); end else Writein('Файла с именем '+Name+' на диске нет'); end; begin {Основная программа} ClrScr; End_Menu:=False; repeat {Повторять показ меню, пока End_Menu=False} Writein('*** Телефонный справочник ***');
1 288 Глава 12 Writeln(' Выберите вид работы:'); Writeln(' 1 — создание нового файла'); Writeln(' 2 — просмотр списка справочника') ; Writeln(' 3 — изменение Записи'); Writeln(' 4 — дополнение справочника'); Writeln (' 5 — поиск абонента') ; - Writeln(' О — Завершение работы'); Write(* Ваш выбор :'); Readln(Vid); case Vid of {Вызов разных процедур в зависимости от вида работы} 1 : Create_Book_Phone; k 2 : OutputAllRec; 3 : UpdateRec; 4 : AddRecToEnd; 5 : FindFio; 0 : End_Menu:=True; end; Writeln('Для продолжения нажмите Enter*); Readln; ClrScr; until End_Menu; {Больше не выводить меню} end. Запустите интегрированную среду программирования, введите текст программы BookPhone и запишите файл на диск под соответствующим именем, а затем откомпилируйте его и проверьте действие программы в различных режимах. Приведенный пример наглядно демонстрирует преимущества файлов прямого доступа. Если детализировать структуру записи ReeBook, модернизировать саму программу и доба- вить процедуры для удаления, поиска и сортировки записей, то можно получить программу работы с базой данных. 12.6. НЕТИПИЗИРОВАННЫЕ ФАЙЛЫ Перейдем к рассмотрению файлов, поддержка которых осуществляется с мак- симально возможной скоростью. Введение таких файлов в систему Турбо Паскаль было вызвано стремлением повысить эффективность программ, участвующих в ин- тенсивном обмене с внешними наборами данных. Эти файлы в отличие от рассмот- ренных выше не имеют строго определенного типа. Нетипизированный файл рассматривается в Паскале как совокупность симво- лов или байтов. Представление Char или Byte не играет никакой роли, а важно
Файлы 289 лишь относительно объема занимаемых данных. Такое представление стирает все различия между файлами независимо от типа их объявления. На практике это при- водит к тому, что любой файл, подготовленный как текстовый или типизирован- ный, можно открыть и начать работу с ним, как с нетипизированным набором дан- ных. Для определения в программе нетипизированного файла служит зарезервиро- ванное слово File: var UntypedFile : File; Внутренняя поддержка таких файлов выглядит наиболее близкой к аппаратной поддержке работы с внешними носителями. За счет этого достигается максимально возможная скорость доступа к наборам данных. Для нетипизированных файлов не нужно терять время на преобразование типов и поиск управляющих последователь- ностей, достаточно считать содержимое файла в определенную область памяти. Нетипизированный файл является файлом прямого доступа, что говорит о воз- можности одновременного использования операций чтения и записи. Для таких файлов самым важным параметром служит длина записи в байтах. Открытие нети- пизированного файла с длиной записи в 1 байт можно выполнить следующим обра- зом: Rewrite(UntypedFile, 1) ; ИЛИ Reset(UntypedFile, 1) ; Второй параметр, предназначенный только для использования с нетипизиро- ванными файлами, задает длину записи файла на сеанс работы. Особенность аппаратной поддержки заключается в том, что при обращении к внешнему устройству минимально возможным объемом для считывания являются 128 байт. В стремлении добиться наибольшей эффективности файловых операций в Турбо Паскале принято соглашение, по которому длина записи нетипизированно- го файла по умолчанию составляет 128 байт. Поэтому после открытия файла с по- мощью вызовов Rewrite(UntypedFile) ; или Reset(UntypedFile); все процедуры и-функции, обслуживающие файлы прямого доступа, работают с за- писями длиной 128 байт. Каждый пользователь для своих программ .может выбрать наиболее подходящий размер записи. Используя для базовых операций ввода-вывода с нетипизированными файлами стандартные процедуры Read и Write, нельзя добиться большой эффективности в скорости передачи данных. Поэтому только для данного типа файлов в Турбо Пас- каль введены две новые процедуры, поддерживающие операции ввода-вывода с бо- лее высокой скоростью. 10—116
290 Глава 12 BlockRead (var F : file ; var Buf; Count : word {;Result:word}); Процедура считывает из файла F определенное число блоков в память, начиная с первого байта переменной Buf. Параметр Buf представляет любую переменную, используемую для накопления информации из файла F. Параметр Count задает число считываемых блоков. Параметр Result является необязательным и содержит после вызова процедуры число действительно считанных записей. Использование параметра Result подсказывает, что число считанных блоков может быть меньше, чем задано параметром Count. Если Result указан при вызове, то ошибки ввода-вывода в такой ситуации не произойдет. Для отслеживания этой и других ошибок чтения можно использовать опции {$1—}, {$!+} и функцию lOresult. BlockWrite(var F : file ; var Buf; Coun£:word {;Result:word}); Процедура предназначена для быстрой передачи в файл F определенного числа записей из переменной Buf. Все параметры процедуры BlockWrite аналогичны па- раметрам процедуры BlockRead. Обе процедуры выполняют операции ввода-выво- да блоками. Объем блока в байтах определяется по формуле: Обт>ем == Count * RecSize, где RecSize — размер записи файла, заданный при его открытии. Суммарный объ- ем разового обмена не должен превышать 64 Кбайт. Помимо скорости передачи данных преимущество этих процедур заключается в возможности пользователя са- мостоятельно определять размер буфера для файловых операций. Эта возможность играет значительную роль в тех задачах, где необходимо же- сткое планирование ресурсов. Упражнение 4. Составим программу, которая создает нетипизированный файл из 100 вещественных чисел и выводит на экран k-й элемент файла. Проиллюстрируем обработку созданного файла двумя разными способами: поиск элемента в файле данных прямого дос- тупа по его номеру и поиск элемента в файле данных с последовательным доступом. В разделе описания переменных опишем файловую переменную F, представляющую в нашей программе нетипизированный файл вещественных чисел; вещественную переменную Р, которой будет присваиваться значение очередного элемента файла при заполнении файла случайными вещественными числами и искомого элемента файла; целую переменную типа byte К, значения которой будут указывать на номер элемента в файле. Раздел описания запишется так: var F : file of real; P : real; К : byte; Создание файла вещественных чисел запишем следующей процедурой: procedure Mak_file; begin Assign(F,’a.dat'); Rewrite(F); {Открыть файл для записи}
Файлы 291 Randomize; for K:=l to 100 do begin P:=Random(100); Write(F,P); {Записать в файл значение К-го элемента} end; Writeln('Создание файла вещественных чисел завершено'); Close(F); {Закрыть файл} end; Поиск элемента в файле прямого доступа по его номеру запишем следующим образом: procedure Find_Elem; {Поиск элемента в файле прямого доступа по его номеру} * begin Assign(F,'a.dat'); Write('Введите номер нужного элемента '); Readln(К); Reset(F); {Открыть файл для чтения} Seek(F,K—1); {Переместить указатель обработки на К—1-й эле- мент } Read(F,P); {Присвоить значение элемента, на который указы- вает указатель обработки переменной Р} Writeln(К,'-й элемент файла',Р:6:2); Close(F); {Закрыть файл} end; Примечание. При позиционировании номер физической записи на единицу меньше но- мера логической записи, поэтому адрес элемента К—1. Поиск элемента в файле с последовательным доступом запишем следующим образом: procedure Find_Fil_P; {Поиск элемента в файле с последовательным доступом} var N : byte; begin Assign(F,'a.dat*) ; Write('Введите номер нужного элемента '); Readln(К); Reset(F); N:=0; {Указатель обработки в начало файла} While not Eof(F) do {Повторять, пока не просмотрим весь файл} begin
292 Глава 12 Read(F,P); {Чтение элемента и смешение указателя обработки вправо на один элемент} if N=K—1 then {Нашли элемент с искомым номером} begin Writeln(К,'-й элемент файла равен ',Р:6:2); Exit; {Прервать поиск, так как элемент найден} end; N:=N+1; {Увеличить счетчик числа элементов файла на 1} end; Close(F); end; й Основной блок программы будет содержать в себе вызовы вышеописанных процедур, а вся программа будет записываться следующим образом: program dem_file; uses Crt; var F : file of real; P : real; К : byte; procedure Mak_file-; {Создание файла вещественных чисел} begin Assign(F,'a.dat'); Rewrite(F); Randomize; for K:«l to 100 do begin P^Random (100) ; Write(F,P); end; Writeln('Создание файла вещественных чисел завершено'); Close(F); end; procedure Find_Elem; {Поиск элемента в файле прямого доступа по его номеру} begin Assign(F,'a.dat'); Write('Введите номер нужного элемента '); Readln(К); Reset(F); Seek(F,К-1);
Файлы 293 Read(F,P); Writein(К,'-й элемент файла’,Р:6:2); Close(F); end; procedure Find_Fil_P; {Поиск элемента в файле последовательного доступа} var N : byte; begin Assign(F,'a.dat'); Write('Введите номер нужного элемента '); Readln(К); Reset(F); * N:=0; {Поместить указатель обработки в начало файла} While not Eof(F) do {Повторять, пока не просмотрим весь файл} begin Read(F,P); {Чтение элемента и смещение указателя обработки вправо на один элемент} if N=K—1 then {Нашли элемент с искомым номером} begin Writein(К,'-й элемент файла равен ',Р:6:2); Exit; {Прервать поиск, так как элемент найден} end; N:=N+1; {Увеличить числа элементов файла на 1} end; " Close(F); end; begin {Основная программа} Mak_file; {Вызов процедуры создания файла вещественных чисел} kind_Elem; {Вызов процедуры поиска элемента в файле прямого дос- тупа} Find_Fil_P; {Вызов процедуры поиска элемента в файле с последо- вательным доступом} end. Запустите интегрированную среду программирования, введите текст программы dem file и запишите файл на диск под соответствующим именем, а затем откомпилируйте его и проверьте действие программы. Обратите внимание на то, что при обработке файла a.dat как файла прямого доступа для чтения определенного элемента выполняется позиционирование указателя обработки на указанный элемент, а при обработке файла a.dat как файла последовательного доступа пере- мещение указателя на нужный элемент осуществляется последовательным чтением эле-
294 Глава 12 ментов, начиная с первого, до тех пор, пока указатель обработки не будет установлен на ис- комый элемент. Упражнение 5. Составим программу, которая создает массив целых чисел и записыва- ет (считывает) его в файл разными способами (с использованием процедур Write или BlockWrite, Read или BlockRead), а также вычисляет значение среднего арифметического всех элементов, записанных в файл. В разделе описания программы запишем константу Count=30000, значение которой бу- дет определять количество элементов массива в файле 'Prob.dat', имя которого также описа- но в разделе констант. Массив Count чисел типа byte опишем так: М : array[1..Count] of byte. Сумму всех элементов массива обозначим переменной Sum типа Longint. Переменную I типа integer будем использовать для указания индекса элемента массива. Целочисленная пе- ременная Vybor будет принимать значения 1 или 2 в зависимости от выбора способа запи- си(чтения) файла. Переменные h, mi, s, hund типа Word будут принимать значения: ч, мин, с, 0.01 с, полученные от стандартной функции GetTime из модуля Dos. В начале основной программы запишем создание массива М из Count случайных целых чисел. Так как нам необходимо сравнить эффективность использования процедур чтения(запи- си) Write или BlockWrite, Read или BlockRead, то один и тот же массив запишем и извлечем из файла разными способами. Для этого в основной программе организуем выбор типа про- цедур 1 — Write; 2 — BlockWrite. Затем с использованием оператора case в зависимости от значения переменной Vybor запишем вызов процедур обращения к файлу Write File или BlockWriteFile. В каждой процедуре считаем время начала процесса создания массива и за- пись в файл и время окончания просмотра всех значений в файле и расчет среднего арифме- тического элементов. Для считывания времени запишем вызов стандартной процедуры GetTime(h,mi,s,hund). При выводе сообщения о текущем времени используем вызов проце- дуры LeadingZero, преобразующей время в строку типа чч:мин:сек:доли сек. После оператора case (после окончания выполнения процедур) запишем вывод на экран значения среднего арифметического элементов файла. Текст основной программы будет записан так: begin {Основная программа} Randomize; for I:=l to Count do M[I]:=Random(200) ; Writeln('Укажите.процедуру копирования в файл данных:'); Write('1 — Write; 2 — BlockWrite : '); Readln(Vybor); case Vybor of {Использование разных процедур записи(чтения)} 1 : Write_File; 2 : BlockWrite_File; end;
Файлы 295 Writeln('Расчет окончен. Среднее Значение элементов массива =',Sum/Count : 8:4); {Вывод вещественного числа с форматом 8:4} Readln; end. В разделе описания переменных процедуры записи(считывания) массива чисел в файл с использованием стандартных процедур Write и Read запишем файловую переменную Fil ти- па byte. В начале процедуры запишем обращение к стандартной процедуре считывания систем- ного времени GetTime, затем выведем сообщение о начале процесса. После этого запишем Assign и Rewrite для открытия нового файла данных. Запись элементов массива в файл опи- шем с помощью оператора повтора for, в теле которого используется процедура Write. По окончании записи всех элементов массива в файл выведем сообщение о завершении созда- ния файла и начале считывания данных из него. Для считывания данных открываем файл для чтения и, также используя оператор повтора for с процедурой Read, считываем все эле- менты, выполняя их суммирование. Как только все элементы массива будут считаны из файла, закрываем файл, считываем текущее системное время и выводим его значение на экран для анализа временных затрат на процесс записи(считывания) в файл. После этого управление передается основной программе, в которой рассчитывается среднее арифмети- ческое всех элементов и печатается результат. Текст процедуры записи(считывания) массива чисел в файл с использованием стан- дартных процедур Write и Read запишется таким образом: procedure Write_File; var Fil : file of byte; begin GetTime(h,mi,s,hund) ; Writeln('Начало процесса : ',LeadingZero(h),':', LeadingZero(mi),':’, LeadingZero(s)); Assign(Fil,FileName); Rewrite(Fil); for I:=l to Count do Write(Fil,M[I]); Close(Fil); Writeln('Файл создан. Извлекаем данные'); Reset(Fil); Sum:=0; for I:=l to Count do begin Read(Fil ,M[I]); Sum:=Sum+M[I]; end; Close(Fil);
296 Глава 12 GetTime (h, mi, s, hund) ; . Writein ('Конец процесса : ' ,LeadingZero(h) , *: *, LeadingZero(mi),':*, LeadingZero(s)); end; Текст процедуры BlockWrite_File, в которой создается нетипизированный файл для раз- мещения элементов такого же массива с использованием для записи(считывания) стандарт- ных процедур BlockWrite и BlockRead почти такой же, но отличается следующими элемен- тами: 1) в разделе описания файловая переменная Fil объявлена как переменная типа file; 2) запись в файл выполняется процедурой BlockWrite; 3) чтение из файла выполняется процедурой BlockRead. Далее приводится полный текст данной программы. * program demo_not_typ_file; uses Dos, Crt; const Count—30000; FileName='Prob.dat'; var M : array[1..Count] of byte; Sum : Longint; I : integer; Vybor : 1. . 2 ; h, mi, s, hund : Word; {Переменные ч, мин, с, 0.01 с для GetTime} function LeadingZero(w : Word) : String; {Преобразовать время в строку "00:00:00" с выравниванием дополнением нулями слева} var s : String; begin Str(w:0,s); if Length(s) = 1 then , s := '0* + s; LeadingZero := s; end; procedure Write_File; {Создание файла типа byte с использованием Write и Read} var Fil : file of byte; begin GetTime(h,mi,s,hund); Writein(* Начало процесса : ',LeadingZero(h),':', LeadingZero(mi),*:', LeadingZero(s));
Файлы 297 Assign(Fil,FileName) ; Rewrite(Fil) ; for I:=l to Count do Write(Fil ,M[I]); Close(Fil); Writeln('Файл создан. Извлекаем данные'); Reset(Fil); Sum:=O; for I:=1 to Count do begin Read(Fil,M[I]); Sum: =Sum+M [ I ] * end; Close(Fil); GetTime(h,mi,s,hund); Writeln ( 'Конец процесса : ', LeadingZero (h)',': ', LeadingZero(mi),':', LeadingZero(s)); end; {Создание нетипизированного файла с использованием BlockWrite и BlockRead} procedure BlockWrite_File; var Fil : file; begin GetTime(h,mi,s,hund); Writeln('Начало процесса : ',LeadingZero(h),':', LeadingZero(mi), ':', LeadingZero(s)) ; Assign(Fil,FileName); Rewrite(Fil); BlockWrite(Fil, M, 100); {Запись из массива M в файл} Close(Fil); Writeln('Файл создан. Извлекаем данные'); Reset(Fil); Sum:=0; BlockRead(Fil, M, 100); Close(Fil); for I:=l to Count do Sum:— Sum+M[I] ; GetTime(h,mi,s,hund) ; Writeln('Конец процесса : ',LeadingZero(h),':', LeadingZero(mi), ':' , LeadingZero(s));
298 Глава 12 end; begin {Основная программа} Randomize; for I:= 1 -to Count do M[I]:=Random(200) ; Writeln('Укажите процедуру копирования в файл данных:'); Write(* 1 — Write; 2 — BlockWrite : ') ; Readln(Vybor); case Vybor of {Выбор разных процедур записи/чтения} 1 : Write_File; 2 : BlockWrite_File; В end; Writeln(* Расчет окончен. Среднее Значение элементов массива =', Sum/Count:8:4); Readln; end. Запустите интегрированную среду программирования, введите текст программы demo_not_typ_file и запишите файл на диск под соответствующим именем, а затем отком- пилируйте его и проверьте действие программы, выбирая разные процедуры записи(считы- вания) и сравнивая время, затрачиваемое программой на обработку файла. Упражнение 6. Рассмотрим программу, которая создает на диске файл массива целых чисел, а затем делит его на части, каждую часть записывая отдельным набором данных на диск. Необходимость в такой программе возникает при перенесении больших наборов дан- ных с дисков большой емкости на носители малой емкости во время транспортировок или поставок. Для использования некоторых стандартных функций к программе подключим модуль Crt. В блоке описания констант задано значение Count — размер массива целых чисел. Для ускорения ввода-вывода используется буфер — Buffer (переменная, используемая для нако- пления информации), размер которого задан константой SizeBuffer. Тип буфера описан как array[l..SizeBuffer] of Byte. Переменные W1,W2,W3 используются для измерения объема информации в файле (единицей измерения служит 1 буфер). Переменная KolZap будет принимать значения коли- чества записей остатка делимого файла, Siz — размер файла-приемника (выходного файла), куда копируется часть делимого файла. Строковые переменные NameFile и NamePart будут принимать значения имен главного и выходного файла, файловые переменные FileMain и FilePart будут представлять в программе нетипизированные файлы (исходный и выходной), логическая переменная Flag будет принимать значения в зависимости от результата диалога о продолжении работы. Блок описания можно будет записать таким образом: uses Crt; const Count—15000; {Размер массива целых чисел}
Файлы 299 SizeBuffer = 25600; {Размер буфера для ввода-вывода} type BufFile = array[1..SizeBuffer] of Byte; var Buffer : BufFile; {Буфер} Wl, W2, W3 : word; KolZap, Siz : Longint; NameFile, NamePart : string[60]; {Имя основного файла и выход- ного} FileMain, FilePart : file ; {Переменные под нетипизированные файлы} Flag : booleary {Флаг для логических операций} Процедуру принятия решения о продолжении работы запишем в виде функции, которая возвращает значение True или False в зависимости от выбора пользователя. Запишем эту функцию следующим образом: function Answer : boolean; var Ch : char; begin Write('Продолжать работу? (Д/Н)'); repeat Readln(Ch) ; case Ch of •Y’,’y','Д',’д’: Answer := true; 'N'j'n’j'H'^H': Answer := false; end; until CH in [’Y’,*y*,'N*,'n','Д','д',’H’,’н']; end; Процедуру создания нетипизированного файла случайных целых чисел запишем анало- гично описанию в предыдущем упражнении. В начале основной программы запишем вызов процедуры создания исходного файла, затем выведем сообщение 'Программа разбивает исходный файл на части', установим соот- ветствие между файловой переменной FileMain и файлом, имя которого задано в перемен- ной NameFile, затем откроем его для чтения и установим количество записей в нем (в бай- тах). Деление исходного файла запишем с использованием оператора повтора while, повто- рение которого запишем условием KolZap > 0, т. е. повторять до коДца исходного файла. В начале каждого повторения выводим сообщение о размере оставшейся части исходного файла и запрос о продолжении деления, для чего в начале тела цикла запишем: Write(* Введите имя выходного файла: ’); Readln(NamePart);
300 Глава 12 Assign (FilePart, NamePart) ; Rewrite(FilePart, 1); - {Создать выходной файл для части основного файла с длиной записи 1} Если пользователь выбирает вариант, чтобы продолжать деление, то переменной Flag присваивается результат функции Answer — True, и выполняется составной оператор, за- писанный после слова then в операторе условия if Flag. При этом запрашивается размер вы- ходного файла, его размер устанавливается не больше числа оставшихся записей в исход- ном файле. Если пользователь выбирает вариант не продолжать деление исходного файла, то пере- менной Flag присваивается результат функции Answer — False, и выполняется составной оператор, записанный после слова else в операторе условия if Flag: размер части задается равным размеру остатка основного файла, после чего выполняется копирование порциями, размер которых равен объему буфера. Используем для быстрой передачи информации из ос- новного файла в файл-приемник процедуры BlockRead и BlockWrite. Для наглядного изо- бражения завершения копирования одной порции, равной объему буфера, используем опе- ратор Write('+'). После этого файл-приемник закрывается, определяется размер остатка основного фай- ла и управление передается в заголовок цикла для проверки условия KolZap > 0, и так до тех пор, пока это условие выполняется. Как только оно перестанет выполняться, управление из цикла передается на оператор закрытия файла, после чего программа завершает работу. Полный текст программы, создающей на диске файл массива целых чисел а затем разделяющей его на части, каждую часть записывая отдельным набором данных на диск, можно будет записать следующим образом: program Div_File; {Создание файла и деление его на части} uses Crt; const Count=15000; {Размер массива целых чисел} SizeBuffer = 25600; {Размер буфера для ввода-вывода} type BufFile = array[1..SizeBuffer] of Byte; var Buffer : BufFile; {Буфер} Wl, W2, W3 : word; KolZap, Siz : Longlnt; NameFile, NamePart : string[.60] ; {Имя основного файла и выход- ного} FileMain, FilePart : file ; {Переменные под нетипизированные файлы) Flag : boolean; {Флаг для логических операций} function Answer : boolean; var Ch : char;
Райлы 301 begin Write('Продолжать работу? (Д/Н)'); repeat Readln(Ch) ; case Ch of 'У',’у’,'Д’,'д': Answer := true; 'N','n','H','h': Answer := false; end; until CH in [’Y','y',’N’,'n','Д',’д’,’H’,’и’]; end; procedure Create_File; {Создание файла-массива случайных целых лисел} t var М : array[1..Count] of byte; I : integer; segin Write('Введите имя создаваемого файла: '); Readln(NameFile); Randomize; for I:=l to Count do M[I]Random(200) ; Assign(FileMain,NameFile); Rewrite(FileMain); {Открыть новый файл} BlockWrite(FileMain, M, 100) ; {Записать в файл} Close(FileMain); Writeln('Создание файла окончено, нажмите Enter...'); Readln; end; begin {Начало основной программы} Create_File; Writeln('Программа разбивает исходный файл на части'); Assign(FileMain, NameFile); Reset(FileMain, 1);{Открыть основной файл, длина записи 1} KolZap := FileSize(FileMain); {Количество записей=размер файла в байтах} while KolZap > 0 do {Повторять до конца основного файла} begin Writeln('Часть файла *,NameFile,' = ',KolZap); Write('Деление файла, ');
302 Глава 12 Flag := Answer; {Вызов функции диалога с пользователем) Writein; Write('Введите имя выходного файла: '); Readln(NamePart); Assign(FilePart, NamePart); Rewrite(FilePart, 1);{Создать выходной файл для части ос- новного файла с длиной записи 1} if Flag then begin Write('Введите размер выходного файла: '); Readln(Siz); if Siz > KolZap then {Размер выходного файла должен быть не больше числа оставшихся записей в основном файле) Siz:= KolZap; end else Siz:= KolZap;{Иначе размер части равен размеру остатка основного файла) {Вводится коррекция на размер буфера ввода-вывода } Wl := Siz div SizeBuffer; {Количество полных Buffer) W2 :— Siz mod SizeBuffer; {Остаток} Write(’Копирование '); for W3 := 1 to Wl do {Копировать порциями=целым буферам) begin BlockRead(FileMain, Buffer, SizeBuffer); BlockWrite(FilePart, Buffer, SizeBuffer); Write('+'); {Отображать на экране завершение копирова- ния 1 буфера} end; if W2 О О then begin BlockRead(FileMain, Buffer, W2); BlockWrite(FilePart, Buffer, W2); Write('+'); end; {Конец оператора if W2 <> 0} Writein; Close(FilePart); {Закрыть файл-приемник) KolZap := KolZap — Siz; {Расчет остатка основного файла) end; {Конец оператора while KolZap > 0} Close(FileMain); end.
Файлы 303 Запустите интегрированную среду программирования, введите текст программы Div File и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. Проверьте действие программы в пошаговом режиме без захода в процедуры и наблю- дая текущие значения переменных: Flag, SizeBuffer, KolZap, Wl, W2, W3, Buffer. Если применить расширенный вызов BlockRead и BlockWrite с 4 параметрами, то мож- но проконтролировать внутри программы количество передаваемой информации. 12.7. НЕКОТОРЫЕ СТАНДАРТНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ ОБРАБОТКИ ФАЙЛОВ МОДУЛЯ DOS Ряд очень полезньрс стандартных процедур обработки файлов содержатся в модуле DOS. Некоторые из них мы уже использовали ранее. В модуле DOS опреде- лены следующие константы и переменные. 12.7.1. Константы Константы флагов. Следующие константы используются для проверки от- дельных битов флага в регистре Flags после вызова Intr или MSDOS: Константа Значение Константа Значение FCarry $0001 FZero $0040 FParity $0004 FSign $0080 FAuxiliary $0010 FOverFlow $0800 Например, если R — запись типа регистр, то тест R.Flags and FCarry О 0 R.Flags and FZero = 0 равен True соответственно, если Carry флаг установлен и если Zero флаг сброшен. Константы режима файла. Эти константы используются процедурами обра- ботки файлов, при открытии и закрытии дисковых файлов. Поля режимов файло- вых переменных Турбо Паскаля будут содержать одно из указанных ниже значе- ний: Константа fmClosed fmlnput fmOutput fmlnOut Значение $D7B0 $D7B1 $D7B2 $D7B3
304 Глава 12 Константы атрибутов файла. Эти константы используются для проверки, ус- тановки и очистки битов файловых атрибутов в процедурах GetFAttr, SetFAttr, FindFirst, FindNext: Константа Значение Константа Значение Readonly $01 Directoiy $10 Hidden $02 Archive $20 SysFile $04 AnyFile $3F VolumelD $08 Эти константы можно суммировать. Например, оператор FindFirst ReadOnly+Directory,S); будет искать файлы "только для чтения" и подкаталоги в текущем каталоге. Константа AnyFile — это просто сумма всех атрибутов. * 12.7.2. Типы файловых записей Определения записей, используемых внутренне Турбо Паскалем, также опре-. делены в модуле Dos. FileRec используется для типизированных и нетипизирован- ных файлов, a TextRec — внутренний формат файловой переменной типа Text. type {Типизированные и нетипизированные файлы} FileRec = record Handle : Word; Mode : Word; RecSize : Word; Private : array [1..6] of Byte; UserData : array [1..16] of Byte; Name : array [0..79] of Char; end; {Тип записи для текстовых файлов} TextBuf = array [О..127] of Char; TextRec — record Handle : Word; Mode : Word; BufSize : Word; Private : Word; BufPos : Word; BufEnd : Word; BufPtr : ЛTextBuf; OpenFunc : Pointer; InOutFunc : Pointer; FlushFunc : Pointer; CloseFunc : Pointer; UserData : array [1..16] of Byte;
Файлы 305 Name : array [0..79] of Char; Buffer : TextBuf; end; Примечание. Запись BufPtr : ^TextBuf; следует понимать: BufPtr — указатель на TextBuf; OpenFunc : Pointer; — OpenFunc — указатель стандартного типа Pointer. Подроб- нее с указателями вы познакомитесь в гл. 13. Тип Registers. Переменные типа Registers используются процедурами Intr и MS DOS для указания содержимого входного значения регистров и проверки выходного содержимого регистров процессора для программного прерывания. type Registers — record case Integer of 0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word); 1: (AL, AH, BL, BH, CL, CH, DL, DH: Byte); end; Заметим, что можно пользоваться одновременно 8-и и 16-битовыми регистра- ми. Тип DataTime. Переменные типа DataTime используются в сочетании с про- цедурами UnpacTime и PackTime для проверки и создания 4-байтовых упакованных значений даты и времени в процедурах GetFTime, SetFTime, FindFirst и FindNext: type DataTime “ record Year, Month, Day, Hour, Min, Sec: Integer; end; Диапазон допустимых значений: Year 1980..2099, Month 1..12, Day 1..31, Hour 0..23, Min 0..59, Sec 0..59. Тип SearchRec. Переменная типа SearchRec используется процедурами FindFirst и FindNext для просмотра справочников: type SearchRec — record Fill: array[l..21] of Byte; Attr: Byte; Time, Size: Longint; Name: String[12]; end; Информация, найденная для каждого файла одной из этих процедур, возвра- щается в SearchRec. Поле Attr содержит атрибуты файла (сформировано из констант атрибутов), Time содержит упакованные время и дату (используйте UnpackTime для распаковки), Size содержит размер файла в байтах и Name содер- жит имя файла. Поле Fill резервируется операционной системой и никогда не должно модифицироваться.
306 Глава 12 Строковые типы обработки файлов. Эти строковые типы используются в процедуре FSplit: DirStr » String[67]; {Строка устройства, и справочника} NameStr » String[8]; {Строка имени файла} ExtStr » String[4]; {Строка расширения файла} ComStr = String[127]; {Командная строка} PathStr = String[79]; {Полная строка пути файла} 12.7.3. Переменные Переменная DosError. Переменная DosError используется многими програм- мами в модуле Dos для указания ошибок. * var DosError : Integer; Значение, запомненное в DosError, представляет собой код ошибки операцион- ной системы. Значение 0 означает "нет ошибки", другие коды означают: Код ошибки DOS 2 3 5 6 8 10 11 18 Значение Файл не найден Путь не найден Доступ запрещен Неверный обработчик Нет памяти Неправильная среда Неправильный формат Больше нет файлов 12.7.4. Процедуры и функции Процедуры даты и времени Процедура GetDate GetFTime GetTime PackTime Описание Возвращает текущую дату, установленную в DOS Возвращает дату и время последней записи в файл Возвращает текущее время, установленное в DOS Преобразует запись в 4-байтовое упакованное значение даты и времени типа longint, используемое процедурой SetFTime. Поля записи DateTime не проверяются на диапа- ЗОН SetData SetFTime SetTime UnpackTime Устанавливает текущую дату в DOS Устанавливает время и дату последней записи в файл Устанавливает текущее время в DOS Преобразует 4-х байтовое упакованное значение даты н времени, возвращаемое GetFTime, FindFirst или FindNext в распакованную запись типа DateTime
Файлы 307 Функция статуса диска Функция Описание DiskFree Возвращает число свободных байтов на указанном диске DiskSize Возвращает полный объем указанного диска в байтах Процедуры обслуживания прерываний Процедура Описание GetlntVec Возвращает адрес, хранящийся в указанном векторе прерывания Intr Выполняет указанное программное прерывание MSDos Выполняет функцию операционной системы SetlntVec Устанавливает адрес для указанного вектора прерывания Процедуры обработки файлов Процедура Описание FindFirst Ищет в указанном или текущем справочнике первый файл, соответствующий задан- ному имени файла и набору атрибутов FindNext Возвращает следующий файл, соответствующий имени н атрибутам, указанным в предыдущем вызове FindFrst • GetFAttr Возвращает атрибуты файла SetFAttr Устанавливает атрибуты файла FSplit Разбивает имя файла на три составные части (директорий, имя файла, расширение) Функции обработки файла * Функция Описание FExpand Берет имя файла и возвращает полное имя файла (устройство, директорий, имя и рас- ширение) FSearch Ищет файл в списке директориев Процедуры обработки процессов Процедура Описание Ехес Выполняет заданную программу с указанной командной строкой Keep Завершает программу и оставляет ее в памяти (реализует прерывание "завершить и оставить резидентным"- TSR) Swap Vectors Меняет сохраненные векторы прерываний с текущими векторами Функции обработки процессов Функция Описание DosExitCode : Возвращает код завершения подпроцесса Функции управления средой Функция Описание EnvCount Возвращает число строк, содержащихся в среде DOS EnvStr Возвращает указанную строку среды GetEnv Возвращает значение указанной переменной среды
308 Глава 12 Дополнительные функции Функция Описание DosVersion Возвращает номер версии DOS Дополнительные процедуры Процедура Описание GetCBreak Возвращает состояние проверки Ctrl+Break в DOS SetCBreak Устанавливает состояние проверки Ctrl+Break в DOS GetVerify Возвращает состояние флага верификации в DOS SetVerify Устанавливает состояние флага верификации в DOS Упражнение 7. Составить программу, которая обеспечивает сканирование диска, опре- деление его размера, размера свободного места на диске, вь|вод списка файлов по заданно- му шаблону, поиск указанного файла и переименование его. Для использования стандартной функции очистки экрана к программе подключим мо- дуль Crt, а для использования стандартных процедур обработки файлов — модуль Dos. В разделе описания переменных запишем файловую переменную Fil, представляющую в про- грамме файл данных типа byte, переменную Fileinfo типа SearchRec, в которой стандартны- ми процедурами FindFirst и FindNext при просмотре каталогов записывается следующая ин- формация: имя и расширение файла, размер в байтах, упакованные дата и время создания, атрибут. Целая переменная Ftime будет содержать упакованные время и дату создания файла, считанные процедурой GetFTime. В переменной Т типа DateTime будем хранить распако- ванные время и дату. Для отображения распакованного значения времени создания файла введем целые переменные Н, М, S, Hund, значения которых будут содержать соответствен- но ч, мин, с, сотые доли секунд. Строковые переменные Dir s, N, Е, Name, New_Name ис- пользуем для хранения значений каталога, имени, расширения файла, старого и нового име- ни файла при переименовании. Целая переменная I будет принимать значения количества найденных в каталоге файлов, а Р — указывать позицию разделяющую имя и расшире- ние файла. Для преобразования распакованного значения времени в строку текста используем функцию LeadingZero, которая, получая из основной программы целочисленное значение (ч, мин, с), преобразует его в строковый тип, при длине полученной строки 1 приклеивает к ней впереди "0" и возвращает результат основной программе. Текст подпрограммы-функции LeadingZero будет таким: function LeadingZero(W : Word) : string; var S : string; begin Str(W:0,S); if Length(S) — 1 then S := ’O’ + S; LeadingZero :— S; end;
Файлы 309 Для отделения имени файла от расширения используем процедуру NamExtFile. В этой процедуре стандартной функцией Pos определяется позиция точки, отделяющей имя от расширения, а затем копируется значение части считанного из каталога имени файла — зна- чение поля Filelnfo.Name от 1-го символа до позиции точки в переменную N — имя файла, а значение части имени файла от позиции, следующей за позицией точки до конца имени, — в переменную Е — расширение файла. Если точки в строке Filelnfo.Name нет, то все значе- ние Filelnfo.Name присваивается переменной N. Текст этой процедуры записывается сле- дующим образом: procedure Nam_Ext_File; {Отделить имя и расширение файла} begin > Р := Pos(*.',Fileinfo.Name); {Определить позицию в имени файла} if Р > 1 then » begin N := Copy(Filelnfo.Name, 1, P — 1); E := Copy(Filelnfo.Name, P 4- 1, 3) ; end else {Расширение ртсутствует} begin N := Fileinfo.Name; E : = ' ' ; end; end; Процедуру переименования указанного файла запишем, используя стандартную проце- дуру Rename, дополнив ее запросом и вводом нового имени переименовываемого файла и выводом на экран сообщения о результатах переименования. В начале основной программы очистим экран компьютера, стандартной функцией GetTime считаем системное время и выведем сообщение о текущем времени. Затем стан- дартной процедурой GetDir(O,Dir_s) прочитаем в переменную Dir s каталог текущего диска (параметр 0 указывает текущий дисковод) и напечатаем значение переменной Dir s. Ис- пользуя стандартные процедуры DiskSize и DiskFree, запишем вывод на экран сообщения о размере текущего дисковода и свободном пространстве на диске. Перед началом поиска присвоим переменной I значение 0 и вызовем процедуру FindFirst('*.pas', Archive, Fileinfo) для поиска в текущем каталоге файлов с расширением .PAS и атрибутом "архивный". После вывода на экран заголовка списка файлов 'Имя файла Размер(байт) Дата Время создания' поиск файлов опишем с использованием оператора по- втора while. Поиск будет повторяться до тех пор, пока выполняется условие DosError=0. В начале тела цикла увеличивается счетчик числа найденных файлов заданного шабло- на, затем файловая переменная связывается с файлом, имя которого определяется значением поля Name записи Fileinfo типа SearchRec. Вызывается процедура отделения имени файла от расширения, после чего на экран вы- водится значение имени и расширения файла, значение поля Size записи Fileinfo типа SearchRec, которое указывает размер файла в байтах.
310 Глава 12 Стандартная функция GetFTime возвращает дату и время последнего изменения файла в упакованном виде. Для его распаковки применим процедуру UnpackTime. После этого с использованием точечной нотации запишем вывод на экран значений полей даты и времени записи Т типа DateTime. Для продолжения поиска файлов, начатого процедурой FindFirst с прежним именем и атрибутами, запишем вызов процедуры FindNext, после чего управление передается в заго- ловок цикла для проверки условия DosError=0 и, если оно выполняется, операторы тела цик- ла выполняются еще раз. Если условие DosError=0 не выполняется, то управление передается за пределы цикла поиска файлов, на экран выводится сообщение о количестве найденных файлов. ' Переименование файла запишем с применением оператора повтора repeat. Для поис- ка файла с заданным именем используем функцию поиска FSearch. Если результат, возвра- щаемый этой функцией, есть пустая строка, то файла в каталогах, описанных в переменной окружения PATH, нет. Для считывания переменной окружения, в которой записан маршрут поиска файлов, используем функцию GetEnv. Условием окончания поиска файла запишем FSearch(Name,GetEnv('PATH')) о ", т. е. файл для переименования успешно найден. После этого записью процедуры Assign связываем файловую переменную Fil с указан- ным файлом и вызываем процедуру Rename File для переименования файла. ; Полный текст программы можно записать следующим образом: program Find_Ren_File; uses Dos, Crt; var Fil : file of byte; Filelnfo : SearchRec; H, M, S, Hund : word; {Переменные ч, мин, с, 0.01 с для GetTime} Ftime : longint; {Упакованные время и дата в GetFTime} j Т : DateTime; {Распакованные время и дата в переменной типа DateTime} , Dir_s, N, Е, Name, New_Name : string; I, P : integer; function LeadingZero(W : Word) : string; {Преобразовать время в строку} var S : string; begin Str(W:0,S); ; if Length(S) = 1 then S := 'O’ + S; LeadingZero := S; end; procedure Nam_Ext_File; {Отделить имя и расширение файла} i
Файлы 311 begin Р := PosFileinfo.Name ); {Определить позицию в имени файла} if Р > 1 then begin N := Copy(Fileinfo.Name, 1,P — 1); E := Copy(Fileinfo.Name, P + 1, 3); end else {Расширение отсутствует} begin N := Fileinfo.Name; E := "; end; * end; procedure Rename_File; {Переименовать файл} begin Write('Введите новое имя файла: '); Readln(New_Name); Rename(Fil,New_Name); Writein('Файл ',Name,' переименован в ',New_Name,1, нажмите Enter Readln; end; begin {Основная программа} ClrScr; GetTime(H,M,S,Hund); {Прочитать системное время} Writein('Текущее время : ', LeadingZero(Н),':', LeadingZero(М),':', LeadingZero(S)); GetDir(O,Dir_s); {0 = читать каталог текущего диска} Writein('Текущий диск й директорий : ',Dir_s); Writein(DiskSize(О) div 1024, ' Кбайт всего на диске'); Writein(DiskFree(O) div 1024, ' Кбайт свободно'); Writein; 1:=0; {Пока не найдено ни одного файла} FindFirst('*.pas', Archive, Fileinfo); Writein('Имя файла Размер(байт) Дата Время создания*); Writeln; while DosError=0 do {Пока поиск файла завершается успешно} begin I:=I+1; {Найден еще один файл} Assign(Fil,Fileinfo.Name);
312 Глава 12 Nam_Ext_File; {Отделить имя файла от расширения} Write(N, ' ': 9 — Length(N), Е, ’ ’: 4 — Length(Е)); Write(* ',Fileinfo.Size: 8); GetFTime(Fil,Ftime) ; '({Возвратить дату и время последнего из- менения файла} UnpackTime(Fileinfo.Time,Т); {Преобразует 4-байтовые, упако- ванные в Longint время и дату в распакованную запись типа DateTime} WriteC ',T.Day:2,,T.Month:2,,T.Year:4); WritelnC ',T.Hour:2,’:•,T.Min:2,':',T.Sec:2); FindNext(Fileinfo); end; t Writeln; WritelnCВ текущем каталоге найдено архивных файлов с расширением .PAS'); repeat Write('Введите имя файла для переименования >’); Readln(Name); {Искать файл в каталогах, описанных в переменной окружения PATH} if FSearch(Name,GetEnv('PATH')) = ’' then Writeln('На диске нет файла ',Name); until FSearch(Name,GetEnv('PATH')) <> '{Найден файл Name} Assign(Fil,Name); Rename_File; {Вызов процедуры переименования файла} end. Запустите интегрированную среду программирования, введите текст программы FindRenFile и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его. Проверьте действие программы в пошаговом режиме с заходом в процедуры и наблюдая текущие значения переменных: Н, М, S, Hund, Dir s, Filelnfo.Name, N, E, Filelnfo.Size, T.Year, T.Month, T.Day, T.Hour, T.Min, T.Sec. Контрольные вопросы и задания Вопросы. 1. Что такое файл? Для каких целей используются файлы? 2. Какими причинами диктуется целесообразность применения файлов? 3. Какие устройства компьютера Турбо Паскаль использует наряду с файлами? Каковы логические имена этих устройств? 4. Каковы требЬвания к именам файлов? 5. Перечислите три характерные особенности файлов, дающие основание считать их одной из наиболее фундаментальных структур данных в Турбо Паскале.
Файлы 313 6. Назовите общие и отличительные черты текстовых, типизированных и нетипизиро- ванных файлов. 7. Зачем используется специальная файловая переменная? Как устанавливается соответ- ствие файловой переменной файлу во внешней памяти? 8. Что общего у процедуры Reset и Rewrite и чем они отличаются? 9. Какие отличия существуют в использовании процедуры Reset при открытии различ- ных типов файлов (текстовых, нетипизированных)? 10. Зачем применяется процедура Close? 11. Какие процедуры применяются для переименования и удаления файлов? Каковы особенности их использования? 12. Для каких целей используется специальная функция lOresult? Каковы условия ее применения? Назначение директивы компилятора {$!}? Каков результат, возвращаемый функцией lOresult после корректного выполнения операции ввода-вывода? 13. В чем заключается специфика текстовых файлов? Назначение процедуры Append. Отличительные особенности процедур Read и Write от Readln и Writeln. 14. Назначение функций Eoln, Eof, SeekEoln, SeekEof. 15. Назначение процедуры Flush и особенности ее использования. 16. Назначение процедуры SetTextBuf, ее параметры. 17. Какие файлы относятся к типизированным? Как представлена информация в типи- зированных файлах? 18. Назначение процедур SizeOf, Seek, Truncate, FilePos, FileSize. 19. В чем заключается несоответствие номера физической записи и номера логической записи в типизированном файле? 20. Какие файлы называются нетипизированными? Как они определяются, каковы их особенности? 21. Назначение процедур BlockRead, BlockWrite. Их особенность. , 22. Назовите стандартные процедуры обработки файлов, содержащиеся в модуле Dos. Каково назначение переменной DosError? 23. Что общего и каковы отличия процедур обработки файлов FindFirst и FindNext, функций FSearch и FExpand? 24. Можно ли, считав из файла пятый элемент, затем сразу же считать второй элемент? А какой можно? 25. В какое место файла можно добавлять новые элементы: в начало, в конец, в середи- ну, куда угодно, никуда? 26. Если не переписывать файл заново, то значения каких элементов можно изменять: только первого, только последнего, каких угодно, никаких? А какие элементы можно уда- лять из файла (при том же условии)? 27. Верно ли, что в одно и то же время нельзя считывать из файла и записывать в него? Верно ли, что, начав считывать из файла, затем уже никогда нельзя записывать в него? А на- оборот? Задания. 1. Составьте программу, которая создает файл, состоящий из 10 значений типа integer. Прочитайте файл и вычислите сумму его элементов. Тип record не используйте.
314 Глава 12 2. Составьте программу, которая создает файл, состоящий из неопределенного количе- ства значений типа integer. Для ввода используйте цикл, выход из цикла — значение 999. После записи выведите файл на экран и уничтожьте файл. Тип record не используйте. 3. Составьте программу, которая создает файл, состоящий из 10 значений типа integer. Прочитайте файл и определите, есть ли в нем заданное с клавиатуры значение. Тип record не использовать. 4. Составьте программу, которая создает файл из элементов типа Char с помощью цик- ла while. Признак выхода из цикла — буква 'z'. Скопируйте созданный файл в другой файл и выведите его на экран. 5. Составьте программу, которая создает файл, состоящий из пяти значений типа real. Тип record не используйте. Выведите файл на экран. В цикле while..do расширьте файл за счет добавления новых значений. Выход из цикла — 999. После расширения выведите файл на экран. * 6. Составьте программу, которая создает файл, состоящий из N значений типа integer. Прочитайте файл и выведите только четные элементы. Тип record не используйте. 7. Создайте файл, компоненты которого являются целыми числами. Напишите про- грамму, переписывающую компоненты файла в обратном порядке. (Новый файл не заво- дить.) 8. Дан файл f, компоненты которого являются целыми числами. Составьте программу, записывающую в файл q все компоненты файла f, делящиеся на 5 и принадлежащие интер- валу [C,DJ. 9. Составьте программу, которая создает файл, компоненты которого имеют следую- щую структуру: табельный номер; ФИО; оклад. Введите в файл данные о пяти работниках, выведите в другой файл данные о работни- ке, имеющем максимальный оклад. 10. Составьте программу, которая создает файл из элементов типа Char с помощью цик- ла while. Признак выхода из цикла — буква 'z'. Присвойте файлу новое имя T)2.DOC' и вы- ведите его содержимое на экран. 11. Составьте программу, которая создает и выводит на экран файл ZARPL.DAT, ком- поненты которого имеют следующую структуру: табельный номер; ФИО, сумма зарплаты. Выход из ввода — табельный номер=999. Выведите на экран табельные номера, ФИО и зарплату только тех, у кого зарплата превышает 100000.00 руб. Используйте оператор with. 12. Составьте программу, которая создает и выводит на экран файл AVANS.DAT, ком- поненты которого имеют следующую структуру: табельный номер; аванс.
Файлы 315 Выход из ввода — табельный иомер = 999. Получите ведомость следующей структуры: Таб. номер Аванс Л 100.00 _2____________200.00 Итого: 300.00. 13. Составьте программу, которая создает файл 'RANDOM1.DAT, состоящий из 50 слу- чайных цифр типа integer в диапазоне 0..200. После создания выведите элементы файла на экран. 14. Составьте программу, которая создает файл 'RANDOM2.DAT', состоящий из 100 случайных цифр типа integer в диапазоне 0..300. Исследуйте получившийся файл с целью обнаружения в нем простых чисел 23,31,37,41,53,107,127,151,197. В конце программы унич- тожьте созданный файл. 15. Составьте программу, которая создает файл 'F1.DTA' из 10 элементов типа integer. Выведите его на экран. Удалите последние пять элементов и выведите его содержимое на экран. 16. Составьте программу, которая создает файл 'OLD.T' из элементов типа char с помо- щью цикла repeat Признак выхода из цикла — символ '!'. Присвойте файлу новое имя NEW.T' и выведите его содержимое на экран. 17. Составьте программу, которая создает файл из 20 компонентов: 1,2,...,20 типа integer с помощью for без ввода с клавиатуры. Выведите файл на экран. Дайте компоненту номер 15 новое значение — 99 и снова выведите файл на экран, затем уничтожьте файл. 18. Составьте программу, которая считывает текст из файла, заменяет в нем все буквы "о" на "а" и записывает файл на диск. 19. Напишите программу, которая создает файл данных, хранящий записи о владельцах ( автомототранспорта: марка автомобиля, номер регистрации в ГАИ, дата постановки на учет, ФИО владельца, домашний адрес (область, город, район, улица, дом, квартира), и обеспечи- вает обслуживание данного файла, запись, изменение данных, удаление, а также поиск дан- ных по регистрационному номеру. 20. Составьте программу, считывающую с диска файл, в котором записана некоторая последовательность символов, и переписывающую эти символы в другой файл, выбрасывая символы, расположенные между скобками (,). Сами скобки тоже выбрасываются. Предпола- гается, что внутри каждой пары скобок нет других скобок. 21. Составьте программу, которая построчно выводит содержимое текстового файла на экран, печатает на бумаге. 22. Имеется текстовый файл. Составить.программу, которая, игнорируя исходное деле- ние этого файла на строки, переформатирует его, разбивая на строки так, чтобы каждая строка оканчивалась точкой либо содержала ровно 60 литер, если среди них нет точки. 23. Имеется файл из целых чисел. Составьте программу упорядочения файла по неубы- ванию. 24. Составьте программу, которая создает файл таблицы значений функций Sin(x) и Tg(x) на отрезке [0,3] с шагом 0.01. Значения х записывать с одной цифрой в дробной части, значения функции Sin(x) — с пятью, а значения Tg(x) — в экспоненциальной форме.
316 Глава 12 25. Дан файл D, содержащий даты. Каждая дата — это число, месяц и год. Найти и за- писать в файл D1 год с наименьшим номером и самую позднюю дату, а в файл D2 — все ве- сенние даты. 26. Составьте программу, которая обеспечивает создание файла, в котором содержатся сведения об игрушках: указывается название игрушки, ее стоимость и возрастные границы (например, игрушка может предназначаться для детей от двух до пяти лет), а также выпол- няет изменение данных и поиск по следующим критериям: а) названия игрушек, цена которых не превышает 4000 руб и которые подходят детям до четырех лет; б) цена и название самой дорогой игрушки; в) названия игрушек, которые подходят детям как четырех лет, так и десяти лет. 27. Составьте программу, которая создает на диске файл ведомость успеваемости уча- щихся класса, в котором будет записана следующая информация: фамилии, имена учащих- ся, название предмета, оценки за две контрольные работы по трем предметам. Программа должна обеспечивать ввод — редактирование данных и вывод списков учащихся: а) выполнивших первую работу по всем предметам на 5; б) выполнивших хотя бы одну работу на 5; в) выполнивших обе работы хотя бы по одному предмету на 5; г) выполнивших все работы на 4 и 5; д) выполнивших две работы на 4 и 5; е) выполнивших все работы на 3. Вывести на экран число учеников, выполнивших обе работы на 4, не выполнивших ни одной работы. 28. Составьте программу записывающую в файл закодированный текст, считывающую его и выполняющую дешифрование, если известен код шифрования — число, указывающее смещение букв в алфавите (например, код 3 означает, что вместо буквы "а" в зашифрован- ном тексте указана буква "в"). Дешифрованный текст записать в другой файл. 29. Составьте программу записывающую в файл одномерный массив случайных целых чисел, а затем считывающую его с диска и выполняющую запись четных элементов массива в другой файл. 30. Составьте программу, которая создает файл записей — телефонный справочник од- ноклассников и обеспечивает ввод данных, поиск номера телефона по фамилии, подсчет и вывод списка всех абонентов по критерию увлечение компьютерными играми. В записи о каждом однокласснике содержатся следующие сведения: фамилия, имя, телефон, хобби. 31. Составьте программу, которая создает файл данных о химических элементах, ото- бражая следующую информацию: название, символическое обозначение, массу атома, заряд атомного ядра, перечень основных химических свойств. Программа должна выполнять вы- вод данных о химическом элементе по указанному символическому обозначению, находить элемент с самой большой массой, с самым маленьким зарядом ядра. 32. Составьте программу, которая создает файл данных о жильцах дома, отображая в нем следующую информацию о каждом: номер квартиры, фамилию, имя, возраст, для лиц старше 18 лет в зависимости от рода занятий (учеба, работа, пенсия) — запись места учебы, места работы и трудового стажа, для пенсионеров — год выхода на пенсию. Программа должна обеспечивать ввод данных, поиск квартиры с максимальным числом жильцов, поиск самого юного и самого пожилого жильца, поиск студентов, пенсионеров.
Глава 13. ДИНАМИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ В любой вычислительной системе память относится к таким ресурсам, которых всегда не хватает. Управление памятью — одна из главных забот программиста, так как для него очень важно создавать программы, эффективно использующие па- мять, ведь во время выполнения программы память необходима для следующих элементов программ и данных: • сама программа пользователя; • системные программы времени выполнения, которые осуществляют вспомо- гательные действия при работе программы пользователя; • определяемые пользователем структуры данных и константы; • точки возврата для подпрограмм; • временная память для хранения промежуточных результатов при вычислении выражений; • временная память при передаче параметров; • буферы ввода-вывода, используемые как временные области памяти, в кото- рых хранятся данные между моментом их реальной физической передачи с внеш- него устройства или на него и моментом инициации в программе операции ввода или вывода; • различные системные данные (информация о статусе устройств ввода-вывода и т.п.). Из этого перечня видно, что управление памятью касается широкого класса объектов. Ранее вы использовали простейший способ распределения памяти — статическое распределение, т. е. распределение памяти при трансляции програм- мы. Статическое распределение памяти эффективно, поскольку на управление па- мятью не тратится ни времея, ни память. Далее вы познакомитесь с динамическим (во время выполнения программы) управлением памятью. 13.1. СТАТИЧЕСКИЕ И ДИНАМИЧЕСКИЕ ПЕРЕМЕННЫЕ Практика программирования часто выдвигает на передний план два конфлик- тующих фактора: время выполнения программы и объем занимаемой памяти. Ко-
318 Глава 13 нечно, высокое быстродействие программы всегда желательно, но бывают случаи, когда важнее обеспечить максимальную экономию памяти даже ценой потери в скоростных характеристиках. Рассмотрим пример. Пусть в программе обрабатыва- ется матрица 300*300 целых чисел, тогда ее нужно описать следующим образом: var Ml : array[1..300,1..300] of integer; Такие переменные, описанные в разделе var, Н.Вирт назвал статическими. Название “статические” они получили за то, что компилятор Паскаля может обра- ботать их без выполнения программы, т. е. на основании лишь статического текста программы. Статические переменные можно использовать в случаях, когда память, необходимая для работы программы, предсказуема в момент написания програм- мы. В данном случае мы имеем наглядный пример нерационального использования памяти компьютера с применением статических переменных. Так как один элемент матрицы — целое число — занимает в памяти два байта, а общее количество эле- ментов равно 300*300= 90000, то для размещения всей матрицы вышеописанным способом в памяти компьютера нужно 90000*2 байт = 180000 байт. Вместе с тем маловероятно, чтобы при всяком выполнении программы ей действительно были нужны одновременно все элементы такого огромного массива. К тому же все пере- менные, объявленные в программе, размещаются в одной непрерывной области оперативной памяти, которая называется “сегментом данных”. Длина сегмента дан- ных определяется архитектурой микропроцессора 8086 и составляет 65536 байт (64 Кбайта), что также вызывает затруднения при обработке больших массивов дан- ных. Выходом из этого положения может быть использование динамической памя- ти. Динамическая память — это оперативная память компьютера, предоставляе- мая программе при ее работе, за вычетом сегмента данных (64 Кбайта), стека (обычно 16 Кбайт) и собственно тела программы. Размер динамической памяти оп- ределяется всей доступной памятью компьютера и составляет 200...400 Кбайт. Решение проблемы экономного расходования памяти состоит в том, чтобы не резервировать заранее максимальный объем памяти для размещения данных, а, предварительно определив тип данных, создавать новый экземпляр данных всякий раз, когда в нем возникает необходимость. Такие переменные, которые создаются и уничтожаются в процессе выполнения программы, называются динамическими. Помимо экономии памяти использование динамических переменных обуслов- лено существованием задач, для которых невозможно предсказать размер памяти в момент написания программы, а память должна выделяться динамически в процес- се их решения. Необходимость применения динамических переменных возникает при разработке систем автоматизированного проектирования, так как размерность математических моделей, используемых в САПР, может значительно различаться в разных проектах. Динамическая память широко используется для временного запо- минания данных при работе с графическими и звуковыми средствами компьютера.
Указатели 319 13.2. УКАЗАТЕЛИ Все рассмотренные ранее типы данных содержат непосредственно данные, размещенные в памяти компьютера. Для организации динамической памяти приме- няются особые переменные, называемые указателями. Назначение их состоит в том, чтобы указывать местоположение какой-то другой переменной заранее опре- деленного типа. Таким образом, указатель — это переменная, которая в качестве своего значения содержит адрес первого байта памяти, по которому записаны дан- ные. Сам по себе указатель занимает в памяти всего четыре байта, а данные, на ко- торые он указывает, могут простираться в памяти на десятки килобайт. Перемен- ной, на которую указывает указатель, не обязательно присваивать какое-либо имя. К ней можно обращаться через имя указателя, потому она называется ссылочной переменной. * 13.2.1. Типизированные указатели Указатели, содержащие адрес, по которому записана переменная заранее опре- деленного типа, называются типизированными. Для объявления типизированного указателя используется знак л, который помещается перед соответствующим ти- пом. Синтаксическая диаграмма определения типа указателя такова: + идентификатор базового типа * В соответствии с диаграммой для объявления типа указателя Р на целые следу- ет записать: type Р = Ainteger; Тип integer в данном примере является базовым типом. Имея в программе оп- ределение типа указателей (ссылочного типа), можно по общим правилам описать переменные этого типа. При этом ссылочные типы в описаниях переменных можно задавать как посредством идентификаторов, так и явно, например: var Pl, Р2 : Р; {Тип Р введен выше} R : AByte; Описание типов указателей — единственное исключение из общего правила, согласно которому все идентификаторы должны быть описаны перед использова- нием. Однако если базовый тип является еще не объявленным идентификатором, то он должен быть объявлен в той же самой части объявления, что и тип “указа- тель”.
320 Глава 13 Например: RecPtr = ARecordType; RecordType=record Name : string[20] ; Number : integer; end; В данном примере RecPtr описывается как указатель на переменную RecordType. Базовый тип RecordType описывается в той же самой последова- тельности определений типов, что и тип RecPtr. Реально значения ссылочных типов содержат адреса расположения в памяти конкретных значений базового типа. В персонально^ компьютере адреса задаются совокупностью двух шестнадцатиразрядных слов, которые называются сегментом и смещением. Сегмент — это участок памяти, имеющий длину 65536 байт (64 Кбайт) и начинающийся с физического адреса, кратного 16 (0, 16, 32, 48 и т.д.). Смещение указывает, сколько байт от начала сегмента необходимо пропустить, чтобы обратиться к нужному адресу. Таким образом, по своей внутренней структу- ре любой указатель представляет собой совокупность двух слов (данных типа word), трактуемых как сегмент и смещение, а абсолютный адрес образуется сле- дующим образом: сегмент* 16+смещение. Отсюда видно, что сегменты могут пе- рекрываться, т. е. один и тот же абсолютный адрес может образовываться несколь- кими способами, например: Сегмент $0020 —1 $001F Смещение $0010 +16 $0020 Абсолютный адрес $00210 $00210 Такая многозначность является следствием того, что из 16+16 бит сегмента и смещения для образования адреса используются только 20 бит. Поэтому при срав- нении адресов, сгенерированных с помощью стандартной функции Ptr, нужно быть осторожным. Адреса, генерируемые с помощью стандартных процедур New и GetMem, всегда нормализуются, т. е. смещение всегда лежит в диапазоне $0000..$000F. Для того чтобы присвоить переменной ссылочного типа некоторое значение, можно воспользоваться унарной операцией взятия указателя, которая строится из знака этой операции — символа @ (амперсанд) и одного операнда- переменной. Например, если имеется описание переменной I целого типа: var I : integer; то применение этой операции к переменной I: @1 дает в качестве результата значе- ние типа "указатель на целое". Аналогичный результат получится и в результате операции Р = Ainteger.
Указатели 321 Примечание. Операция взятия указателя допустима для любых переменных, в том чис- ле для элементов массивов, полей записи и т. д. Например, если есть описание var А : array[1..10] of integer; то конструкция @А[1] имеет смысл "указателя на I-е целое в массиве А" и также может участвовать в присваивании: Р := @А[1]. Ссылочные типы можно образовывать от любых типов, поэтому допустимо оп- ределение вида "указатель на указатель". Среди всех возможных указателей в Турбо Паскале выделяется одинхпециаль- ный указатель, который "никуда не указывает". Можно представить такую ситуа- цию: в адресном пространстве оперативной памяти компьютера выделяется один адрес, в котором заведомо не может быть размещена никакая переменная. На это место в памяти и ссылается такой-нулевой (или пустой) указатель, который обозна- чается словом Nil. Укг^атель Nil считается константой, совместимой с любым ссы- лочным типом, поэтому его значение можно присваивать любому указателю. Обычно значение Nil присваивают указателю, когда его указание надо отменить или в начале инициализации программы. Это позволяет проверять значение указа- теля, прежде чем присваивать ему какое-либо значение. 13.2.2. Нетипизированный указатель (pointer) В Турбо Паскале можно объявлять указатель и не связывать его при этом с ка- ким-либо конкретным типом данных. Для этого служит стандартный тип pointer. Он обозначает нетипизированный указатель, т. е. указатель, который не указывает ни на какой определенный тип. С помощью нетипизированных указателей удобно динамически размещать данные, структура и тип которых меняются в ходе про- граммы. Переменные типа pointer не могут быть разыменованы; указание символа л по- сле такой переменной вызывает появление ошибки. Как и значение, обозначаемое словом Nil, значения типа pointer совместимы со всеми другими типами указате- лей. Над значениями ссылочных типов допускаются две операции сравнения на ра- венство и неравенство, например @Х о @Y или Pl = Р2. Два указателя равны только в том случае, если они ссылаются на один и тот же объект. Примечание. При сравнении указателей в Турбо Паскале просто сравниваются сегмен- ты и смещения. В соответствии со схемой размещения сегментов процессоров 80x86 два логически различных указателя могут фактически указывать на одну и ту же физическую ячейку памяти. 13.2.3. Доступ к переменной по указателю Для доступа к переменной имеются две возможности: первая —использовать идентификатор переменной, вторая — воспользоваться адресом этой переменной, который содержится в указателе на эту переменную. Например, чтобы увеличить
322 Глава 13 значение переменной I, на которую указывает указатель Р, по первому способу можно записать: 1:=1+2. Для реализации второго, косвенного доступа к переменной по указателю, ис- пользуется разыменование указателя. Правило разыменования таково: для того чтобы по указателю на переменную получить доступ к самой переменной, нужно после переменной-указателя поставить знак Л. Так, запись Writeln(Int2A); означает напечатать значение переменной, на которую ссылается указатель Int2. Поэтому чтобы увеличить значение переменной I, на которую указывает указа- тель Р, используя косвенный доступ к переменной по указателю, можно записать: рл •= рл + 2 . Примечания. 1. Разыменование допускается для любых ссылочных типов. В случае ис- пользования "указателя на указатель" возможно многократное разыменование. 2. Разыменование считается некорректным, если ссылочная переменная имеет значение Nil, т. е. в этом случае не существует переменной, на которую ссылается указатель. 13.3. УПРАВЛЕНИЕ ДИНАМИЧЕСКОЙ ПАМЯТЬЮ Как было сказано в гл. 1, вся динамическая память в Турбо Паскале рассматри- вается как сплошной массив байтов, который называется кучей. Физически куча располагается в старших адресах сразу за областью памяти, которую занимает тело программы. Начало кучи хранится в стандартной переменной HeapOrg, конец — в перемен- ной HeapEnd. Текущую границу незанятой динамической памяти указывает указа- тель HeapPtr. Для управления кучей используются следующие стандартные процедуры и функции: Процедуры динамического распределения Процедура Описание Dispose Уничтожает динамическую переменную FreeMem Уничтожает динамическую переменную данного размера GetMem Создает новую динамическую переменную заданного размера и устанавливает пере- менную-указатель для нее Mark Записывает в переменной-указателе состояние кучи New Создает новую динамическую переменную и устанавливает на нее переменную-указа- тель Release Возвращает кучу в заданное состояние Функции динамического распределения Функция Описание MaxAvail Возвращает размер наибольшего непрерывного свободного блока кучи, соответст- вующей размеру наибольшей динамической переменной, которая может быть распре- делена в момент вызова MaxAvail MemAvail Возвращает количество имеющихся в куче свободных байтов
Указатели 323 Функции для работы с указателями и адресами Функция Описание Addr Возвращает адрес заданного объекта CSeg Возвращает текущее значение регистра CS DSeg Возвращает текущее значение регистра DS Ofs Возвращает смещение заданного объекта Ptr Преобразует базовый адрес сегмента и смещение в значение типа указатель Seg Возвращает сегмент для заданного объекта SPtr Возвращает текущее значение регистра SP SSeg Возвращает текущее значение регистра SS Основные действия над динамическими переменными — создание и уничтоже- ние — реализуются в Турбо Паскале стандартными процедурами New и Dispose. Процедура New предназначена для создания динамических переменных опре- деленного типа. Она отводит новую область памяти в куче для данной динамиче- ской переменной и сохраняет адрес этой области в переменной-указателя. Можно присвоить значение переменной-указателю и с помощью оператора @ или функ- ции Ptr. Оператор @ устанавливает переменную-указатель на область памяти, со- держащую существующую переменную, включая и те переменные, которые име- ют идентификаторы. Функция Ptr устанавливает переменную-указатель на опреде- ленный адрес в памяти. Например: New(Inti); Pl := @Х; Ptr($40, $49); Для освобождения памяти в куче предназначена процедура Dispose с парамет- ром указатель на динамическую переменную, причем эта переменная должна быть ранее размещена в куче посредством New, а тип размещенной переменной должен совпадать с базовым типом параметра процедуры Dispose. Например: Dispose(Intl); освобождает выделенный в предыдущем примере в куче фрагмент памяти так, что его можно снова использовать, если потребуется. Если в программе не предполагается использование кучи, то при работе с про- граммой Турбо Паскаля рекомендуется снизить потребность в памяти для создавае- мой программы с помощью меню Options/Heap. Примечание. При освобождении динамической памяти нужно соблюдать осторож- ность, т. е. пользоваться либо New/Dispose, либо New/Mark/Release, либо GetMem/FreeMem, но ни в коем случае не перепутать сочетание этих процедур. Упражнения Упражнение 1. Изучите текст программы, которая демонстрирует использование ука- зателей массива. program d_pointl; {Пример указателя массива и указателя компонен- та} type
324 Глава 13 feld — array[1..10] of integer; {Описание типа массива из 10 целых чисел} var I : integer; {Целая переменная для указания индекса элемента массива} F : feld; {Переменная типа feld (массив из 10 целых чисел)} Zf : Afeld; {Указатель на массив} begin for I:=l to 10 do F[I]:=10*I; {Генерировать массив} Zf:=6F; {Присвоить указателю Zf адрес начала размещения масси- ва в памяти} Writeln(Longint(@F)) ; Writeln(ZfА[1]); {Напечатать значение 1-*го элемента массива, на который указывает Zf} Zf:=6F[2]; {Присвоить указателю Zf адрес размещения 2-го эле- мента массива} Writeln(ZfА[1]); {Напечатать значение 1-го элемента массива, на который указывает Zf} Zf:=Ptr(Seg(F[3]),Ofs(F[3])+SizeOf(integer)); {Присвоить ука- зателю Zf адрес 4-го элемента массива} Writeln(ZfA[1]); {напечатать значение 1-го элемента массива, на который указывает Zf} end. Запустите интегрированную среду программирования, введите текст программы d_pointl, запишите его н?. диск и откомпилируйте. Исполните программу в пошаговом про- ходе и пронаблюдайте текущие значения переменных: I, F,Zf, ZfA. Обратите внимание на различие вывода значений элементов массива на экран оператором WritelnCZf^l]) в связи с изменением значения указателя на массив. Упражнение 2. Изучите текст программы, которая демонстрирует использование дина- мической памяти. program D_point2; type IntPtr =Ainteger; {Описание типа указателя на целое IntPtr} RecPtr = АRecordType; {Описание типа указателя на запись RecordType} RecordType «record {Описание типа записи RecordType} Name : string[20]; Number : integer; end; var Inti,Int2,Pl,P2 : IntPtr; {Описание переменных типа IntPtr} He : RecPtr; {Описание переменной типа RecPtr} X,Y,I : integer;{Переменные целого типа} P : AByte; {Указатель на данные типа byte}'
Указатели 325 begin Р := Ptr($40,-$49} {Присвоить Р значение ячейки памяти по адре- су: сегмент $40, смещение $49 — по этому адресу записан текущий видеорежим} Writeln('Текущий видеорежим :', РЛ); Writeln (MemAvail,' байт всего свободно в памяти (размер кучи)'); Writeln('Объем наибольшего непрерывного свободного блока кучи =', MaxAvail, ' ,байт'); Write('Введите два целых числа : ') ; Readln (X,Y) ; New(Inti); {Создать новую динамическую переменную Inti и устано- вить на нее указатель} IntlA:=X; {Записать по адресу, указанному Inti, значение пере- менной X} Writeln('Х=',Х,' IntlA=',IntlA); New(Int2); {Создать новую динамическую переменную Int2 и устано- вить на нее указатель} Int2A:=Y; {Записать по адресу, указанному.Int2, значение пере- менной Y} Writeln('Y=*,Y,' Int2A=',Int2A) ; {Подсчитать размер динамической памяти для размещения одной RecordType и вывести сообщение} Writeln('Для одной записи нужно ',SizeOf(RecordType),' байт'); New(Не); {Создать новую динамическую переменную Не и установить на нее указатель} Write('Введите Ваши фамилию и имя :'); Readln(НеА.Name); {Считать в динамическую переменную значение фамилии и имени} Write('Сколько Вам лет :'); Readln(НеА.Number); {Считать в динамическую переменную зна- чение возраста} {Напечатать на экране значения полей динамической переменной Не типа RecPtr} Writeln('НеЛ.Number=',НеА.Number,' НеА.Name=',НеА.Name); {Выполнить сравнение значений динамических переменных, на которые указывают НеЛ.Number и IntlA и выполнить соответствующие операции} if НеА.Number>IntlAthen Y:= НеА.Number else Y:=IntlA; * Writeln('Y=*,Y); Dispose(Inti); {Освободить память, отведенную ранее под динами- ческую переменную Inti} Dispose(Int2); Dispose(Не); Pl := вХ; {Теперь присвоить указателю Pl адрес переменной X}
326 Глава 13 Р2 := Addr(Y); {Присвоить указателю Р2 адрес размещения в дина- мической памяти переменной Y} Writeln(PlA); {Напечатать значение переменной X} Р2 := Pl; {X —> Р2} Y р2Л; {То же, что и Y := X;} {Сравнить значения адресов, по которым в динамической памяти запи- саны переменные X и Y} if ex О @Y then Writein ('Значения адресов переменных X и Y не- равны ') ; Writein (Y); {Выводит значение Y, равное значению X} end. Запустите интегрированную среду программирования, введите текст программы d_point2, запишите его на диск и откомпилируйте. Исполните программу в пошаговом про- ходе и пронаблюдайте текущие значения переменных: MemAvail, MaxAvail, HeapOrg, HeapEnd, HeapPtr, X,Y, НеЛ. Обратите внимание на изменение текущих значений перемен- ных MemAvail, MaxAvail при создании и уничтожении динамических переменных. Упражнение 3. Изучите текст программы, которая считывает файл целых чисел, со- стоящий из нескольких последовательностей чисел, каждая из которых оканчивается чис- лом — 1, и затем выводит эти последовательности чисел в выходной файл, но внутри каж- дой из них расставляет числа в обратном порядке. program d_point3; type Pointer=A DataSet; {Определение указателя на запись DataSet} DataSet =record {Определение структуры записи} Data : integer; {Поле — целое число} Point : Pointer; {Поле — нетипизированный указатель} end; var Rl, R2 : pointer; {Нетипизированные указатели} I,К : integer; {Вспомогательные переменные} Inp, Out: file of integer; {Файловые переменные Inp — исходный файл целых чисел, Out — выходной файл целых чисел} begin {Фрагмент создания исходного файла — при первом запуске отключите комментарии} { Assign(Inp,'Input.dat *) ; Rewrite(Inp); for K:=l to 20 do {Создать файл из 20 чисел} begin Write('Введите ',K,*-е целое число :’); Readln(I); Write(Inp,I);{Записать в исходный файл очередное число} end;
Указатели 327 Close(Inp);' {Завершение создания исходного файла чисел} Assign(Inp,' Input.dat '); Reset(Inp); {Открыть исходный файл Input.dat} Assign(Out,* Output.dat {); Rewrite(Out); 1Открыть исходный файл Output.dat} while not Eof(Inp) do {Пока не будет обнаружен конец исходного файла} begin Rl:=Nil; {Установить указатель R1 на Nil} Read(Inp,I); {Читать из исходного файла число в переменную I) while I<>—1 do {Пока значение переменной I не равно —1 (ко- нец последовательности)} begin New(R2); {Создает новую динамическую переменную и уста- навливает на нее переменную-указатель} R2A.Data:=I; {Записываем в поле Data динамической перемен- ной DataSet , адрес которой указывает R2 (первой в создаваемой в динамической памяти цепочки), значение переменной I — очередного числа последовательности} R2А.Point:=R1; {Записываем в поле Point динамической пере- менной DataSet , адрес которой указывает R2, значение указателя) Rl:=R2;•{Теперь указатель R1 указывает на новый объект в це- почке чисел} Read(Inp,I);{Читать из исходного файла число в переменную 1} end; R2:=R1; {Указать начало следующей последовательности чисел} while R2ONil do {Пока значение указателя R2 О Nil} begin Write(Out,R2A.Data); {Записать в выходной файл значение поля Data динамической переменной, на которую указывает R2) Write(R2A.Data);{Напечатать на экране (для контроля) зна- чение поля Data динамической переменной, на которую указывает R2} R2:= R2A.Point {Присваиваем R2 значение поля Point динами- ческой переменной DataSet, указывающей на следующее число в после- довательности} end; Write(Out,I); {Записать в выходной файл 1=—1} Writeln; end; Dispose(R1); {Освободить динамическую память} Close(Inp); {Закрыть исходный файл} Close(Out); {Закрыть выходной файл} end.
328 Глава 13 J- Запустите интегрированную среду программирования, введите текст программы d_point3, запишите его на диск и откомпилируйте. Исполните программу в пошаговом про- ходе. При первом запуске программы исключите фигурные скобки — признак комментари- ев в фрагменте создания исходного файла и введите 20 целых чисел, разделяя их на после- довательности числом —1, например: 1, 3, 5, —1, 0, 3, 5, 6, —1, 9, 2, 4, —1, 2, 33, 4, 21, 33, 7, —1. Перед повторными запусками программы отключите фрагмент создания исходного файла, чтобы не вводить всякий раз заново исходную последовательность чисел. Запустив программу в пошаговом исполнении, пронаблюдайте текущие значения переменных: MaxAvail, Rl, R2A.Point' R2,1, R2AData. Упражнение 4. Изучите текст программы, создающей в динамической памяти матрицу из N*M случайных чисел и вычисляющей среднее арифметическое ее элементов. program d_point4; const SizeOfReal = 6; {Размер места в памяти для размещения перемен- ных типа real} N = 200; {Размерность матрицы: строк} М = 100; {Размерность матрицы: столбцов} type RealPoint = Areal; {Определение указателя с базовым типом real) var I,J : integer; {Переменные для адресации к нужному эле- менту матрицы} PtrStr : array [1..N] of pointer; {Массив указателей на начало размещения в динамической памяти каждой строки матрицы} S : real; {Сумма элементов матрицы) function AddrR(I,J:word) : RealPoint; {Вычисление адреса элемента матрицы} begin {Присвоить функции AddrR значение указателя, полученного стандарт- ' ной функцией Ptr} AddrR := Ptr(Seg(PtrStr[I]А),ofs(PtrStr[I]А)+(J—I)*SizeOfReal); {Seg(PtrStr[I]л) — базовый адрес сегмента памяти, в котором разме- щены элементы I-й строки матрицы, ofs(PtrStr[I]л)+(J—I)*SizeOfReal — смещение (позиция [I,J]-го элемента матрицы в данном сегменте)} end; function GetR(I,J:integer) : real; begin GetR := AddrR(I,J)A;{Присвоить функции GetR значение элемента матрицы по адресу AddrR(I,J)} end; procedure PutR(I,J:integer; X:real); begin
Указатели 329 AddrR(I, J) Л :=Х {Присвоить элементу матрицы по адресу AddrR(I,J) значение переменной X} end; begin {Начало основной программы} Randomize; Writeln('Объем наибольшего непрерывного свободного блока кучи —*, MaxAvail, * ,байт'); Readln; for I:=l to N do {Просматривая матрицу по строкам от 1 до N) begin GetMem(PtrStr[I], M*SizeOfReal); {Создать новую динамическую переменную размера M*SizeOfReal для размещения I-й строки матрицы и установить переменную-указатель для нее} for J:=l to М do PutR(I,J, Random) {Для всех элементов I-й строки от 1 до М-го столбца вызвать функцию присваивания элементу матрицы значения результата стандартной функции Random, здесь ре- зультат функции Random — параметр-значение} end; S:=0; {Обнулить сумму элементов} Writeln('Объем наибольшего непрерывного свободного блока кучи —', MaxAvail, ' ,байт'); Readln; for I:=l to N do {Просматривая все строки матрицы} for J:=l to M do {Просматривая все столбцы матрицы} S:=S+GetR(I,J); {Вычисление суммы элементов матрицы) Writeln('среднее арифметическое =', S/(N*M):12:5); Release(HeapOrg); {Возвращает динамическую память в заданное состояние, очищая ее с адреса, указанного в HeapOrg} end. Запустите интегрированную среду программирования, введите текст программы d_point4, запишите его на диск и откомпилируйте. Исполните программу в пошаговом про- ходе с заходом в процедуры и функции и пронаблюдайте текущие значения переменных: MaxAvail, HeapOrg, HeapPtr, U, AddrR(I,J)A, S. 13.4. Использование указателей для организации связанных списков Чаще всего указатели используются для ссылки на записи, тем самым дости- гается значительная экономия памяти. Если же сама запись содержит в себе поле- указатель, указывающий на следующую за ней запись, то это позволяет образовать связанные списки — структуру, в которой отдельные записи последовательно свя- заны друг с другом. В приведенном ниже примере используются записи, в которых наряду с данными об автомобиле имеется указатель на следующую запись, благо- s даря чему получается связанный список.
330 Глава 13 Объект 1 Объект 2 Объект 3 Объект N Nil Одно из полей каждого объекта связанного списка имеет тип указатель и ука- зывает на очередной объект в списке. Указатель на первый объект содержится в пе- ременной First, а последний объект имеет указатель на Nil. Упражнение 5. Изучите текст программы, в которой создается связанный список из за- писей, содержащих сведения об автомобилях, и иллюстрируются некоторые операции со связанными списками: запись первым в список; удаление первого объекта из списка; про- смотр всего списка; удаление объекта, следующего за указанным. program d_point5; {Пример использования указателей для обработки связанного списка} uses Crt; type NameStr “ string[20]; Link = AAuto;. ( Auto — record Name : NameStr; {Марка автомобиля} Speed : real; {Скорость} Next : Link; {Поле для связи co следующим объектом в списке} end; var Р,First : Link; {Указатели на запись: текущую, первую} NamFind : NameStr; {Марка автомобиля для поиска} V : 0..4; {Селектор меню} EndMenu : boolean; {Окончание вывода меню} function FindName(EN:NameStr) : Link; {Поиск объекта с именем FN, по результатам поиска возвращает указатель на найденный объект или Nil, если объект не найден} var Curr : Link; begin Curr:“First; {Установить указатель на первом объекте в спи- ске } while Curr О Nil do {Повторять пока не дойдем до конца списка} if Curr*.Name = FN then {Если нашли заданный объект} begin FindName:=Curг;{Возвращаем значение указателя на него}
Указатели 331 Exit; {Завершаем функцию} end else Curr:=CurrA .Next; {Перейти к следующей записи} FindName:—Nil; {В списке нет искомого'объекта} end; {Конец FindName} procedure AddFirst(A:Link); {Добавление записи первой в связанный список} begin АА .Next:=First; {Новый объект первый в списке} First:=A; {Голова списка ссылается на новый объект} end; {Конец AddFirst} procedure DelFirsV(var A:Link); {Удаление первого объекта из спи- ска} begin A:=First; First:=FirstA.Next; {Теперь First указывает на тот объект, на который ранее ссылался объект А} end; {Конец DelFirst} procedure DelAfter(Old:Link; var A:Link); {Удаление из списка объекта, стоящего после объекта Old } begin A:=OldA.Next; {Переменной А присваивается значение указателя на удаляемый объект} OldA.Next:=OldA.Next*.Next; {Теперь OLd указывает на тот объ- ект, на который ранее ссылался следующий за ним объект, а объект А исключен из связанного списка} end; {Конец DelAfter} procedure InpAvto; {Ввести данные об объекте} begin Р:—New(Link); {Создать очередной объект типа Auto} Write('Введите марку автомобиля :'); Readln(РА.Name); Write('Максимальная скорость :'); Readln(РА.Speed); AddFirst(Р); {Вызов процедуры добавления записи, на которую ссылается указатель Р (Р — фактический параметр, А — формальный параметр-значение)} end; {Конец InpAvto} procedure fcfyList; {Вывести на экран все объекты из связанного списка} var Curr : Link; {Локальная переменная — указатель На очередной объект)
332 Глава 13 begin Curr:=First; {Установить указатель на первом объекте в списке} while Curr О Nil do {Повторять, пока не дойдем до конца списка) begin Writeln('Марка : ',CurrЛ.Name,' скорость : Curr*.Speed); Curr:=Curr*.Next; {Перейти к очередному объекту связанного списка} end; Write('Вывод списка окончен. Нажмите Enter'); Readln; end; {Конец MyList) begin {Основная программа) New(P); {Создать новую динамическую переменную и установить на нее переменную-указатель} EndMenu:—False; repeat {Очищать экран и выводить меню до тех пор, пока EndMenuOTrue} ClrScr; Writeln('Укажите вид работы:'); Writeln('1. Запись первым в список'); Writeln('2. Удаление первого объекта из списка *); Writeln('3. Просмотр всего списка'); Writeln(*4. Удаление объекта, следующего в списке За указан- ным' ) ; Writeln('0. Окончание работы'); Readln(V); case V of {Вызов разных процедур в зависимости от выбора пунк- та меню) 1 : InpAvto; {Ввод данных об объекте} 2 : DelFirst(P); {Удаление первого в списке) 3 : MyList; {Вывод списка всех элементов связанного списка} 4 : begin {Удаление объекта, следующего за указанным} Write('Введите марку автомобиля, За которым следует удаляемый из списка :'); Readln(NamFind); DelAfter(FindName(NamFind),P); {Вызов процедуры DelAfter с параметрами: функцией FindName(NamFind) и указателем P) end; else EndMenu:—True; {Завершить вывод меню} end; until EndMenu; {Если EndMenu=True, то завершить вывод меню на экран)
Указатели 333 Dispose(Р); {Уничтожить динамическую переменную Р и освободить память в куче} end. Запустите интегрированную среду программирования, введите текст программы d_point5, запишите его на диск и откомпилируйте. Исполните программу в пошаговом про- ходе с заходом в процедуры и пронаблюдайте текущие значения переменных: MaxAvail, Curr, Р, First* Old* A, A*, A*.Next. Каждую операцию по изменению связанного списка: за- пись первым в список; удаление первого объекта из списка; удаление объекта, следующего за указанным; сопровождайте выполнением просмотра всего списка для наблюдения изме- нений в списке. Как видно из наблюдений за выполнением программы, в ней моделируется поведение стека: конец списка является его основанием, новый элемент добавляется в вершину стека. I Контрольные вопросы и задания Вопросы. 1. Какие переменные называются статическими, в каких случаях они применяются, по- чему их необходимо предварительно описывать? 2. Что называют динамическими переменными, каковы причины их использования? 3. Что называют указателем, базовым типом? Чем отличаются типизированный и нети- пизированный указатели? 4. Каковы особенности описания типов указателей? 5. Каково назначение указателя Nil? 6. Объясните синтаксическую диаграмму определения типа указателя. 7. Для чего применяется и как задается операция взятия указателя? 8. Что такое разыменование указателя. Каково правило разыменования? 9. Что называется кучей? В какой области памяти компьютера она располагается? Ка- кие значения имеют стандартные переменные HeapOrg, HeapEnd, HeapPtr? 10. Какие стандартные процедуры и функции используются для управления кучей? 11. Каковы особенности применения сочетания стандартных процедур при освобож- дении динамической памяти? 12. Даны следующие описания: type ref = *integer; var P,Q : ref; Переменные P и Q имеют значения, как показано на рисунке. Ответьте на вопросы:
334 Глава 13 а) что является значением переменной Р: ссылка на объект (переменную) целого типа или сам этот объект? Что обозначает переменная РЛ: ссылку на объект целого типа, сам этот объект или целое 5? Каковы типы переменных Р и РЛ? б) Что будет выдано на печать в результате выполнения следующих операторов? PA:=QA; if P=Q then P:=Nil else if PA=QA then Q:=P; if P=Q then QA:=4; Writein(PA); Задания. 1. Имеется программа: * program zadaniel; . var X : Aboolean; Y : boolean; begin {a} New(X); {b} XA:=True; Y:=not XA; {c} Dispose(X) ; {d} Writein(Y); end. Ответьте на следующие вопросы: а) какие переменные существуют в каждой из точек a,b,c,d, каковы их значения в эти моменты? б) можно ли переменной X присвоить ссылку на переменную Y? Можно ли с помо- щью процедуры Dispose уничтожить переменные X и Y? 2. Найдите ошибки в следующей программе: program Errors; var А,В : Аinteger; begin if A=Nil then Read (A) ; AA:=5; B:=Nil; BA:=2; New(B); Read(BA); Writein(B,BA); New (A) ; B:=A; Dispose(A) ;
Указатели 335 ВЛ:=4; end. 3. Дан список случайных целых чисел. Переверните список, т. е. расставьте все числа в обратном порядке. Подсчитайте среднее арифметическое его элементов. Создайте два но- вых списка, в один из которых запишите все элементы больше 5, в другой — остальные эле- менты исходного списка. 4. Дан список слов. Выведите на экран слова списка, которые: а) оканчиваются и начинаются одной и той же литерой; 6) начинаются с той же литеры, что и следующее слово; в) совпадают с последним или первым словом. 5. Дан текстовый файл. Распечатайте текст в обратном порядке слов. 6. Дан файл сведений об учащихся (фамилия, имя, возраст), расположенных по алфави- ту фамилий. Создайте новый файл, в котором эти сведения будут записаны в обратном по- рядке. 7. Дан текстовый файл. Замените все вхождения указанного слова на заданное слово. 8. Дан файл картотеки сведений об автомобилях (марка, номер и фамилия владельца); а) найдите фамилии владельцев, номера автомобилей указанной марки и их количество; б) найдите фамилию владельца по указанному номеру автомобиля; в) исключите из картотеки сведения об автомобиле заданного владельца.
Часть IV УПРАВЛЕНИЕ ЭКРАНОМ КОМПЬЮТЕРА В ТЕКСТОВОМ И ГРАФИЧЕСКОМ РЕЖИМАХ. УПРАВЛЕНИЕ ЗВУКОМ Глава 14. УПРАВЛЕНИЕ ЭКРАНОМ И ЗВУКОМ КОМПЬЮТЕРА 14.1. УПРАВЛЕНИЕ ЭКРАНОМ В ТЕКСТОВОМ РЕЖИМЕ Формирование изображения на экране персонального компьютера происходит с помощью дисплейного адаптера. В зависимости от установленного адаптера дис- плей может работать в текстовых или графических режимах. Во многих случаях возможности ввода-вывода данных с помощью стандартных процедур Read, ' Readln, Write, Writeln оказываются явно недостаточными для разработки удобных в использовании диалоговых программ. В Турбо Паскале предусмотрено несколько процедур и функций, значительно , увеличивающих возможности текстового ввода-вывода. Эти подпрограммы сосре- ; доточены в библиотечном модуле CRT, название которого происходит от Cathode < Ray Tube display — дисплей на электронно-лучевой трубке. 14.1.1. Назначение модуля CRT Стандартный модуль CRT является важнейшей частью интегрированной систе- мы Турбо Паскаль. Он устанавливает режим работы адаптера дисплея, организует прямой вывод в буфер экрана, регулирует яркость свечения символов и выполняет многие другие жизненно важные функции, необходимые для нормальной работы компьютера. Выделение средств, реализующих эти функции, в один модуль вызва- но требованиями унификации работы с экраном. Подключение модуля CRT к пользовательской программе осуществляется сле- дующим образом: uses CRT;
Управление экраном и звуком компьютера 337 С момента старта модуля CRT пользователю доступны все содержащиеся в нем стандартные средства. 14.1.2. Установка текстовых режимов Текстовые режимы служат для отображения символов кодовой таблицы персо- нального компьютера и характеризуются количеством символов в строке и строк на экране. Минимальной единицей управления является символ. Он строится из нескольких точек (пикселей), преобразование которых в символ происходит на ап- паратном уровне. Текстовые режимы поддерживаются всеми существующими ти- пами адаптеров. Установка текстовых режимов осуществляется стандартной процедурой TextMode (Mode :k integer) ; Возможные значения режима Mode указаны в табл. 14.1. Значение может быть задано именем константы (BW40.C040,...) или номером режима (0,1,...). Пример: TextMode(С080); TextMode(2); Таблица 14.1 Стандартные текстовые режимы персонального компьютера Монитор Экран Цвет Адаптер Сегмент Константа Номер режима Ч/Б 40x25 16/8 cga,ega B800 BW40 0 Цветной 40x25 16/8 CGAJEGA B800 СО40 1 Ч/Б 80x25 16/8 CGAJEGA B800 BW80 2 Цветной 80x25 16/8 CGAJEGA B800 СО80 3 Ч/Б 80x25 3 MA.EGA B800 Mono 7 В модуле CRT существует переменная LastMode размерности word, имеющая прямое отношение к текстовым режимам работы адаптера дисплея. Она содержит номер последнего установленного текстового режима и может быть использована как для проверки номера текстового режима. Write('Текстовый режим*, LastMode);, так и для возвращения из графического режима в текстовый: TextMode(LastMode) ; 'В модуле CRT имеется специализированная константа Font8x8=256, которая служит для инициализации расширенных текстовых режимов работы адаптеров дисплея. Это, как правило, относится к адаптерам дисплея EGA (Enchanced Graphics Adapter) и VGA (Virtual Graphics Array). Эти адаптеры позволяют работать в 43- и 50-строчных текстовых режимах. Различные их модификации дают возможность манипулировать строками экрана до 120 символов длиной. Для таких адаптеров при установке видеорежимов из па-
338 Глава 14 мяти машины загружаются текстовые шрифты с различными матрицами для сим- волов. Известны матрицы 8x8, 8x14, 8x16 для представления каждого символа на экране дисплея. Матрицу 8x14 поддерживают адаптеры MDA (Monochrom Display Adapter), EGA, VGA, а матрицу 8x8 поддерживают — CGA, EGA и VGA. Поэтому исполь- зование константы Font8x8 во время инициализации текстовых режимов работы может относиться только к трем последним адаптерам. Поскольку разрешающая способность адаптеров EGA и VGA выше, чем у CGA, то при стандартном тексто- вом режиме работы 80x25 у них в знакогенератор загружена таблица символов с матрицей 8x14. При загрузке текстового шрифта с матрицей размерности 8x8 мо- жет произойти установка расширенных текстовых режимов 80x43 и 80x50. Шриф- ты первоначально загружаются из памяти ПЗУ, где они располагаются на заранее известных фиксированных адресах. Во время сеанс^ работы пользователь может записать собственные шрифты в свободную память, а затем загрузить их в знакоге- нератор адаптера дисплея. В этом заключена основа русификации ориентирован- ной на английский язык компьютерной техники, так как она открывает возмож- ность отображения на экране символов кириллицы. Пример: TextMode(Font8x8+C080) — дополнительно к установке текстового режима в знакогенератор дисплея за- гружается шрифт из ПЗУ с матрицей размерности 8x8. 14.1.3. Очистка экрана Процедуры, входящие в эту группу, позволяют программисту управлять со- стоянием строк на экране и выполнять некоторые другие действия, относящиеся к работе с экраном. ClrScr — полностью очищает экран или текущее окно и помещает курсор в ле- вый верхний угол экрана (координаты 1,1). Выполняется только в текстовом режи- ме работы экрана. ClrEol — стирает все символы в строке, начиная с текущей позиции курсора до конца строки. DelLine — полностью стирает содержимое строки, в которой расположен кур- сор; все нижестоящие строки перемещаются на одну позицию вверх. InsLine — вставить пустую строку в позицию экрана, где расположен курсор; все нижестоящие строки перемещаются на одну позицию вниз. 14.1.4. Управление курсором Работа процедуры Write вызывает перемещение курсора по экрану дисплея. Естественно, результаты хаотического вывода только с помощью операторов Write практически не воспринимаются человеком.
Управление экраном и звуком компьютера 339 Для формирования хорошего восприятия экрана при выводе информации поль- зователь должен иметь средства направления курсора в любую позицию экрана. Для этого в Турбо Паскаль предназначены специальные средства. GoToXY (X,Y:byte) Процедура перемещает курсор в позицию, заданную координатами X (столбец) и Y (строка), относительно текущего окна (оконная система модуля CRT рассмат- ривается ниже). Если рассматривать экран как максимально возможное по размеру окно, то диапазон значений для Х= 1..80, для Y= 1..25, что соответствует текстово- му режиму работы адаптера дисплея 80x25. Верхний левый угол экрана имеет ко- ординаты (1,1), правый нижний — (80,25). Пример: ClrScr; {Очистить экран} GoToXY(33,4); {Переслать курсор в центр строки 4} Write(* Управляющая программа'); Функции WhereX: byte и WhereY: byte позволяют получить значение X- или Y- координаты курсора относительно текущего окна. Диапазон возвращаемых значе- ний определяется полным экраном (1..80 для X и 1 ..25 для Y) или размерами теку- щего окна. Пример: Write(’Курсор находится в столбце ',WhereX); Write('Курсор находится в строке ',WhereY); Упражнение 1. Составим программу, которая в разных текстовых режимах выводит текст, смещая его по диагонали, вставляет строку текста, удаляет две строки, а затем отсека- ет в каждой строке часть текста от курсора до конца строки. Для использования стандартных функций подключим в программу модуль Crt. Введем переменные: Row — номер строки, Col — номер столбца экрана, N — номер текстового ре- жима. Блок описания программы запишется так: uses CRT; var Row, Col, N : byte; В начале программы запишем запрос на ввод номера текстового режима и считаем его значение в переменную N, после этого процедурой TextMode(N) установим заданный ре- жим. Процедурой ClrScr очистим экран. Затем выведем сообщение о текстовом режиме. Установим начальную позицию курсора в 1-м столбце Col := 1. Вывод текста по диаго- нали экрана запишем с использованием оператора повтора for, параметром которого являет- ся переменная Row, изменяющаяся от 2 до 25. Оператор GoToXY(Col,Row) позиционирует курсор в точке на пересечении столбца Col и строки Row. Начиная с данной позиции, проце- дура Write выводит текст 'Вывод текста по диагонали’. После вывода текста значение пере- менной Col изменяется на 2, обеспечивая тем самым вывод следующей строки текста со смещением вправо на 2 столбца. Для замедления процесса вывода запишем вызов стандарт- ной процедуры Delay(lOO). Этот фрагмент программы запишется так:
340 Глава 14 Col := 1; for Row := 2 to 25 do begin GoToXY(Col,Row); Write('Вывод текста no диагонали'); Col := Col+2; Delay(100); end; Delay(200); Для того, чтобы вставить строку, позиционируем курсор процедурой GoToXY(8,10), а затем запишем вызов стандартной процедуры InsLine. Во вставленной строке выведем текст 'Вставленная строка текста'. * Затем удалим две строки, переведя курсор процедурой GoToXY(8,8) и дважды вызывая процедуру DelLine. Данный фрагмент программы запишется так: GoToXY(8,10) ; InsLine; Writeln('Вставленная строка текста'); Readln; GoToXY(8,8); DelLine; {Удаление строки} DelLine; {Удаление строки} Для демонстрации отсечения текста по диагонали установим начальную позицию Со1=6. Отсечение текста запишем с использованием оператора повтора for; его параметр из- меняет номер строки, в которой удаляется текст, от 2 до 25. Для удаления текста от позиции курсора до конца строки используем стандартную процедуру ClrEol. Можно в данную часть строки вывести сообщение 'ОтСеченце текста'. Текст этой части программы можно записать таким образом: Col:=6; for Row := 2 to 25 do begin GoToXY(Col,Row); ClrEol; {Отсечение no ClrEol} Write('Отселение текста'); Col := Col+2; Delay(100); end; В завершение работы программы позиционируем курсор в 10-й столбец и 25-ю строку, послр чего выведем сообщение и считаем нажатие клавиши Enter, записав вызов процедуры Readln. Полный текст программы будет записан следующим образом:
Управление экраном и звуком компьютера 341 program Demo_CRTl; uses CRT; var Row, Col, N : byte; begin Write('Введите номер текстового режима 0,1,2,3,5,7 > '); Readln(N); TextMode(N); ClrScr; Writeln('Включен текстовый режим № ' ,N) ; Col := 1; for Row := 2 to 25 do begin * GoToXX (Col,Row); Write('Вывод текста no диагонали'); Col := Col+2; Delay(100); end; Delay(200); {Задержка} GoToXX (8,10); InsLine; Writeln('Вставленная строка текста'); Readln; GoToXX(8,8); DelLine; {Удаление строки} DelLine; {Удаление строки} Col:=6; for Row := 2 to 25 do begin GoToXX(Col,Row); ClrEol; {Отсечение части строки no ClrEol} Write('Отсечение текста'); Col := Col+2; Delay(100); end; Writeln; GoToXX(10,25); Write('Для завершения работы нажмите клавишу <Enter>'); Readln; end.
342 Глава 14 Запустите интегрированную среду программирования, введите текст программы Demo CRTl и запишите файл на диск под соответствующим именем, а затем откомпили- руйте его и проверьте действие программы по шагам, наблюдая изменение состояние экра- на. 14.1.5. Вывод на цветной и монохромный экраны При работе в текстовых режимах с цветным дисплеем каждый выводимый на экран символ может иметь один из 16 (0 — 15) возможных цветов. Фон может быть один из 8 (0 — 7) цветов. Для установки цвета выводимых символов исполь- зуется процедура TextColor, для фона — процедура TextBackGround. Имеется воз- можность организации мерцания символов. При работе с монохромным дисплеем возможно* использование только двух цветов — черного и белого, но в языке . Турбо Паскаль имеется ряд дополнитель- ных возможностей для вывода на экран: • символы могут отличаться интенсивностью (яркостью)* свечения; • белые символы могут выводиться на черный фон, а черные символы — на бе- лый; • интенсивность свечения символов на экране может периодически уменьшать- ся и увеличиваться (это свойство называется мерцанием символов). Управление цветом. Установка определенного режима работы адаптера дис- плея, как можно было заметить из приведенных выше примеров, дает возможность установить сегментный адрес буфера видеопамяти. В большинстве случаев это ад- рес $В800:$0000 и только для адаптера МА он равен $В000:$0000. Текстовый ре- жим 80x25 поддерживает вывод на экран в 2000 (80*25 = 2000) символов. Для каждого символа в видеопамяти отводится два байта: 1. для самого симво- ла (первый) и 1 для атрибута цвета выводимого символа (второй). Следовательно, общий объем памяти буфера экрана будет 2000*2=4000 байт. Фактически (для удобства адресации) эта величина равна 4096 байт =4 Кбайт. Принимая во внимание, что координаты верхнего левого угла экрана — (1,1), а правого нижнего — (80,25), запишем формулу определения адреса смещения бай- тов символа и атрибута в области видеопамяти в зависимости от координат симво- ла: Смещение_байт_символа = (колонка—1)*2+(строка—1)*160 Смещение_байт_атрибута = Смещение_байт символа — 1 Если байт символа содержит значения от 0 до .255 и представляет собой один из 256 символов кодовой таблицы, то с байтом атрибута дёло обстоит сложнее. В представленной ниже табл. 14.2 показана структура байта атрибута цветно- сти для цветных текстовых режимов; черно-белые текстовые режимы можно рас- сматривать как подмножество цветных режимов.
Управление экраном и звуком компьютера 343 Таблица 14.2 Представление байта атрибута I Установка Биты Описание ' 7 Бит мерцания Цвет фона TextBackGround 6 Бит красного цвета 5 Бит зеленого цвета 4 Бит голубого цвета Цвет переднего плана TextColor 3 Бит яркости 2 Бит красного цвета 1 Бит зеленого цвета к 0 Бит голубого цвета Значение атрибута содержит цвет самого символа и цвет фона. Цвет символа занимает первые четыре бита (0,1,2,3) и допускает формирование 16 значений для цвета символа (0..15), что соответствует набору специальных констант модуля CRT: от Black (0) до White (15). Цвет фона определяется тремя битами (4,5,6), что позволяет формировать восемь значений: от Black (0) до LightGray (7). В соответ- ствии с диапазонами для символов и фона устанавливаются значения параметров стандартных процедур: TextColor(Color:byte) — установить цвет выводимых символов; TextBackGround(Color:byte) — установить цвет фона. Значения параметров могут задаваться как константами (например, Yellow, Red), так и их числовыми эквивалентами (например, 14,4), полный список которых приведен в таблице 14.3. Пример: TextColor(Yellow); ТехtBackGround(Red); Write('Желтые символы на красном фоне'); Таблица 14.3 Цветовая шкала Темные цвета Светлые цвета 0 (Black) — черный 1 (Blue) — синий 2 (Green) — зеленый 3 (Cyan) — голубой 4 (Red) — красный 5 (Magenta) — фиолетовый 6 (Brown) — коричневый 7 (LightGray) — светло-серый 8 (DarkGray)—темно-серый 9 (LightBlue) — светло-синий 10 (LightGreen) — светло-зеленый 11 (LightCyan) — светло-голубой 12 (LightRed) — светло-красный 13 (LightMagenta) — светло-фиолетовый 14 (Yellow) — светло-коричневый 15 (White) — белый
344 Глава 14 Установка одинакового цвета для символов и фона приводит к тому, что выво- димый на экран текст становится невидимым. Возможно и реверсное изображение: для этого меняется местами Шет фона и символа. Для предыдущего примера ре- версное изображение получается следующим набором команд: TextColor(LightRed); TextBackGround(Brown); Write(* Реверсный вывод *); Здесь мы использовали нормализацию цветности. Поскольку значение Yellow = 14, то соответствующий фон будет Brown (Brown = Yellow—8), a LightRed = Red+8. Чтобы добавить при выводе эффект мерцания, при установке цвета указы- вается константа Blink (или 16). Мерцание будет поддерживаться до тех пор, пока не будет задана установка цвета выводимых символов без мерцания. Пример: TextColor(Yellow+Blink); Write('Мерцающие желтые символы'); TextColor(12+16); Write('Мерцающие светло-красные символы'); TextColor(6); Write('Не мерцающие коричневые символы'); При работе в черно-белых текстовых режимах значение битов цветности такое же, как и для цветных, но определяющее значение здесь имеют черный (Black или 0), белый (White или 15), светло-серый (LightGray) цвета и константа Blink. Ис- • пользуя их различные сочетания, можно добиться неплохих зрительных эффектов. Процедуры TextColor и TextBackGround связаны с переменной TextAttr (раз- мерность 1 байт), значение которой устанавливается системой при инициализации модуля CRT и может изменяться этими процедурами. Переменная TextAttr содер- жит текущие значение атрибута цветности. После того как процедуры TextColor и TextBackGround установили значение TextAttr, любой вызов процедуры Write при- ' водит к последовательной записи в буфер экрана каждого символа выводимой i строки вместе с байтом TextAttr. В результате на экране появляется соответст- : вующее изображение. Установка значения переменной TextAttr дает тот же эф- фект, что и совместное использование процедур TextColor и TextBackGround. На- ; пример, следующие фрагменты программ выполняют одно и то же действие: TextColor(Yellow+Blink); TextAttr:=Yellow+Blink +Red shl 4; TextBackGround(Red); Управление яркостью. Для управления яркостью используются стандартные процедуры LowVideo, NormVideo, HighVideo. Действие всех трех процедур сводит- ся к установке соответствующего значения переменной TextAttr.
Управление экраном и звуком компьютера 345 LowVideo устанавливает режим минимальной яркости свечения выводимых на экран символов. Режим действует до его отмены, например, процедурой NormVideo. Действие процедуры эквивалентно оператору TextAttr : = TextAttr AND $F7; который устанавливает бит яркости в байте атрибута в 0. Пример: \ LowVideo; Write ('MS-DOS 4.01'); Предложение "MS-DOS 4.01" будет выведено на экран с минимальной ярко- стью свечения. NormVideo — устанавливает режим нормальной яркости свечения выводимых на экран символов; р&ким является стандартным и действует до его отмены. Ре- жим, аналогичный NormVideo, устанавливается автоматически при инициализации модуля CRT. Пример: LoWVideo; Write ('MS-DOS 4.01'); NormVideo; {Отменяет режим LowVideo} Write ('Операционная система'); Предложение "Операционная система" выводится в режиме нормальной ярко- сти свечения. HighVideo устанавливает режим максимальной яркости свечения, действует до его отмены. Процедура действует подобно оператору TextAttr:= TextAttr OR $08; Пример: LoWVideo; Write ('MS-DOS 4.01'); NormVideo; Write ('Операционная система '); HighVideo; {Отменяет режим NormVideo} Write ('Для персональных компьютеров'); Предложение "Для персональных компьютеров" выводится с максимальной яркостью свечения. Некоторые модели терминалов не распознают сигналы интенсивности, в них результаты работы процедур LowVideo, NormVideo и HighVideo не будут отли- чаться. 14.1.6. Работа с буфером экрана Процедура Write может выводить информацию на экран в двух режимах. Пер- вый стандартный режим использует для вывода прерывание $10, а именно функ- цию вывода символа на экран. Таким образом, любая информация для вывода пре-
346 Глава 14 образуется в поток символов, которые выводятся по одному с текущим атрибутом TextAttr, начиная с позиции, где расположен курсор. Для вывода каждого символа происходит вызов прерывания $10. Фактически символ и атрибут цветности зано- сятся по соответствующему адресу в видеопамять и автоматически отображаются на экране дисплея. Отрабатывается цепочка: вызов Write — прерывание int 10h — загрузка символа и атрибута в буфер экрана — автоматическое отображение на эк- ране. Итак, можно значительно уменьшить время вывода, загружая информацию на- прямую в буфер дисплея, т. е. реализовать цепочку: загрузка в видеопамять — ав- томатическое отображение на экране. Проще всего организовать это с помощью предопределенного массива Мет для доступа к любому байту памяти по указанно- му адресу. В качестве примера приведем два фрагьЛнта программ для адаптера МА: TextColor(Black); TextBackGround(LightGray); TextAttr:-LightGray ahi 4 + Black; Mem[$B000:$0000]:=Ord('A'); GoToXY(1,1); Write('A')'; Mem[$B000:$0001]:-TextAttr; Результат работы обоих фрагментов — вывод в первую позицию первой стро- ки буквы ’А' черного цвета на светлый фон. По одному символу трудно судить о скорости вывода. Если же выводится большое количество символов, ясно видно, что второй способ работает значительно быстрее. В связи с этим в модуль CRT введена стандартная булевская переменная DirectVideo, которая при DirectVideo = True показывает что процедура Write, производит вывод прямо в буфер экрана, без использования прерывания $10. Сравните скорость вывода двух следующих про- грамм: uses CRT; var I:byte; begin ClrScr; DirectVideo:-False; for X:- 1 to 24 do Write('Вывод с прерыванием'); end. uses CRT; var X:byte; begin ClrScr; DirectVideo:—True; for X:- 1 to 24 do Write('Вывод в буфер *); end. Программа, расположенная справа, значительно быстрее заполнит экран, чем расположенная слева. Возникает вопрос: Зачем вообще пользоваться выводом на экран при DirectVideo=False? При инициализации модуля CRT значение DirectVideo автоматически устанав- ливается в True, но стандарты персональных компьютеров требуют, чтобы подпро- грамма обработки прерывания $10 находилась в ПЗУ и учитывала все особенности и характеристики конфигурации каждого конкретного компьютера.
Управление экраном и звуком компьютера 347 На практике это приводит к тому, что даже если изготовитель выпускает ком- пьютеры, отличные по параметрам от типовых (например, буфер экрана может на- чинаться не с $В000 или $В800, а с другого адреса). Можно быть уверенным: по прерыванию $10 будут выполняться те же функции, что и на стандартных маши- нах. При использовании прерывания $10 пользователю совсем не обязательно знать адрес буфера видеопамяти, и его программа будет безошибочно работать на любом компьютере. Использование DirectVideo = False на 100 % гарантирует со- вместимость программных продуктов с компьютером самой различной архитекту- ры. Для повышения эффективности работы программ, в частности для обеспечения быстрой сменяемости поколений экранов и восстановления нужного в данный мо- мент экрана, удобно использовать буфер 'экрана. Это можно сделать несколькими способами. Первый способ предполагает описание буфера экрана с помощью ABSOLUTE с последующим сохранением его в массиве. program DemoScrBufferl; uses CRT; type Scr = array[1..4000] of byte; var Screen: Scr absolute $B800 : $0000; Savel: Scr; Ch: char; begin ClrScr; GoToXY(20,10) ; Write (•********** ЭКРАН N 1 *************•); Savel:= Screen; {Сохранение буфера экрана в Savel} Delay(5000); {Пауза в пять секунд} ClrScr; GoToXY(20,10); Write (********•** ЭКРАН N 2 *************1); Delay(5000); Screen:= Savel; {Восстановление сохраненного в Savel содержи- мого буфера} end. Тот же результат можно получить с помощью процедуры Move, которая копи- рует указанное количество байтов непосредственно в памяти. Для этого операторы Savel:= Screen; и Screen= Savel; нужно заменить на Move(Screen, Savel, 4000); и Move(Savel, Screen, 4000); соответственно. Применение этих двух способов не все- гда эффективно, так как при большом количестве поколений экрана расходуется много памяти, и ее может просто не хватить.
348 Глава 14 Третьим способом сохранения и восстановления экрана является использова- ние динамических переменных. Рассмотрим программу, выполняющую те же функции, что и предыдущая, но для сохранения содержимого буфера экрана использующую указатели. Этот способ позволяет создавать до 100 поколений экранов, каждое из которых можно мгновен- но восстановить. program DemoScrBuffer2 ,* {Сохранение буфера экрана для PC АТ} uses Crt; type Scr = array [1..4000] of byte; var Screen : Scr Absolute $B800 : $0000; k Savel : AScr; Ch : char; begin New(Savel); {Выделение памяти в куче для буфера} ClrScr; GoToXY(20,10); Write (•************ ЭКРАН N 1 **************•); Savel*:= Screen; {Сохранение содержимого буфера} Delay(5000); ClrScr; GoToXY(20,10); Write (•************ ЭКРАН N 2 *************•); Delay(5000); Screen:= Savel*; {Восстановление буфера экрана} end. Эффект "Снег". Рассмотрим пример. Пусть имеется цветной графический адаптер CGA (в России он устанавливается на персональных компьютерах ЕС 1840.05/1841). Выполним две процедуры, каждая из которых заполняет экран символом 'А': program DemoSnow; uses CRT; const CFF : word — 0; begin ClrScr; TBoctAttx:=LightGray shl 4 + Black; ahile CFF < 4000 do begin MbuW[$B800:CES1 :=^DextAttr SHL + ord ('A') ; Inc (CFF, 2) ; «nd; «nd. program DemcNoSnow; uses CRT; var CFF : word; begin ClrScr; DiructVideo:- True; TextAttr:” Li^itQray SHL 4+ Black; for CFF:- 1 to 2000 do Write ('A') «nd.
Управление экраном и звуком компьютера 349 Оба варианта достаточно быстро заполняют экран. Однако первый вариант при несколько более высокой скорости вывода вызывает искажения экрана, называемое "снегом". "Снег" прекращается сразу после вывода последнего символа. При вы- полнении второй программы "снег" не наблюдается благодаря не используемой яв- но в программе булевской переменной CheckSnow. При прямом выводе в буфер видеопамяти она автоматически принимает значение True и активизирует средст- ва, блокирующие появление "снега". При работе с базовой системой ввода-вывода переменная CheckSnow не используется и соответственно "снег" сопровождает весь процесс вывода на экран. Из всех известных авторам адаптеров дисплея толь- ко CGA вызывает явление "снега", что связайо с особенностями его технической реализации. Во второй программе переменная CheckSnow явно не указывалась, но если изменить ее установленное по умолчанию значение на False: CheckSnow:= False; то это неминуемо вызовет "снег", хотя и увеличит скорость вывода. 14.1.7. Текстовые окна Модуль CRT поддерживает возможность в любой момент работы программы использовать для вывода не весь экран, а определенную его часть, которая называ- ется окном. Величина окна определяется пользователем, но не может превышать размера экрана. Для организации окон в языке Турбо Паскаль используется проце- дура Window(XI,Y1,X2,Y2), где XI, Y1 — координаты левого верхнего, а Х2, Y2 — координаты правого ниж- него угла окна. Размер максимального окна (полный экран) — (1,1,80,25), мини- мального — один столбец на одну строку. Пример: Window(1,1,80,25); {Окно — полный экран} Window(19,7,59,16); {Окно в центре экрана} После активизации процедуры Window модуль CRT формирует две специаль- ные переменные WindMin и WindMax, в которых фиксируются размеры текущего окна. Значения этих переменных можно использовать в программе для формирова- ния оконных систем. Например, для получения X- и Y-координат верхнего угла те- кущего окна можно использовать выражения: X := Lo (WindMin) ; Y : = Hi (WindMin) ; Аналогичным образом можно получить координаты правого нижнего угла те- кущего окна. Особенности архитектуры модуля CRT требуют при работе с WindMin и WindMax начинать отсчет координат не с (1,1), а с (0,0), что необходи- мо учитывать при их практическом использовании. На экране могут находиться несколько окон, но в каждый отдельный момент времени активным может быть только одно окно. Процедуры и функции Writeln,
350 Глава 14 Write, Readln, Read, Window, GoToXY, ClrScr, InsLine, DelLine, WhereX, WhereY выполняют соответствующие их назначению действия относительно текущего ок- на. Окна часто используются в программах для скролинга (прокрутки) текстовой информации. Этот процесс проще всего реализовать с помощью процедур InsLine и DelLine. Упражнение 2. Изучите программу Dem_Crt2, которая выводит на экран окно с изме- няющимся цветом фона и цветом текста. program Dem_Crt2; uses CRT; var I,J: integer; begin TextBackGround(l); {Фон только выбран, закраски пока нет} ClrScr; {Очистка и закраска фона экрана} TextColor(14); {Установить цвет текста} GoToXY(15,3); Writein(* Пример окна с изменяющимся цветом фона и текста'); for 1:= 0 to 7 do begin Window(5,5,75,20) ; {Раскрыть окно} TextBackGround(I); for j:=0 to 15 do begin ClrScr; {Очистка и закраска окна} TextColor(14); GoToXY(25,3); Writein('Цвет фона = ',!); TextColor(j); {Выбор цвета для вывода} GoToXY (25,7); Writein('Цвет текста =',J); TextColor(14); GoToXY(15,12); Write(* Для продолжения работы нажмите клавишу *); Write('<Enter>'); Readln; end; end; end. Обратите внимание на применение в данной программе процедур TextBackGround, TextColor и Window. Запустите интегрированную среду программирования, введите текст
Управление экраном и звуком компьютера 351 программы Demo_CRT2 и запишите файл на диск под соответствующим именем, а затем от- компилируйте его и проверьте действие программы по шагам, наблюдая изменение состоя- ние экрана. Упражнение 3. Изучите программу, которая выводит на экран окно увеличивающегося размера с синим фоном, затем с паузой 30 мс выводит в него 200 символов случайного цвета в точки экрана со случайными координатами, печатая в нижней строке экрана коорди- наты точки красным цветом. После этого на экране раскрывается окно с черным фоном и в нем выводятся случайные символы светло-зеленого цвета. Вывод символов прекращается, как только будет нажата любая клавиша, после чего устанавливается черный цвет фона и экран очищается. program Demo_Crt3; uses CRT; * var I,X1,Y1,X2,Y2: integer; begin TextBackGround(Black); {Установить черным цвет фона и очистить экран} ClrScr; XI:= 39; Yl:= 12; {Координаты для минимального окна} Х2:= 40; Y2:= 13; TextBackGround(Blue); {Установить синий цвет фона} for 1:= 0 to 12 do {Повторять вывод окна с изменяющимися размерами} begin Window(XI,Y1,X2,Y2); {Задание текущего окна} ClrScr; {Очистка и закраска текущего окна} Delay(100); XI:= XI—3; Yl:= Yl—1;{Изменение координат точки XI,Y1} Х2:= Х2+3; Y2:= Y2+1; {Изменение координат точки X2,Y2} Window(1, 1, 80, 25) ; end; Randomize; for I:=l to 200 do {Вывести 200 символов "*"} begin TextColor(LightCyan); GoToXY(Random(80),Random(25)); {Задать координаты точки} Xl:= WhereX; Yl:= WhereY; TextColor(Random(15)+Blink) ; {Задать случайный номер цвета с мерцанием} Write('*');
352 Глава 14 TextColor(LightCyan); GoToXY(70,25);{Перевести курсор в 70-й столбец 25-й строки} TextColor(LightRed); Write(XI:3,Y1:3); {Напечатать координаты точки} Delay(30); end; Window(50,3,75,15); {Раскрыть окно} TextBackGround(Black); {Установить черный цвет фона и очистить окно} ClrScr; TextColor(LightGreen); {Установить светло-зеленый цвет текста} repeat {Повторять} * GoToXY(Random(25),Random(14)); Write(Chr(Random(255) )); {Печатать символ, номер которого — случайное число} Delay(20); until KeyPressed; {Завершить цикл, так как нажата клавиша}. TextBackGround(Black); {Установить черный цвет фона и очистить экран} ClrScr; end. Изучая текст программы, обратите внимание на применение функций WhereX и WhereY, задание мерцания символа, изменение координат вершин окна. Запустите интегри- рованную среду программирования, введите текст программы Demo_CRT3 и запишите файл на диск под соответствующим именем, а затем откомпилируйте его и проверьте действие программы, исполняя ее по шагам и наблюдая изменение состояние экрана. Упражнение 4. Изучите текст программы, которая создает и обслуживает вертикальное меню. program Dem_Menu; uses Dos, Crt; const nMenu=4; {Количество строк в меню} var Ch : char; {Код последней нажатой клавиши} Fun_Key : boolean; {Нажата ли спецклавиша} Str_Menu : array[0..nMenu—1] of string; {Массив строк меню} Text_Color : byte; {Цвет текста меню} Back_Color : byte; {Цвет фона строки меню} Sel_Text_Color : byte; {Цвет текста выбранного пункта меню} Sel_Back_Color : byte; {Цвет фона выбранного пункта меню} Menu_Back_Color : byte; {Цвет фона меню}
Управление экраном и звуком компьютера 353 Screen_Back_Color : byte; {Цвет фона экрана} Border_Back_Color : byte; {Цвет фона рамки} Border_Color : byte; {Цвет рамки} Width : byte; {Ширина меню} Wx : byte; {Координата х левого верхнего угла меню} Vty : byte; {Координата у левого верхнего угла меню} Dy : byte; {Интервал между строками меню} Сгж : byte; {Номер подсвеченного пункта меню} {Процедура Hide_Cursor делает невидимым курсор. В.процедуре приме- няются переменные типа Registers, значения которых используются стандартной процедурой Intr.} procedure Hide_Ci^rsor; {Скрыть курсор} var Regs : Registers; begin with Regs do begin Ah:=l; Ch:=$20; Cl:=0; Bh:=0; Intr($10,Regs); end; end; {Процедура Box рисует на экране рамку. Изменив коды, можно изме- нить рисунок рамки} procedure Box(XI,Y1,X2,Y2:integer); {Нарисовать рамку меню} var I,J : integer; begin GoToXY(X1+1,Y1); for I:=X1+1 to X2-1 do Write(#196); GoToXY(X1+1,Y2); for I:=X1+1 to X2—1 do Write(#196) ; for I:=Y1 to Y2-1 do begin GoToXY(XI,I) ; Write(#179) ; end; for I:=Y1+1 to Y2-1 do begin GoToXY(X2,1) ; 12—116
354 Глава 14 Write(#179); end; GoToXY(XI,Y1); Write(#218); GoToXY(X2,Y1); Write(#191); GoToXY(X2,Y2); Write(#217); GoToXY(XI,Y2); Write(#192); end; {Процедура Draw_Menu очищает экран, * вызывает процедуру Hide_Cursor, которая делает курсор невидимым, затем на экран вы- водится изображение меню. Для рисования рамки меню вызывается про- цедура Box} procedure Draw_Menu; {Вывод меню на экран} var I,J : integer; begin TextBackGround(Screen_Back_Color); ClrScr; Hide_Cursor; TextColor(Border_Color); TextBackGround(Border_Back_Color); Box (Wx—2 ,Wy-2 ,Wx+Width,Wy+ (nMenu-1) *Dy+2) ; TextBackGround(Menu_Back_Color); I:=Wx—1; for J:=Wy—1 to Wy+(nMenu—1)*Dy+l do for I:-Wx-1 to Wx+Width—1 do begin GoToXY(I, J) ; Write('’); end; for I:=0 to nMenu—1 do begin if I=Crm then begin TextBackGround(Sel_Back_Color); TextColor(Sel_Text_Color) ; end else
Управление экраном и звуком компьютера 355 begin TextBackGround(Back_Color); TextColor(Text_Color); end; GoToXY(Wx,Wy+I*Dy); Write(Str_Menu[ I ]) ; end; end; {Процедура Init устанавливает значения для переменных, хранящих цвета текста и фона: размеры меню, координаты положения меню на экране и текст, содержащийся в строках меню} procedure Init; t{ Начальная установка значений переменных} begin Text_Color := Black; Back_Color := LightGray; Sel_Text_Color := White; Sel_Back_Color := LightRed; Menu_Back_Color := Blue; Screen_Back_Color := LightGray; Border_Back_Color := Blue; Border_Color := White; Width := 25; Wx := 15; Wy := 5; Dy := 1; Сгж := 0; Str_Menu[0]:='Первая строка меню'; Str_Menu[1]:='Вторая строка меню'; Str_Menu[2]:='Третья строка Меню'; Str_Menu[3]:='Четвертая строка меню'; end; {Процедура New_Menu получая параметры-значения Old — старый номер подсвеченной строки меню и Sei — новый номер подсвеченной строки, обновляет изображение меню на экране за счет печати строки меню со старым номером цветам Text_Color на фоне Back_Color, а строки меню с новым номером цветом Sel_Text_Color на фоне цветом Sel_Back_Color.} procedure New_Menu(Old,Sei:integer); {Обновление меню} begin TextBackGround(Sel_Back_Color); TextColor(Sel_Text_Color);
356 Глава 14 GoToXY (Wx, Wy+Sei *Dy) ; Write(Str_Menu[Sel]); TextBackGround(Back_Color); TextColor(Text_Color); GoToXY(Wx,Wy+Old*Dy); Write(Str_Menu[Old]); end; {Процедура Up осуществляет реакцию на нажатие клавиши со стрелкой вверх. При этом она уменьшает на 1 значение переменной Сип, указы- вающей номер активного пункта меню и вызывает процедуру New_Menu для обновления меню на экране} procedure Up;{Реакция на нажатие клавиши со стрелкой вверх} var Old : byte; begin Old:=Crm; Crm:=Crm—1; if Crm=—1 then Crm:=nMenu—1; New_Menu(Old,Crm); end; {Процедура Down осуществляет реакцию на нажатие клавиши со стрел- кой вниз. При этом она увеличивает на. 1 значение переменной Сип, указывающей номер активного пункта меню и вызывает процедуру New_Menu для обновления меню на экране} procedure Down; {Реакция на нажатие клавиши со стрелкой вниз} var Old : byte; begin Old:=Crm; Crm:=Crm+l; if Crm=nMenu then Crm:=O; New_Menu(Old,Crm); end; {Процедура Do_Select получает параметр-значение — номер выбранного пункта меню и имитирует действие программы при выборе данного пункта. В реальной программе в этой процедуре нужно будет органи- зовать вызов разных процедур в зависимости от выбранного пункта меню} procedure Do_Select(N:integer) ; {Активизация действия после выбо- ра строки меню} begin GoToXY(40,1); TextColor(N);
Управление экраном и звуком компьютера 357 Write('Выполняется задание из пункта ’ ,N+1) ; end; {Процедура Do_Conunand, получив параметры-значения Key и Fun_Key, выполняет проверку клавиши и, если была нажата клавиша Enter (код #13), то передает управление процедуре Do_Select, указывая ей но- мер выбранного пункта меню. Если была нажата клавиша со стрелкой вверх или вниз, то вызываются процедуры Up или Down соответствен- но. } procedure Do_Command(Key:char; ^un_Key:boolean); {Выполнить дей- ствия) begin if (not Fun_Key) or (UpCase(Key)=#13){Если не спецклавиша и на- жата Enter) t then Do_Select (Спи) ; if Fun_Key then {Если спецклавиша) case Upcase(Key) of #72 : Up; {Нажата стрелка вверх — вызвать процедуру) #80 : Down; {Нажата стрелка вниз ...} end; end; {Основная программа. После инициализации переменных и появления изображения меню на экране выполняется основной цикл: считать код нажатой клавиши — обработать его. Цикл выполняется до тех пор, по- ка не нажата клавиша ESC (код #27) . Для обработки вызывается про- цедура Do_Conunand, которой в качестве параметров передаются код клавиши и флаг (логическая переменная Fun_Key), указывающий, явля- ется ли эта клавиша специальной.} begin {Начало основной программы) Init; Draw_Menu; repeat {Повторять} Ch:=ReadKey; if Ch=#0 then {Нажата спецклавиша) begin Fun_Key:=True; Ch:=ReadKey; end else Fun_Key:=True; Do_Command(Ch,Fun_Key); {Выполнить действия) until (Ch=#27) {Прекратить цикл, как только нажата ESC) end.
358 Глава 14 Обратите внимание на применение подпрограмм-процедур. Запустите интегрирован- ную среду программирования, введите текст программы Dem Menu и запишите файл на диск под соответствующим именем, а затем откомпилируйте его и проверьте действие про- граммы, выбирая различные пункты меню. 14.2. УПРАВЛЕНИЕ ЗВУКОМ 14.2.1. Общие сведения Для создания звуковых эффектов в Турбо Паскале используются стандартные процедуры Sound, NoSound и Delay модуля CRT. Sound(I: word) — активизирует звуковые средства персонального компьютера. Целочисленное значение I указывает частоту звучание звука в герцах. Звук указан- ной частоты будет генерироваться до тех пор, пока не будет отменен процедурой NoSound. NoSound — отмена звука. Отменяет звуковой режим, заданный процедурой Sound. На некоторых типах персональных компьютеров (например, ROBOTRON СМ 1910) процедура Sound аппаратно не поддерживается и реализация звуковых эф- фектов невозможна. Для указания времени, в течение которого будет продолжаться звучание, ис- пользуется процедура Delay. Пример: begin Sound(500); Delay(2000) ; NoSound end; B данном примере звуковой сигнал частотой 555 Гц будет звучать в течение 2 с (2000 мс). С помощью процедур Sound, NoSound, Delay и операторов цикла можно соз- дать самые разнообразные звуковые эффекты: звучание сирены, метронома, бу- дильника, пение птиц, фрагменты музыкальных произведений и т.д. Для этого ис- пользуется набор частот или элементы массива, соответствующие нотам различных октав (табл. 14.4.). Таблица 14.4 Частота звучания йот Нота Большая октава Малая октава Первая октава Вторая октава До 130.81 261.63 523.25 1046.50 Ре 146.83 293.66 587.33 1174.07 Ми 164.81 329.63 659.26 1318.05
Управление экраном и звуком компьютера 359 Продолжение Нота Большая октава Малая октава Первая октава Вторая октава Фа 174.61 349.23 698.46 1396.09 Соль 196.00 3.92.00 784.99 1568.00 Ля 220.00 440.00 880.00 1760.00 Си 246.94 493.88 987.77 1975.00 Для использования в процедуре Sound все указанные в таблице значения час- тот округляются. 14.2.2. Генерация мелодий Одним из способов построения мелодичных звуковых рядов является исполь- зование частот, соответствующих нотам. Частоты загружаются в один массив, про- должительность звучания каждой частоты — в другой. Примером может служить процедура, генерирующая гамму с нарастающей продолжительностью звучания ка- ждой ноты. procedure Gamma; const: {Ноты} М: array[1..7] of integer =(262,294,330,349,392,440,494); {Продолжительность звучания} Т: array[1..7] of integer =(10,11,12,13,14,15,16); var I: byte; begin while not KeyPressed do begin for I: = 1 to 7 do begin Sound(M[I]) ; Delay(T[I]); NoSound end; end; end; Меняя значения элементов массивов М и Т, можно добиться довольно хорошей имитации музыкальных произведений. Рассмотрим пример программы, позволяющей воспроизвести простую музы- кальную гамму (частоты всех полутонов первой октавы записаны в массив F). program gamma; uses Crt; const
360 Глава 14 F : array [1..12] of real =(130.8,138.6,146.8,155.6,164.8, 174.6,185.0,196.0,207.7,220.0,233.1,246.9); Temp = 150; var K,N : integer; begin for K:=0 to 3 do for N:=l to 12 do begin Sound (Round (F [N] * (1 shl к ))); Delay(Temp); NoSound; * * end; for K:=3 downto 0 do for N:=12 downto 1 do • begin Sound(Round(F[N]*(1 shl к ))); Delay(Temp); NoSound; end; end. На основе этого примера вы можете создавать фрагменты программ, обеспечи- вающие звуковое сопровождение ваших программ. Обратите внимание на то, что при переходе от одной октавы к соседней частоты изменяются в два раза. 14.2.3. Звуковое сопровождение процесса вывода Весьма впечатляет вывод информации, сопровождаемый звуковыми эффекта- ми. Он применяется при формировании заставок, подсказок, сообщений об ошиб- ках и т. д. Например, пусть требуется вывести в центр экрана элемент заставки 'ЭКСПЕРТНАЯ СИСТЕМА' в сопровождении звукового сигнала. Генерацию сигнала оформить в процедуре • Zvuk: procedure ZvukOut; var St: string[40]; I: byte; procedure Zvuk; begin Sound(5000) ;
Управление экраном .и звуком компьютера 361 Delay(70); NoSound end; {Zvuk} begin ClrScr; St: = 'ЭКСПЕРТНАЯ СИСТЕМА'; GoToXY(18,12); for I:= 1 to Length(St) do begin Write(St[I]); Zvuk end * end; Кроме сопровождения вывода строковых и любых других данных в ряде типов программ (например, игровых) требуется формирование более сложных звуков: криков животных, шума автомобильных или авиационных двигателей и т.д. Для организации подобных имитаций требуется определенный навык и опыт работы. Звуковое сопровождение этапов выполнения программы применяется как дополнительное средство для контроля решения задачи на персональном компью- тере. Во время длительного счета на одной машине пользователь может работать на другом компьютере и следить за работой первой по звуковым сигналам. Звуко- вое сопровождение может носить самый разнообразный характер: от определенно- го количества элементарных сигналов типа AG до имитации человеческого го- лоса. В качестве примера приведем программу, в которой после выполнения каждо- го из трех блоков на экран выводится сообщение и выдается один или несколько звуковых сигналов. Количество сигналов равно номеру отработанного блока, program DemoSignal; procedure Signal(N: byte) ; var I: byte; begin , for I:= 1 to N do begin Write(AG); Delay(500) end end; begin {Основная программа) Write('Отработал блок 1');Signal(1);
362 Глава 14 Write('Отработал блок 2') ;Signal(2); Write('Отработал блок 3')/Signal(3) end. Заменив в процедуре Signal оператор WriteCXj) на имя процедуры, генерирую- щий звуковой ряд, можно добиться повторения его звучания нужное количество раз. Упражнение 5. Изучите программу Dem Music, которая имитирует разные звуки. program Dem_Music; i uses Crt; {Процедура генерации звуков Bird получает из основной программы три параметра-значения. В зависимости от селектора KindOfBird, ко- торому при вызове процедуры Bird присваивается целочисленное зна- чение от 1 до 4, а также параметров-значений Duration и Pitch, ге- нерируются различные звуки} procedure Bird(KindOfBird : Byte; Duration : Word; Pitch : Integer); var I, J, К : Integer; begin I := 1; case KindOfBird of 1 : begin while I < Duration do begin Sound(Pitch + I); Inc(I); end; NoSound; end; 2 : begin J := I + (I div 10); while I < Duration do begin Sound(Pitch + I); Inc(I); end; NoSound;
Управление экраном и звуком компьютера 363 Delay(100); while I > J do begin Sound(Pitch + I); Dec(I); end; NoSound; end; 3 : begin К := 1; while К < Random(20) + 10 do begin Sound(Pitch); Delay(30); Sound (Pitch -I- (Pitch div 10.)); Delay(30); Inc(K); end; NoSound; end; 4 : begin I := Random(5) + 10; К := Pitch + (Pitch div 10); while I > 1 do begin J := Pitch; . while J < К do begin Sound (J) ; Delay(6); Inc(j, 10); end; NoSound; Delay(40) ; Dec(I); end; end; end; ' {Case} end;
364 Глава 14 {Основная программа. Пока не нажата любая клавиша, выполняется вы- зов процедуры Bird с параметрами-значениями, величина которых за- дается с использованием функции Random. После нажатия на любую клавишу цикл завершается, звуковой режим отменяется, и программа завершает работу.} begin Randomize; repeat: {Повторять, пока не нажата любая клавиша} Bird(Succ(Random(4)),Random(2000)+300,Random(4000)+ 200); Delay(Random(400) + 20); until KeyPressed; NoSound; end. * Обратите внимание на применение стандартных процедур Sound, NoSound, Delay, Dec, Inc, функций Random, Succ. Запустите интегрированную среду програм- мирования, введите текст программы Dem_Music и запишите файл на диск под со- ответствующим именем, а затем откомпилируйте его и проверьте действие про- граммы. Контрольные вопросы и задания Вопросы. 1. Каково назначение модуля CRT? Как подключить данный модуль к программе? 2. Какой стандартной процедурой осуществляется установка текстовых режимов? 3. ’Общие и отличительные черты процедур ClrScr, ClrEol, DelLine и InsLine. 4. Назначение процедуры GoToXY, функций WhereX и WhereY. 5. Какова структура байта атрибута цветности для цветных текстовых режимов? 6. Каково назначение переменной TextAttr? Как влияют на ее значение процедуры TextColor и TextBackGround? 7. Как задается эффект мерцания выводимых символов? 8. Какие процедуры используются для управления яркостью? 9. Какой эффект дает использование буфера дисплея для вывода информации? 10. Какая процедура используется для организации окон в Турбо Паскале? Каково на- значение специальных переменных WindMin и WindMax? 11. Перечислите назначение стандартных процедур Sound, NoSound и Delay модуля CRT? 12. Какая типичная конструкция используется при организации звуковых эффектов? 13. Каков будет результат выполнения следующей программы: program music; uses Crt;
Управление экраном и звуком компьютера 365 begin Sound (12300) ; Delay(500) ; NoSound; end. Задания. 1. Заполните экран 1600 случайными числами в диапазоне 0...9 стандартным белым цветом на красном фоне ярко-зелеными буквами. Установите зеленый фон и выведите еще 1600 случайных чисел желтым цветом. Не забудьте подключить модуль CRT, так как вам понадобятся экранные процедуры. 2. Покажите восемь возможных фонов экрана задержкой в три с. В левый верхний угол экрана выведите номер текущего цвета. 3. Выведите на экран 16 случайных чисел 16-ю разными цветами на черный экран. 4. Выведите на предварительно очищенный экран строку 'ЯРКИЙ И ТУСКЛЫЙ' таким образом, чтобы первое слово было ярким, 'и' — менее ярким, а последнее слово—тусклым. 5. Выведите на предварительно очищенный экран строку 'Для продолжения работы на- жмите клавишу Enter’ таким образом, чтобы последнее слово ярко выделялось по сравне- нию с другими словами предложения. 6. Выведите 200 символов '*' случайного цвета в точки экрана со случайными коорди- натами. Фон экрана — черный. 7. Постройте окно цвета LightRed и выведите в него две строки 'БАЗА ДАННЫХ' и 'СРЕДНЕЙ ШКОЛЫ' черного цвета. Сделав паузу в три с, очистите экран черного цвета. Обратите внимание, что для перехода от окна к полному экрану надо обязательно вы- полнить оператор Window(l,l,80,25). Эта программа — подход к выводу данных в различ- ные места экрана и первый шаг к созданию заставок, содержащих название и назначение программы. 8. Постройте два окна по верхним углам экрана и выведите в каждое сообщение 'Окно Г, 'Окно 2'. Сделайте паузу три с и очистите экран. 9. У левого края экрана создайте окно цвета LightGreen и переместите его к правому краю экрана, имитируя движение с шагом в одну позицию. 10. Постройте "взрывающееся" (т. е. постепенно увеличивающееся от минимального к максимально возможному для вашего экрана) окно цветом Magenta. Шаг для Х-координат = 3, для Y-координат = 1. 11. Постройте "сходящееся" (т.е. постепенно уменьшающееся от максимально возмож- ного к строке толщиной 1 символ) окно цветом Magenta. 12. Постройте два окна (цветами Blue и Green) и подпишите их 'ЧЕТНЫЕ', 'НЕЧЕТНЫЕ' желтым цветом. С клавиатуры в цикле в строке 25 вводите цифры от 1 до 9. Если число четное, выводите его цветом LightRed в окно с надписью 'ЧЕТНЫЕ', если число нечетное, выводите его в другое окно. Выход из цикла — 99. 13. Напишите программу, в которой вывод символов строки сопровождается звуковым сигналом. Формирование сигнала оформите отдельной процедурой Веер.
366 Глава 14 14. Напишите программу, которая вводит в цикле целые числа и если число превышает 100, подает звуковой сигнал с помощью пользовательской процедуры Веер. Выход из цикла — 99. 15. Напишите программу, которая генерирует 100 целых случайных чисел в диапазоне 1...300. Если случайное число попало в диапазон 60... 100, подайте звуковой сигнал с помо- щью пользовательской процедуры Веер и выведите это число на экран. 16. Исследуйте датчик случайных чисел системы Турбо Паскаль. Для этого сгенерируй- те 5000 случайных чисел и подсчитайте число четных и нечетных чисел. Если число четных чисел превысит 2500, подайте один звуковой сигнал, если количество нечетных чисел пре- высит 2500, подайте три сигнала. 17. Разработайте программу-модель датчика-анализатора температуры. В качестве ис- точника температуры возьмите случайное число, полученное по Random(300), 100 раз. Вы- полните анализ диапазонов 0...100,101...200,201...300. В первом случае выдайте сообщение 'Норма', во втором случае — 'Превышение нормы', в третьем — сообщение 'Тревога' и один звуковой сигнал. (По принципу работы этой программы построена работа всех пожарных, химических и прочих датчиков аварийных ситуаций). 18. На основе функции Random постройте программу-исполнителя "случайной" элек- тронной музыки. Продолжительность сигнала также случайна и может длиться от 0 до 500 мс. 19. Напишите программу, которая три раза проигрывает гамму от ДО до СИ, используя ноты ДО, РЕ, МИ, ФА, СОЛЬ, ЛЯ, СИ с частотами соответственно 262, 294, 330, 349, 392, 440,494. 20. Изучите звучание различных частот, вводя с клавиатуры их значения в цикле и гене- рируя соответствующий звук. Выход из цикла — 99. 21. Напишите программу, которая три раза проигрывает гамму от СИ до ДО, т. е. в об- ратном порядке используя ноты ДО, РЕ, МИ, ФА, СОЛЬ, ЛЯ, СИ с частотами соответствен- но 262,294,30,349,392,440,494. 22. Зная, что звук сирены получается последовательной прогонкой частот 500..2000 и назад от 2000 до 500, напишите программу, которая моделирует сирену машины скорой по- мощи. Продолжительность звучания каждой частоты равна 1 мс, NoSound использовать только перед завершающим программу оператором end. 23. Напишите процедуру-заставку программы, которая выводит в окне цветом LightGreen и координатами 30, 10, 20, 15 заставку программы "БАЗА ДАННЫХ спортив- ных рекордов школы N 2" цветом White. Строки оформите типизированными константами. Вывод каждой буквы текста сопроводите коротким звуковым сигналом. 24. Напишите программу ввода символов с клавиатуры, чтобы ввод каждого символа сопровождался случайным звуком. Выход из программы — ввод символа'!'.
Глава 15. ГРАФИКА В ТУРБО ПАСКАЛЕ Экран дисплея компьютера предстайляет собой прямоугольное поле, состоя- щее из большого количества точек. Как было показано в предыдущей главе, дис- плей может работать в текстовых или графических режимах. Но в отличие от тек- стового режима в графическом режиме имеется возможность изменять цвет каждой точки, а точки, окрашенные в разные цвета, могут образовывать линии, тексты и любые другие изображения. Чтобы сделать процесс графического программирования более эффективным, фирма Borland International разработала специализированную библиотеку GRAPH, набор драйверов, позволяющих работать практически со всеми существующими типами видеомониторов, и набор шрифтов для вывода на графический экран тек- стов разной величины и формы. 15.1. АППАРАТНАЯ И ПРОГРАММНАЯ ПОДДЕРЖКА ГРАФИКИ 15.1.1. Адаптер и монитор Аппаратная поддержка графики персонального компьютера обеспечивается двумя основными модулями: видеоадаптером и видеомонитором. Видеомонитор (или дисплей) — это устройство, на котором появляется выводимый текст или гра- фические изображения. Он работает так же, как и обычный телевизор. Экран 25 раз в секунду формируется заново. Так как человеческий глаз не способен уловить та- кое быстрое мелькание кадров, создается иллюзия неподвижного изображения на экране монитора. Изображение на экране строится из небольших точек (пикселей), объединяющихся в телевизионные строки. Соответственно минимальной единицей управления является пиксель. Число точек на одной телевизионной линии и число самих телевизионных ли- ний различно для разных типов видеоадаптеров. При работе в графическом режиме появляется возможность управлять цветом любого пикселя. Число строк пикселей и число пикселей в каждой телевизионной строке зависит от режима работы видео- адаптера. Таким образом, экран в графическом режиме представляет собой матри- цу пикселей.
368 Глава 15 Видеоадаптеры — это весьма сложные электронные устройства, управляемые собственным микропроцессором, сравнимым порой по мощности с основным или центральным процессором вычислительной системы. Будет или не будет способен компьютер выводить графические изображения — целиком зависит от типа адапте- ра. Конструктивно он представляет собой самостоятельную электронную плату, вставляемую в один из свободных разъемов внутри системного блока компьютера. Как правило, видеоадаптеры производятся самостоятельными фирмами-изготови- телями. В новых продуктах фирмы IBM — Personal System/2 (PS/2) видеоадаптер встроен в системную плату, на которой располагается центральный процессор и об- служивающие его узлы. Для персональных компьютеров типа IBM PC доступны три основных типа мо- ниторов: монохроматический, цветной, усовершенствованный цветной (Enchanced Color Display — ECD). Существует несколько вариантов ECD: с низким и высоким разрешением, с фиксированной частотой синхронизации и мультичастотный. Если компьютер оснащен адаптером, генерирующим цветной сигнал, и монохроматиче- ским экраном, цвета передаются на экране различными оттенками серого (как цвет- ная телепередача по черно-белому телевизору). Типы видеоадаптеров. В самом общем виде видеоадаптер состоит из двух ос- новных частей: контроллера электронно-лучевой трубки, или CRT-контроллера (Cathode Ray Tube — CRT), и видеопамяти (видеобуфера). Помимо этих обязатель- ных узлов наиболее совершенные видеоадаптеры имеют в своем составе ряд допол- нительных узлов, например специализированные контроллеры быстрой манипуля- ции содержимым видеобуфера (так называемые контроллеры графики). Для компьютеров IBM PC и совместимых с ними имеется несколько стандар- тов видеоадаптеров. Самые первые компьютеры IBM PC снабжались простейшим из адаптеров — монохроматическим адаптером дисплея. Очень скоро появились цветные графические адаптеры, или CGA, затем усовершенствованные графиче- ские адаптеры EGA. В наиболее совершенных компьютерах (PS/2) фирма IBM соз- дала два новых стандарта графики, реализуемых новыми типами адаптеров. В ком- пьютерах PS/2 модель 30 — это адаптер MCGA (Multi Color Graphics Array — мно- гоцветный графический массив). Во всех остальных моделях PS/2 установлен ви- деоадаптер VGA. Еще один распространенный тип видеоадаптеров — это монохро- матический адаптер Hercules, или HGC (Hercules Graphics Card). 15.1.2. Видеобуфер В графических режимах каждая телевизионная точка, или пиксель имеет закре- пленную за ней группу битов (1 или больше), задающих цвет пикселя. В совокуп- ности эти биты образуют видеобуфер (видеопамять, память адаптера дисплея). Ос- новное назначение видеобуфера — хранение образа экрана.
Графика в Турбо Паскале 369 Для CGA-адаптеров, поддерживающих графические режимы 200 строк по 320 пикселей в каждой строке (режим среднего разрешения 200 х 320) и 200 х 640 (ре- жим высокого разрешения), видеопамять открыта для непосредственного доступа, начиная с физического адреса B8000h. CGA-адаптеры имеют буфер объемом толь- ко 16 Кбайт, поэтому в режиме 200 х 320 на каждый пиксель приходится только по 2 бита: (16 * 1024 байт) / (200 * 320) = 16384 * 8 бит / 64 000 = 2. Оставшиеся биты в графических CGA-режимах не используются. Два бита по- зволяют закодировать четыре различных цвета для пикселя в режиме среднего раз- решения. Аналогичный расчет для режима высокого разрешения показывает, что на каждый пиксель приходится только один бит, и он кодирует два возможных цве- та (светится, не светился). Более современные EGA-адаптеры имеют большую внутреннюю память, что позволяет увеличить число битов, отводимых для кодирования цвета пикселя. При этом EGA-адаптеры полностью имитируют работу CGA-адаптера, если устанавли- вается графический CGA-режим; в этом случае имеется возможность непосредст- венного доступа к видеобуферу, начиная с адреса B8000h. Но при выборе одного из графических EGA-режимов проявляются все дополнительные возможности адапте- ра: увеличивается число возможных цветов для пикселя, либо число строк и столб- цов пикселей, либо и то, и другое. 15.1.3. Видеостраницы Память видеобуфера разделяется на несколько частей — так называемых ви- деостраниц. Их количество зависит от текущего режима и типа адаптера. Более од- ной страницы имеют адаптеры EGA, VGA и Hercules. Нумерация страниц начина- ется с 0. "Разметка" видеобуфера на страницы начинается с некоторого начального ад- реса, называемого смещением до видеостраницы. Страница 0 имеет нулевое сме- щение. Страница 1 в режиме 80 х 25 начинается со смещения 4096 (1000h) относи- тельно начального адреса видеопамяти, страница 2 — со смещения 8192 (2000h) и т. д. Большая внутренняя память позволяет "держать" несколько полных "картинок" (страниц видеопамяти). В EGA-режимах видеопамять адаптеров не может быть прочитана или в нее не может быть записана информация обычными инструкциями пересылки данных, а требуется доступ к внутренним портам видеоадаптера. Еще более широкие аппаратные возможности имеют самые современные на се- годня VGA-адаптеры, поддерживающие не только CGA-, EGA-режимы, но и не- сколько новых режимов, имеющих еще большее число битов на каждый пиксель (следовательно, пиксели могут иметь более широкую гамму цветов) либо большее по сравнению с EGA-режимами число строк и столбцов пикселей. Видеопамять адаптеров разделена на несколько областей фиксированной длины, которые приня-
370 Глава 15 то называть видеостраницами. В адаптерах EGA, VGA, Hercules от 2 до 4 страниц, в CGA-адаптерах только 1. Та видеостраница, которая постоянно "освежается" в данный момент, называется текущей. Видеоадаптер способен выполнять переклю- чение текущей видеостраницы (табл. 15.1). Таблица 15.1 Режимы и видеостраницы Драйвер Номер Режим Номер Разрешение Цвет Страницы EGA 3 EGALO 0 640x200 16 4 EGAHI 1 640x350 16 2 VGA 9 VGALO 0 640x200 16 4 VGAMED 1 640x350* 16 4 HERC 7 HERCMONOHI о • 720x348 2 2 В каждый отдельный момент на экране может быть отображена только одна страница, она называется видимой. По умолчанию видима страница с номером 0. Страничная организация позволяет с помощью графических процедур и функций формировать изображения на любой из страниц. Страница, на которой в данный момент формируется изображение, называется активной. Для работы с видеостраницами предназначены две процедуры: SetActive-Page и SetVisualPage. Они часто используются при разработке анимационных программ. Процедура SetActivePage(Page: word) устанавливает активную страницу для построения изображения, например: SetActivePage(l); Построение изображения может производиться незаметно для смотрящего на экран (в этом случае активная страница не совпадает с видимой). Например, стра- ница может формироваться "подкачкой" данных с диска или с помощью любых процедур Турбо Паскаля. Сформировав страницу, ее можно показать на экране с помощью процедуры SetVisualPage(Page : word), где Page — номер видимой стра- ницы. Например: SetActivePage(0); {Показ страницы 0 на экране} . OutText(’Страница О'); {Строка появляется на экране} SetActivaFage(1); {Активная страница 1} OutText('Страница 1'J;{Формирование изображения на странице 1, но на экране строки нет !} Readln; SetVisualPage(1); {Показ страницы 1, строка на экране} 15.1.4. Драйверы Какой бы адаптер ни был установлен на компьютере, мы можем использовать один и тот же набор графических процедур и функций Турбо Паскаля благодаря
. Графика в Турбо Паскале 371 тому, что их конечная настройка на конкретный адаптер осуществляется автомати- чески. Эту настройку выполняют специальные программы, называемые графиче- скими драйверами. Графические драйверы разработаны практически для всех су- ществующих видеоадаптеров (табл. 15.2). Они находятся в файлах, имеющих рас- ширение .BGI (Borland Graphics Interface) и активизируются при инициации графи- ческого режима. Эти драйверы используются вместе с компиляторами Турбо Си и Турбо Паскаль фирмы Borland. Когда графическая программа пользователя готова, BGI-драйвер может быть включен в ЕХЕ-файл процедурой RegisterBGIDriver. Таблица 15.2 Графические драйверы Драйвер Адаптер Драйвер Адаптер CGA.BG1 EGAVGA.BGI HERC.BGI IBMCGA.MCGA IBM EGA VGA Hercules (mono) ATT.BGI PC3270.BGI IBM8514.BGI AT&T 6300 (400 строк) IBM 3270 PC IBM 8514 Система графики Турбо Паскаля позволяет подключать не только стандартные, но и разработанные программистом драйверы. Последние должны удовлетворять стандарту Borland International, который, к сожалению, в настоящее время полно- стью не опубликован, что делает создание собственных драйверов весьма трудным занятием. Тем не менее, в модуле GRAPH имеется процедура RegisterBGIDriver, поддерживающая работу с нестандартными драйверами пользователя. 15.1.5. Состав графических средств Для получения любого изображения в графическом режиме необходимо заста- вить светиться заданным цветом строго определенную группу пикселей. Сказанное относится и к выводу контура буквы, формируемого из телевизионных точек на эк- ране. Такая работа может быть выполнена самой программой; но при этом объем программирования становится слишком большим. Например, если обычно буква изображается матрицей из 8 строк по 8 точек в каждой строке, требуется "закра- сить” одновременно 64 пикселя, расположенных в разных телевизионных строках. При этом потребуется учесть ряд особенностей видеоадаптера, текущий режим и т.д. 15.2. МОДУЛЬ GRAPH Для формирования графических изображений в языке Турбо Паскаль предна- значен стандартный библиотечный модуль GRAPH. В нем содержится 79 графиче- ских процедур, функций, десятки стандартных констант и типов данных. Все они
372 Глава 15 составляют единый комплекс средств, позволяющих разрабатывать профессио- нальные программные продукты. Запуск графической системы. Для запуска графической системы необходимо сделать следующее: 1. Подключить модуль GRAPH — библиотеку графических процедур: uses graph; 2. Установить графический режим с помощью двух переменных: var DrivexVar, ModeVar: integer; begin DrivexVar:“Detect; InitGraph(DrivexVar,ModeVar,•\TP\GRAPH•); С этого момента все графические средства доступны пользователю. Процедуры и функции модуля Graph. Процедуры модуля Graph. Процедура Аге Ваг Bar3D Circle ClearDevice ClearViewPort CloseGraph DetectGraph Действие Рисует дугу от начального угла к конечному, используя (X, Y) как центр Рисует полосу, используя текущий стиль и цвет Рисует трехмерную полосу, используя текущие стиль и цвет Рисует окружность, используя (X.Y) как центр Очищает экран и устанавливает текущий указатель (СР) в начало Очищает окно Закрывает графическую систему Проверяет аппаратуру и определяет, какой графический драйвер и в каком ре- жиме используется DrawPoly . Ellipse Рисует многоугольник текущими цветом и типом линии Рисует эллиптическую дугу от начального угла к конечному, используя (X,Y) как центр FillEUipse Рисует заполненный эллипс, используя (X.Y) как центр и XRadius и YRadius как горизонтальные и вертикальные оси FillPoly FloodFill Заполняет многоугольник, используя сканирование Заполняет ограниченную область, используя текущий шаблон и цвет заполне- НИЯ GetArcCoords GetAspectRatio Позволяет запросить координаты команды Аге Возвращает разрешение экрана, из которого может быть вычислен относи- , тельный аспект (Xasp/Yasp) GetFillPattem Возвращает шаблон заполнения, установленный последним вызовом SetFillPattem GetFillSettings Позволяет запросить текущие шаблон и цвет, установленные SetFillStyle или SetFillPattem Getlmage Сохраняет битовый образ указанной части экрана в буфере
Графика в Турбо Паскале 373 - Продолжение Процедура GetLineSettings Действие Возвращает текущие стиль, шаблон и толщину линии, установленные SetLineStyle GetModeRange Возвращает минимальный и максимальный графические режимы для данного драйвера GetPalette GetTextSettings Возвращает текущую палитру и ее размер Возвращает текущие шрифт, направление, размер и выравнивание текста, ус- тановленные SetTextStyle и SetTextJustify GetViewSettings GraphDefaults Позволяет запросить текущие параметры окна и отсечения Устанавливает текущий указатель (СР) в начало и переустанавливает графиче- скую систему InitGraph Инициализирует графическую систему и устанавливает устройство в графиче- ский режим Line LineRel Рисует линию от (XI,Y1) к (X2.Y2) Рисует линию от текущего указателя (СР) к точке, лежащей на заданном рас- стоянии LineTo ' MoveRel Рисует линию от текущего указателя к (X, Y) Передвигает текущий указатель (СР) на заданное расстояние от его текущей позиции MoveTo OutText OutTextXY PieSlice Передвигает текущий указатель (СР) в (X.Y) Выводит текст на экран от текущего указателя Выводит текст на экран Рисует и заполняет сектор, используя (X,Y) как центр и рисуя от начального угла к конечному Putlmage PutPixel Rectangle RestoreCRTMode Sector SetActivePage SetAllPalette SetAspectRatio SetBkColor SetColor SetFillPattem SetFiUStyle SetGraphBufSize SetGraphMode SetLineStyle SetPalette SetRGBPallete SetTexUustify SetTextStyle SetUserCharSize Выводит битовый образ на экран Рисует точку (пиксель) в (X, Y) Рисует прямоугольник, используя текущие цвет и тип линии Восстанавливает видеорежим, который был до инициализации графики Рисует и заполняет сектор эллипса Устанавливает активную страницу для графического вывода Изменяет цвет палитры Изменяет значение относительного аспекта Устанавливает цвет фона Устанавливает основной цвет, которым будет осуществляться рисование Выбирает шаблон заполнения, определенный пользователем Устанавливает шаблон заполнения и цвет Позволяет изменить размер буфера для функций заполнения Переводит систему в графический режим и очищает экран Устанавливает текущие толщину и стиль линии Изменяет один цвет палитры, указанный через ColorNum и Color Позволяет модифицировать палитру для IBM 8514 и VGA Устанавливает выравнивание текста, используемое OutText и OutTextXY Устанавливает текущие шрифт, стиль и размер текста Позволяет изменить ширину и высоту символа для штрихового шрифта
374 Глава 15 Продолжение Процедура SetViewPort SetVisualPage SetWriteMode Действие Устанавливает текущее окно для графического вывода Устанавливает номер видимой графической страницы Устанавливает режим вывода (копирование или XOR) для линий, рисуемых с DrawPoly, Line, LineRel, LineTo, Rectangle Функции модуля Graph. Функция GetBkColor GetColor GetDefaultPalette GetDriverName Действие Возвращает текущий фоновый цвет Возвращает текущий цвет Возвращает аппаратную палитру в записи BaletteType Возвращает строку с именем текущего драйвера GetGraphMode SetMaxColor Возвращает текущий графический режим Возвращает максимальный цвет, который можно задать в SetColor GetMaxMode Возвращает номер максимального режима текущего загруженного драйвера GetMaxX Возвращает максимальный X (разрешение по горизонтали) для текущего гра- фического драйвера и режима GetMaxY Возвращает максимальный Y (разрешение по вертикали) для текущего графи- ческого драйвера и режима GetModeName GetPaletteSize GetPixel GetX GetY GraphErrorMsg GraphResult ImageSize Возвращает строку с именем указанного графического режима Возвращает размер таблицы палитры Возвращает цвет точки в (X, Y) Возвращает координату X текущей позиции (СР) Возвращает координату Y текущей позиции (СР) Возвращает строку сообщения об ошибке для заданного кода ErrorCode Возвращает код ошибки для последней графической операции Возвращает число байтов, требуемое для заполнения прямоугольной области экрана InstallUserDriver Устанавливает пользовательский драйвер устройства в BGI-таблицу драйверов устройств InstallUserFont RegisterBGIdriver RegisterBGIfont TextHeight TextWidth Устанавливает новый шрифт, который не встроен в BGI-систему Регистрирует драйвер BGI для графической системы Регистрирует шрифт BGI для графической системы Возвращает высоту строки в пикселях Возвращает ширину строки в пикселях 15.3. ИНИЦИАЛИЗАЦИЯ ГРАФИКИ 15.3.1. Инициализация видеорежима Прежде чем работать с графикой, необходимо установить наиболее подходя- щий для имеющегося монитора видеорежим. Турбо Паскаль имеет фиксированное
Графика в Турбо Паскале 375 число драйверов, каждый из которых поддерживает от одного до трех видеорежи- мов. Тип драйвера и режим могут быть заданы как число или как символическая константа типа. Константы, определяющие видеорежим, приведены в табл. 15.3 вместе с ин- формацией о выбираемом режиме и типе видеоадаптера, который может такой ре- жим поддерживать. Таблица 15.3 Видеорежимы Драйвер Режим Разрешение Файл CGA (1) CGACO, CGAHi 320x200(640x200) CGA.BGI EGA (3) * EGALo, EGAHi 640x200(640x350) EGAVGA.BGI VGA (9) VGALo, VGAHi 640x200(640x350) EGAVGA.BGI HERC (8) HERCMONOHI 720x348 HERC.BGI С момента подключения модуля GRAPH программисту доступны все находя- щиеся в ней подпрограммы. В первую очередь вызывается процедура InitGraph, ко- торая устанавливает один из возможных графических режимов. Формат процеду- ры: InitGraph(var DriverVar, ModeVar: integer; PathToDriver: string); Целочисленные переменные DriverVar и ModeVar задают драйвер и режим в соответствии со значениями, приведенными на с. Например: DriverVar := VGA; ModeVar := VGALo; Первый параметр может задаваться как по имени, так и цифрой (она указана в скобках рядом с именем драйвера). Например, следующие инструкции эквивалент- ны: DriverVar := VGA; DriverVar := 9; Для новичков, которые могут не знать типа дисплея своего компьютера, имеет- ся стандартная константа Detect. Если это значение присвоено параметру DriverVar: DriverVar := Detect; то InitGraph автоматически инициирует нужный драйвер и установит наиболее подходящий для дисплея режим. Третий параметр PathToDriver задает маршрут к модулю GRAPH; если он расположен в активном директории, то вместо маршрута ставятся два апострофа. Подытожив сказанное, запишем начальную группу инструкций, которая позво- лит избежать любых неприятностей на начальном этапе работы с графикой: uses Crt, Graph; var DrivexVar, ModeVar: integer; begin
376 Глава 15 DriverVar Detect; InitGraph(DriverVar, ModeVar, ’•); Программа может потребовать различных манипуляций с видеорежимами. Турбо Паскаль имеет для этого ряд процедур и функций. GetDriverName : string Функция возвращает имя установленного графического драйвера. GetGraphMode : Integer Функция возвращает текущий графический режим. GetModeRange(GraphDriver:integer;var IdMbde,hxMode:integer) Процедура возвращает минимальный и максимальный графические режимы для данного драйвера. DetectGraph(var GraphDriver, GraphMode: integer) Процедура возвращает номера текущих драйвера и режима. GetMaxMode : integer Функция возвращает номер максимального режима установленного драйвера. GetMbdeName(GraphMode: integer): string Функция возвращает имя текущего драйвера. GraphDefaults Процедура устанавливает текущий указатель (СР) в начало и переустанавлива- ет графическую систему. SetGraphMode(Mode: integer) Процедура переводит систему в графический режим и очищает экран. RestoreCRTMode Процедура восстанавливает видеорежим, который был до инициализации гра- фики. 15.3.2. Закрытие видеорежима Когда все запланированные графические работы выполнены, необходимо вый- ти из графического режима. Это делается с помощью не имеющей параметров про- цедуры CloseGraph. В процессе выполнения эта процедура освобождает память, распределенную под драйверы графики, файлы шрифтов и промежуточные дан- ные, и восстанавливает режим работы адаптера в то состояние, в котором он нахо- дился до выполнения инициализации системы. 15.3.3. Переключение текст — графика — текст Иногда требуется периодически переходить из текстового режима в графиче- ский и наоборот. Алгоритм подобных манипуляций показан в следующем фрагмен-
Графика в Турбо Паскале 377 uses Crt, Graph; var DriverVar, ModeVar: integer; begin ClrScr; Writein('Текстовый режим1); DriverVar:= Detect; InitGraph(DriverVar,ModeVar,'•); OutTextXY (300,250,'Графический режим*) RestoreCRTModfe; Writein(’Текстовый режим ! ') ; SetGraphMode (ModeVar) ; OutTextXY (300,250,'Графический режим •') ClовeGraph end. 15.3.4. Обработка ошибок Графическая программа, как и любая другая, может содержать ошибки. Про- граммист должен предусмотреть все возможное для их своевременного обнаруже- ния и нейтрализации. Для этого имеются две функции: GraphResult и GraphErrorMsg. Функция GraphResult: Integer возвращает значение 0, если последняя графиче- ская операция выполнилась без ошибок, или число в диапазоне —15...—1, если ошибка была. Все возможные ошибки и их коды приведены в табл. 15.4. В качестве примера рассмотрим следующий фрагмент: uses Graph; var ErrorNumber: integer; begin ErrorNumber:= GraphResult; В переменной ErrorNumber содержится код ошибки. Можно пользоваться как кодом ошибки, так и соответствующей ему константой, например: if ErrorNumber О grOK then Writein('Обнаружена ошибка: '); GraphErrorMsg (ErrorCode: integer): string;
378 Глава 15 возвращает строку сообщения об ошибке, соответствующую коду ошибки. Напри- мер, процедура Writeln(GraphErrorMsg(ErrorNumber)); выведет строку "No error", так как в нашем примере графический режим установлен правильно. Таблица 15.4 Коды ошибок Константа Значение Описание grOK 0 Нет ошибок gfNoInitGraph —1 Графика не инициализирована (используйте InitGraph) grNotDetected —2 Графическое устройство не обнаружено grFileNotFound —3 Файл драйвера устройства не найден grlnvalidDrivcr —4 Неправильный файл драйвера устройства gfNoLoadMem —5 Недостаточно памяти для загрузки драйвера grNoScanMem —6 Выход за пределы памяти при заполнении (scan fill) grNoFloodMem —7 Выход за пределы памяти при заполнении (flood fill) grFontNotFound —8 Файл шрифта не найден grNoFontMem —9 Недостаточно памяти для загрузки шрифта grlnvalidMode —10 Неверный графический режим для этого драйвера grError —11 Графическая ошибка grIOcrror —12 Ошибка графического ввода-вывода grlnvalidFont —13 Неверный файл шрифта grlnvalidFonfNum —14 Неверный номер шрифта Инициализацию графического режима и проверку возможных ошибок удобно осуществлять в отдельной процедуре: procedure Init; {Процедура инициализации и анализ системных ошибок. DriverVar и ModeVar описаны в основной программе} begin DriverVar:» Detect; InitGraph(DriverVar ,MddeVar,•'); ErrorCodeGraphResult; if ErrorCode О grOK then begin Writeln('Графическая системная ошибка:', GraphErrorMsg(ErrorCode));); Halt(l) end end; Если такая процедура отсутствует, то любая ошибка инициализации вызовет останов машины. В этом случае DOS придется перезагружать заново.
Графика в Турбо Паскале 379 15.4. БАЗОВЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ 15.4.1. Система координат Для построения изображений на экране используется система координат. От- счет начинается от верхнего левого угла экрана, который имеет координаты (0,0). Значение X (столбец) увеличивается слева направо, значение Y (строка) увеличива- ется сверху вниз. Так, в режиме EGAHi адаптера EGA экранные координаты каж- дого из четырех углов и точка в центре экрана будут представлены, как показано на рис. 15.1. Текущий указатель. Чтобы строить изо- бражение, необходимо указывать, по крайней 0,0 639,0 мере, точку начала вывода. В текстовых ре- жимах эту точку указывает курсор, который присутствует на экране, если его искусствен- но не убрать. В графических режимах види- мого курсора нет, но есть невидимый теку- щий указатель СР (Current Pointer). Фактиче- 0,349 320,175 639,349 ски это тот же курсор, но он невидим. В текстовых режимах курсор перемеща- Рис. 15.1. КатУИйгызкражвреиамгЕОАН ется процедурой GoToXY и другими, в графических режимах для перемещения СР также имеется ряд процедур и функций. В первую очередь, это MoveTo и MoveRel. Процедура MoveTo (X,Y: Integer) перемещает текущий указатель в точку с ко- ординатами X,Y. Например, процедура MoveTo(200,100); переместит СР в точку экрана с координатами 200,100. Процедура MoveRel (dX, dY: Integer) перемещает СР на dX точек по горизонта- ли и на dY точек по вертикали. Так, процедура MoveRel(5,10); переместит СР из точки с координатами 200,100 в точку 205,110. В ряде программ выполняется постоянный контроль местоположения текущего указателя. Для этого используются функции GetX и GetY, которые возвращают со- ответственно значение X- и Y-координаты СР. Например: var Xpos, Ypos: integer;. Xpos := GetX; Ypos := GetY; В процессе управления СР может возникнуть ситуация, когда его координаты выйдут за допустимые пределы. Например, в следующем фрагменте процедура MoveTo получит явно не подходящие параметры:
380 Глава 15 X := 6000/10; Y := 2000/2; MoveTo(I,J); Для борьбы с такими ситуациями используются функции GetMaxX: integer и GetMaxY: integer, которые возвращают соответственно максимально возможные для установленного режима значения X- и Y-координат. Например: X := 6000/10; Y := 2000/2; if ((X > GetMaxX) OR (Y > GetMaxY)) then begin Write(*Нарушение диапазона*'); MarginHadling {Пользовательская процедура обработки ошибок} end; * MoveTo(I,J); В графических программах довольно часто приходится определять координаты центра экрана по горизонтали и вертикали. Это делается очень просто: Xcenter := GetMaxX DIV 2; Ycenter := GetMaxY DIV 2; PutPixel(Xcenter,Ycenter,LightGreen); {Точка в центре экрана} Такой способ избавляет вас от настройки на конкретный тип монитора и рас- ширяет область применения программы. 15.4.2. Экран и окно в графическом режиме По аналогии с текстовыми режимами графический экран может рассматривать- ся как одно большое или несколько меньших по размеру окон. Напомним, что окно — это прямоугольная область экрана, выполняющая все функции полного экрана. После установки окна вся остальная площадь экрана как бы не существует и весь ввод-вывод осуществляется только через окно. В каждый отдельный момент может быть активно только одно окно. Если окон несколько, за переключение ввода-вы- вода в нужное окно отвечает программист. По умолчанию окно занимает весь экран, значения координат его левого верх- него и правого нижнего угла устанавливаются автоматически процедурой инициа- лизации InitGraph. Например, следующий фрагмент установит окно, равное полному графическо- му экрану 640x350: DriverVar := VGA; ModeVar := VGAMed; InitGraph(DriverVar,ModeVar) ; Чтобы стереть все изображения на экране, т. е. очистить его, используется не имеющая параметров процедура ClearDevice. С момента ее выполнения все уста-
Графика в Турбо Паскале 381 новки по цвету, фону и окнам аннулируются и указатель СР помещается в точку с координатами (0,0). Если требуется создать окно, следует воспользоваться проце- дурой SetViewPort(xl,yl,x2,y2: integer; Clip: boolean); где xl, yl — координаты ле- вого верхнего угла, х2, у2 — координаты правого нижнего угла окна. Параметр Clip определяет, будет рисунок отсекаться при выходе за границы окна (Clip:= True) или нет (Clip:= False). После создания окна за точку отсчета принимается верхний левый угол окна, имеющий координаты (0,0). Для очистки окна используется процедура ClearViewPort. После ее выполнения все изображения в окне стираются, и указатель СР уста- навливается в левую верхнюю точку окна с координатами (0,0). Напомним, что это внутренние координаты окна, а не полного экрана. Координатную систему полного экрана можно восстацрвить, в частности, с помощью ClearDevice или задав в про- цедуре установки окна максимально возможные значения: SetViewPort (0,0,GetMaxX,GetMaxY, True) ; Атрибуты текущего окна можно получить с помощью процедуры GetViewSettings(var Vp : ViewPortType). Переменная Vp относится к стандартному типу ViewPortType: type ViewPortType = record xl, yl, x2, y2 : integer; Clip : boolean end; Необходимо помнить, что в отличие от текстовых окон графические окна по- сле команды установки фона SetBkColor и очистки с помощью ClearView-Port ме- няют фон вместе с общим фоном экрана. Поэтому фон (точнее "закраску") графи- ческого окна следует устанавливать с помощью процедур SetFill-Style и SetFillPattem. Прямоугольник можно построить с помощью процедуры Ваг: SetViewPort(100,50,500,200,True); SetFillStyle(l,3); {Выбор типа фона} Ваг(100,50,500,200); {Прямоугольник с заданным фоном} В остальном работа с графическим окном аналогична работе с текстовым ок- ном. Например, в следующей программе окно используется как цветовое сопрово- ждение случайного музыкального ряда: program DemoSetViewPort; uses Crt, Graph; var 1: integer; DriverVar, ModeVar: integer; begin
382 Глава 15 DriverVar:=detect; InitGraph(DriverVar,ModeVar,'’); SetViewPort(10,10,630,320,True); I:=l; repeat I:=1+1; Sound(Random(180)+40 +1); Delay(Random(170)) ; SetFillStyle(Random(4),Random (16)) ; Bar(10,10,630,320) ; NoSound; Delay(100) * until KeyPressed; Readln; CloseGraph; end. 15.4.3. Вывод точки Какие бы изображения ни выводились на экран, все они построены из точек. Имея средство построения точки определенного цвета в нужном месте экрана, тео- ретически можно создать любое изображение, вплоть до картины. В библиотеки GRAPH вывод точки осуществляется процедурой PutPixel(X, Y: integer; Color: word), где X и Y — экранные координаты расположения точки, Color — ее цвет. Возможные значения Color приведены в таблице 15.5 (или берутся из установ- ленной палитры). Например, оператор for I:=l to 60 do PutPixel(l,l,Red); выведет в первую строку экрана 60 точек красного цвета. Таблица 15.5 Цветовая шкала Цвет Код Цвет Код Black — черный 0 DarkGray — темно-серый 8 Blue — синий 1 LightBlue — ярко-голубой 9 Green — зеленый 2 LightGreen — ярко-зеленый 10 Cyan — бирюзовый 3 LightCyan — ярко-бирюзовый И Red — красный 4 LightRed — ярко-красный 12 Magenta — малиновый 5 LightMagenta — ярко-малиновый 13 Brown — коричневый 6 Yellow — желтый 14 LightGray — светло-серый 7 White — белый 15
Графика в Турбо Паскале 383 Чтобы узнать цвет точки в конкретной позиции экрана, используется функция GetPixel(X, Y: integer): word. В качестве примера выведем цвет точки, расположенной в позиции 10,1: Writein(GetPixel(10,1)); На экран будет выведено значение 4. 15.4.4. Вывод линии Из точек строятся линии (отрезки прямых). Это делает процедура Line(XI, Y1, Х2, Y2: integer) , где XI, Y1 — координаты начала, Х2, Y2 — координаты конца линии, например: Line( 1,1,600,1); t Нетрудно заметить, что в процедуре Line нет параметра для установки цвета. В этом и других аналогичных случаях цвет задается процедурой SetColor (Color: word), где Color — цвет, значение которого берется из таблицы 15.6 или из установленной палитры. Например: SetColor(Cyan); Lined,1,600,1) ; Для черчения линий применяются еще две процедуры: LineTo и LineRel. Процедура LineTo(X,Y: integer) строит линию из точки текущего положения указателя в точку с координатами Х,У. Процедура LineRel(dX,dY: integer) проводит линию от точки текущего распо- ложения указателя в точку СРх + dX, СРу + dY, где СРх и СРу — текущие коорди- наты СР. Турбо Паскаль позволяет вычерчивать линии самого различного стиля: тонкие, широкие, штриховые, пунктирные и т.д. Установка стиля производится процедурой SetLineStyle (LineStyle: word; Pattern: word; Thickness: word). Параметр LineStyle устанавливает тип строки, возможные значения которого приведены в табл. 15.5; Pattern — образец, Thickness — толщина линии, определяе- мая константами, указанными в табл. 15.6. Если применяется один из стандартных стилей, значение Pattern равно 0. Например: SetLineStyle(DottedLn,О,NormWidth); Rectangle(15,15,150,130) ; Если пользователь хочет активизировать свой собственный стиль, то значение Pattern равно 4. В последнем случае пользователь сам указывает примитив (образ), из которого строится линия. Например: SetLineStyle(UserBitLn,$5555,ThickWidth); Rectangle(20,20,120,100);
384 Глава 15 Таблица 15.6 Стиль линии Константа Значение Описание SolidLn 0 Непрерывная линия DottedLn 1 Линия из точек CenterLn 2 Линия из точек и тире DashedLn 3 Штриховая линия UserBitLn 4 Тип пользователя : word; : word; : word {Стиль} {Образ} {Толщина} Процедура GetLineSettings (var Lineinfo : LineSettingsType) возвращает теку- щие стиль, образ и толщину линии, установленные SetLineStyle. Тип LineSettingsType имеет следующее стандартное описание: type LineSettingsType = record LineStyle Pattern Thickness end; Чтобы получить значения стиля, образа и толщины, надо обратиться к полям записи типа LineSettingsType (табл. 15.7): var Lineinfo : LineSettingsType; SetLineStyle(DottedLn,0,NormWidth); Rectangle(15,15,150,130) ; GetLineSettings(Lineinfo); with Lineinfo do begin Writeln('Стиль:*, LineStyle); Writeln(* Образ:', Pattern) ; Writeln(*Толщина:’, Thickness) end; Таблица 15.7 Толщина линии Константа Значение Описание NormWidth 1 Нормальная толщина (1 пиксель) ThickWidth 3 Жирная линия (3 пикселя)
Графика в Турбо Паскале 385 В ряде случаев при использовании Line, LineRel, LineTo и некоторых других требуется устанавливать режим вывода (копирование или XOR) линии на экран. Для этого предназначена процедура SetWriteMode(Mode: Integer). Значение Mode определяется стандартными константами, например: const CopyPut =0; XORPut = 1; Аргумент Mode может принимать одно из двух значений. Если Mode=0, то пиксели, лежащие на отрезке прямой линии, переопределяют пиксели на экране, и, таким образом, линия на экране имеет текущий цвет. Если Mode=l, то пиксели, об- разующие линию, имеют код цвета, образуемый операцией исключающего ИЛИ (XOR) кода текущего Цвета и кода цвета пикселей на экране, через которые линия проходит. Как частный случай такого поведения можно стереть выведенную ли- нию с экрана, выполнив вывод линии еще раз. Это свойство объясняется тем, что А XOR В XOR В = А. Другими словами, поведение графической системы, на которое оказывает влияние процедура SetWriteMode, соответствует той роли, которую игра- ет 7-й бит байта кода цвета при выводе пикселей на экран функциямй прерывания 1 Oh BIOS. В процессе работы программы полную информацию о характеристиках теку- щего текста можно получить, вызвав процедуру GetTextSettings(var Info: TextSettings^ype); Она возвращает искомые значения в переменную следующего типа: type TextSettingsType = record Font, Direction, CharSize, Horiz, Vert: word end; {Код шрифта} {Код направления} {Размер символа} {Значение горизонтального выравнивания} {Значение вертикального выравнивания} Доступ к полям: var InfVar : TextSettingsType; GetTextSettings(InfVar) ; Writeln('Шрифт:',InfVar.Font); Writeln('Направление:',InfVar.Direction); 13—116
386 Глава 15 15.5. РАБОТА С ТЕКСТОМ 15.5.1. Вывод текста Выводимые на экран изображения обычно сопровождаются пояснительным текстом. В графических режимах для этого используются процедуры OutText и OutTextXY. OutText(Textstring : string) Процедура OutText(Textstring : string) выводит строку текста, начиная с теку- щего положения СР. Например: ОЩТех1('Вводите данные:'); Явный недостаток этой процедуры — нельзя указать произвольную точку на- чала вывода. Его можно устранить с помощью MoveTo, но лучше воспользоваться процедурой OutTextXY(X, Y: integer; TextString: string), где X, Y — координаты точки начала вывода текста, TextString — константа или переменная типа string. Например, чтобы вывести сообщение "Для продолжения нажмите любую клави- шу", начиная с точки 60, 100, надо записать: OutTextXY(60,100,'Для продолжения нажмите любую клавишу.. . ') ; 15.5.2. Вывод численных значений В модуле GRAPH нет процедур, предназначенных для вывода численных дан- ных. Поэтому для вывода численных данных нужно сначала преобразовать число в строку с помощью процедуры Str, а затем посредством'+' подключить ее к выводи- мой OutTextXY строке. Например: Мах:=34.56; Str (Мах:6:2,Smax); {Результат преобразования находится в Smax} OutTextXY(400,40, 'Максимум = ' + Smax); {+ — конкатенация} Преобразование целочисленных и вещественных типов данных в строки удоб- но осуществлять специализированными пользовательскими функциями IntSt и RealSt: function IntSt(Int: integer): string; {Преобразование целочис- ленного значения в строку. Int — целочисленное значение} var Buf: string[10]; begin Str(Int,Buf); IntSt := Buf end; function RealSt(R: real; Dig,Dec: integer): string; {Преобра- зование вещественного значения в строку. R — значение, Dig — ко- личество символов, Dec — количество символов после запятой} var Buf: string[20];
Графика в Турбо Паскале 387 begin Str(R:Dig:Dec,Buf); RealSt := Buf end; Эти функции указываются как параметры в процедурах OutText, OutTextXY и снимают все проблемы вывода цифровых данных в графических режимах с любы- ми пояснениями. Например: Х: = 5.295643871; OutText('X = ’ + RealSt(X,11,9)); В результате на экране появится: X = 5.295643871 15.5.3. Шрифть^ Вывод текста в графических режимах может осуществляться самыми различ- ными стандартными (табл. 15.8) и пользовательскими шрифтами. Различают два типа шрифтов: растровые и векторные. Растровый шрифт задается матрицей точек, а векторный — рядом векторов, составляющих символ. Поэтому при увеличении растрового символа мы начинаем различать составляющие его точки, и качество изображения символа снижается, а при увеличении векторного символа качество изображения не меняется. По умолчанию после инициализации графического режима устанавливается растровый шрифт DefaukFont, который, как правило, является шрифтом, исполь- зуемым установленным драйвером клавиатуры. Каждый его символ формируется в матрице 8x8 бит. Шрифты размещены в отдельных файлах, имеющих расширение CHR. Активизация нужного шрифта осуществляется специальной процедурой. Высота и ширина символов каждого шрифта могут изменяться с помощью специ- альных средств. Таблица 15.8 Стандартные шрифты Шрифт Файл Шрифт Файл TriplexFont TRIP.CHR SansSerifFont SANS.CHR SmallFont LITT.CHR GothicFont GOTH.CHR Шрифты SmallFont, SansSerifFont, GothicFont являются векторными и не содер- жат русских сймволов, однако можно разработать собственные шрифты или менять отдельные символы в уже имеющихся. Разработка шрифтов — довольно сложный и трудоемкий процесс. Он может быть ускорен и упрощен, если воспользоваться специализированными пакетами TurboFont, BgiToolKit. С их помощью можно фор-
388 Глава 15 мировать любое количество собственных CHR-файлов и загружать их по тому же принципу, что и стандартные. Устаиовка шрифта. Качественное оформление экрана требует при выводе текста использования самых различных шрифтов. Список имеющихся в Турбо Пас- кале шрифтов приведен в табл. 15.8. Установить нужный шрифт можно процеду- рой SetTextStyle(Font : word; Direction : word; CharSize: word) где Font — выбранный шрифт, Direction — направление (горизонтальное или вер- тикальное),CharSize — размер выводимых символов. Возможные значения двух первых параметров представлены в табл. 15.9. При организации вертикального вывода необходимо учитывать, что, если программист не установит точку начала вывода с помощью MoveTo, текст начинается с нижней строки экрана и продолжается вверх. Величину выводимых символов можно уста- навливать с помощью коэффициента CharSize. Если CharSize=l, то символ строит- ся в матрице 8x8, если CharSize=2, то используется матрица 16x16 и т.д. до 10-крат- ного увеличения. Таблица 15.9 Шрифты Константа Значение Описание DefaultFont 0 8x8 битовый шрифт TriplexFont 1 Штриховые шрифты SmallFont 2 Малый шрифт SansSerifFont 3 Сансериф GothicFont 4 Готический Константы ориентации HorizDir 0 Слева направо VertDir 1 Снизу вверх В качестве примера шрифтом DefaultFont выведем две строки: вертикальную и горизонтальную разной величины: SetTextStyle(0,1,1); {Буквы стандартной величины} Outtextxy(200,200,'Вертикальная строка'); SetTextStyle(0,0,2); {Размер букв увеличен} Outtextxy(200,220,'Горизонтальная строка'); При загрузке шрифтов возможно появление ошибок, их коды представленные ниже, могут быть получены для анализа с помощью GraphResult.
Графика в Турбо Паскале 389 Код Значение — 8 Файл не найден — 9 Не хватает памяти для загрузки выбранного шрифта — 11 Ошибка графики — 12 Ошибка ввода-вывода — 13 Недопустимый файл шрифта — 14 Недопустимый номер шрифта Нужную величину шрифта можно установить еще одним способом — с помо- щью процедуры SetUserCharSize(multX, divX, multY, divY: word). Первые два параметра управляют горизонтальным размером, два последних — вертикальным. Если взять за 1 ширину стандартного шрифта, то отношение multX/divX будет устанавливать ширину нового шрифта. Отношение multY/divY точно так же определяет высоту символов. До или после вызова SetUserCharSize необходимо установить шрифт и направление с помощью SetTextStyle. Например: SetTextStyle(SansSerifFont,HorizDir,4) ; OutText('Нормальный размер'); SetUserCharSize(1,3,1,1) ; OutText('Уменьшение по высоте'); SetUserCharSize(3,1,1,1) ; OutText('Увеличение по ширине*); Чтобы узнать вертикальный и горизонтальный размеры символа или строки в пикселях, можно воспользоваться функциями: ТехtHeight(Textstring: string): word, Textwidth(TextString: string): word. Эти функции.используются в самых различных случаях, в частности для черче- ния рамки вокруг строки или символа. В качестве примера приведем программу, которая вычерчивает рамку вокруг текста, выведенного любым шрифтом. В данном случае рамка белого цвета строит- ся вокруг строки ярко-голубого цвета: program DemoTextFrame; uses Crt, Graph; var Driver, Mode: integer; St: string; Height, Width, cX, cY, xl, x2, yl, y2: integer; begin St := 'Текст в рамке *; Driver:=detect;
390 Глава 15 InitGraph(Driver,Mode,1’) ; cX:=GetMaxX div 2; cY:=GetMaxY div 2; SetTexfcJustify(CenterText,CenterText) ; SetTextStyle(DefaultFont,HorizDir,2) ; Height:=TextHeight(St) +4; Width:=TextWidth(St) + 4; xl:=cX - (Width div 2) ; x2:=cX + (Width div 2); yl:=cY — (Height div 2) ; y2:=cY + (Height div 2) ; SetColor(White); Rectangle(xl,yl,x2,y2); SetColor(LightCyan); OutTextXY(cX,cY,St); ' Readln; CloseGraph end. 15.5.4. Выравнивание текста В некоторых задачах требуется в пределах одной строки выводить символы выше или ниже друг друга. Например, если строка содержит выражение X в квад- рате, то центр символа 2 должен располагаться выше центра символа X. Естествен- но, отсчет должен вестись от текущего положения указателя СР. Выравнивание текста выполняется с помощью процедуры SetTextJustify(Horiz, Vert : word) как по вертикали, так и по горизонтали посредством задания параметров Horiz и Vert. Их возможные значения приведены в табл. 15.10. Таблица 15.10 Параметры выравнивания Параметр Значение Комментарий Горизонтальное выравнивание LeftText 0 Выровнять влево CenterText 1 Центрировать RightText 2 Выровнять вправо Вертикальное выравнивание BottomText 0 Переместить вниз CenterText 1 Центрировать TopText 2 Переместить вверх
Графика в Турбо Паскале 391 В качестве примера выведем X : SetTexfcJustify(CenterText,CenterText); OutTextXY(100,100, 'X' )i; SetTextJustify(1,0); OutTextXY(108,100,•2•); Значение степени будет расположено выше X, как это обычно делается при за- писи вручную. Аналогичным способом можно пространственно сформировать и вывести на экран практически любую формулу. 15.6. УСТАНОВКА ЦВЕТА И ПАЛИТРЫ Цвет для адаптера CGA. В CGA-адаптерах используется так называемая RGBI-система (Red, Green, Blue, Intensity) работы с цветом. На базе трех цветов (красного, зеленого, синего) путем смешивания и установки низкой или высокой яркости свечения формируются четыре аппаратно предопределенные палитры. Па- литрой называется соответствие между кодами цветов й цветами, отображаемыми на экране видеомонитора (табл. 15.11). Таблица 15.11 Предопределенные палитры для CGA-адаптера Номер палитры Фон(0) Цвет (01) Цвет (02) Цвет (03) 0 Black LightGreen LightRed Yellow 0000 1010 1100 1110 1 Black LightCyan LightMagenta White 0000 1011 1101 1111 2 Black Green Red Brown 0000 0010 0100 0110 3 Black Cyan -Magenta LightGray 0000 0011 0101 0111 Каждый цвет палитры формируется четырьмя битами, смешивающими голу- бой (0001), зеленый (0010), красный (0100) цвета. Четвертый бит управляет ярко- стью свечения: 1000 — высокая интенсивность свечения, 0000 — низкая. Напри- мер, цвет LightCyan можно получить, смешав цвета Green и Blue и добавив бит яр- кости: 0001 + 0010 + 1000 = 1011. Точно так же из цвета Red получается цвет LightRed: 0100 + 1000 =1100. По этому принципу формируется 16 цветов, поддерживаемых для адаптера CGA драйвером CGA.BGL С момента старта CGA.BGI процедурой InitGraph все приведенные в табл. 15.12 цвета становятся доступными программисту.
392 Глава 15 Цвета для адаптера CGA Таблица 15.12 Константа Значение Код Цвет на экране монитора Компоненты цвета Black 0 0000 Черный Blue 1 0001 Синий ...В Green 2 0010 Зеленый ..G. Cyan 3 0011 Сине-зеленый ..GB Red 4 0100 Красный R.. Magenta 5 0101 Красно-синий .R.B Brown 6 0110 Коричневый .RG. LightGray 7 0111 Светло-серый .RGB DarkGray 8 1000 Темно-серый I... LightBlue 9 1001 Ярко-синий I..B LightGreen 10 1010 Ярко-зеленый I.G. LightCyan 11 1011 Яркий сине-зеленый I.GB LightRed 12 1100 Ярко-красный IR.. LightMagenta 13 1101 Яркйй красно-синий IR.B Yellow 14 1110 Желтый IRG. White 15 1111 Белый IRGB Цвет для адаптеров EGA/VGA. Новые модели видеоадаптеров EGA/VGA имеют ряд конструктивных особенностей, позволяющих значительно расширить возможности работы с цветом. Для установки цвета пикселя используется уже 6 бит, а не 4, как в CGA. Соответственно расширяется гамма цветов. Принцип их формирования примерно тот же, что и в CGA-адаптерах, но в основу берется систе- ма RrGgBb, где RGB — красный, зеленый и голубой цвета нормальной яркости, а rgb — те же цвета, но яркость их в два раза меньше. Для EGA/VGA-адаптеров драйвер EGAVGA.BGI устанавливает 54 цвета, часть из них соответствует цветам для CGA (табл. 15.13). Цвета для адаптеров EGA/VGA Таблица 15.13 Константа Значение Код Цвет на экране монитора Компоненты цвета EGABlack 0 000000 Черный EGABlue 1 000001 Синий В EGAGreen 2 000010 Зеленый ....G. EGACyan 3 000011 Сине-зеленый ....GB EGARed 4 000100 Красный ...R.. EGAMagenta 5 000101 Красно-синий ...R.B EGABrown 6 010110 Коричневый .g.RG. EGALightGray 7 000111 Светло-серый ...RGB EGADarkGray 56 111000 Темно-серый rgb...
Графика в Турбо Паскале 393 Продолжение Константа Значение Код Цвет на экране монитора Компоненты цвета EGALightBlue 57 111001 Ярко-синий rgb..B EGALightGreen 58 111010 Ярко-зеленый rgb.G. EGALightCyan 59 111011 Яркий сине-зеленый rgb.GB EGALightRed 60 111100 Ярко-красный rgbR.. EGALightMagenta 61 111101 Яркий красно-синий rgbR.B EGAYellow 62 111110 Желтый rgbRG. EGA White 63 111111 Белый rgbRGB Как для CGA-, так и для EGA/VGA-адаптеров любой цвет для черчения фигур и вывода текста может быть установлен с помощью процедуры SetColor. Фон зада- ется процедурой SetBkColor. Последовательность расположения отдельных цветов и всего набора (см. табл. 15.12 и 15.13) может быть изменена пользователем по сво- ему усмотрению с помощью процедур SetPalette и-SetAHPalette. Установка палитры и цвета. После старта InitGraph вся информация об уста- новленной в зависимости от режима драйвера палитре находится в переменной стандартного типа PaletteType: type PaletteType — record Size : byte; Colors : array[0..MaxColors] of shortint end; где Size — число цветов в палитре, Colors — значения в регистрах палитры. Элементы массива Colors — целые числа, определяющие конкретные цвета. Например, элемент массива с индексом 4 соответствует цвету Red, элемент 14 — цвету Yellow и т. д. Получить информацию о текущей палитре можно с помощью процедуры GetDefaultPalette(var Palette : PaletteType)^ где Palette — переменная типа PaletteType. Доступ к полям такой же, как и при работе с обычными переменными типа record. Очевидно, что восприятие любого графического изображения зависит прежде всего от цвета изображения и от фона, на котором оно создавалось. В языке Турбо Паскаль для установки этих двух важнейших атрибутов используются про- цедуры SetColor и SetBkColor. Процедура SetColor(Color : word) устанавливает цвет, используемый процеду- рами графического вывода, в значение, заданное параметром Color. До того момен- та, пока цвет не определен, для вывода используется цвет, имеющий максимальный номер палитры, и фон, устанавливаемый по минимальному номеру. Если Color за- дает недопустимый номер цвета, текущий цвет остается неизменным. Процедура SetBkColor(Color : word) устанавливает новый цвет фона, который определяется значением Color. Для CGA-адаптеров цвет фона выбирается из табл.
394 Глава 15 15.12. Для EGA- и VGA-адаптеров в качестве фона может быть задано любое целое число в диапазоне 0...63 или соответствующая константа из табл. 15.13. В ряде случаев порядок следования цветов в стандартных палитрах может не удовлетворять программиста. Если требуется изменить расположение одного или нескольких цветов, можно воспользоваться процедурой SetPalette; если необходи- мо изменить всю палитру, следует воспользоваться процедурой SetAllPalette. Процедура SetPalette (ColorNum : word; Color: shortint) используется для изме- нения стандартного соответствия между номером элемента палитры и его цве- том. Параметры ColorNum — номер цвета в палитре, Color — новое значение цве- та. Например, элемент 7 цветовой палитры для EGA — это LightGray. Чтобы седь- мой номер стал представлять другой цвет, например Yellow, надо записать: SetPalette(7, Yellow). * Изменение цвета на экране происходит немедленно и без очистки экрана. Про- цедура работает только для систем с EGA- и VGA-адаптерами, ибо в них любой ре- гистр цвета доступен для переопределения. SetAllPalette (var Palette: PaletteType) используется в системах с EGA- или VGA-адаптером для изменения порядка расположения цветов в палитре. Например, если вас не устраивает порядок, показанный в табл. 15.13, вы можете создать соб- ственную палитру, которая начинается, например, с цвета Brown, а не Black, как в стандартной палитре. Для это^о нужно элементам массива Colors, входящего в пе- ременную типа PaletteType, присвоить новые значения: type PaletteType = record Size : byte; Colors : array [0. .MaxColors] OF shortint end; var Palette: PaletteType; Palette.colors[0]:=Brown; . С момента установки новой палитры все фигуры, выведенные ранее на экран, изменят свой цвет на соответствующий новый. В качестве примера работы с палит- рами рассмотрим следующую задачу. Пусть требуется начертить прямоугольник цветом и на фоне, установленными по умолчанию (это в соответствии с табл. 15.12 White и Black), затем изменить 15-й номер палитры с White на Green. Далее нужно создать собственную палитру и вывести другой прямоугольник цветом и на фоне, соответствующими по умолчанию новой палитре. Реализация: program DemoPalettes; uses Crt, Graph;
Графика в Турбо Паскале 395 var DriverVar, ModeVar: integer; Palette : PaletteType; begin . DriverVar:—detect; InitGraph(DriverVar,ModeVar,•'); GetPalette(Palette); Rectangle(10,10,100,200); {Прямоугольник цвета White} Delay(3000); {Изменяем цвет номер 15 (White)-на цвет Green) SetPalette(15,Green); {Прямоугольник перекрашивается из белого в зеленый) t Delay(4000); {Устанавливаем собственную палитру из 15 цветов) Palette.Colors[0] Brown; Palette.Colors[1] := Red; Palette.Colors[2] := Green; Palette.Colors[3] LightBlue; Palette.Colors[15] := LightRed; SetAllPalette(Palette) ; {Цвет фона Black изменяется на Brown, первый прямоугольник меняет цвет на LightRed, новый прямоугольник вычерчивается по умолчанию последним цветом пользовательской палитры — LightRed) Rectangle(120,100,600,300) ; Readln; CloseGraph end. Кроме перечисленных имеется ряд дополнительных средств: GetBkColor, GetColor, GetMaxColor, GetPalette, GetPaletteSize. Кратко опишем их назначение: GetBkColor: word — возвращает номер текущего цвета фона. GetColor: word — возвращает номер текущего цвета для вычерчивания. GetMaxColor: word — возвращает максимальное значение кода цвета в палитре минус 1. Это значение позволит установить максимальное число цветов, которое может отображаться на экране. В зависимости от режима, в котором проведена инициализация системы графики, возвращаемое значение может быть равно 1, 3 или 15. GetPalette (var Palette: PaletteType) — выводит в переменную типа PaletteType информацию о текущей палитре. В отличие от функции GetDefaultPalette она воз- вращает данные о пользовательских палитрах.
396 Глава 15 GetPaletteSize: integer — возвращает число цветов в текущей палитре, напри- мер, для режимов CGA1 — CGA4 будет возвращено 4. Для системы с EGA-адапте- ром, включенным в режиме EGAHI, число цветов в палитре равно 16. 15.7. ПОСТРОЕНИЕ ГРАФИЧЕСКИХ ФИГУР 15.7.1. Построение прямоугольников Без построения различных графических фигур не обходится ни одна серьезная графическая система. Например, в основе построения меню лежит вывод ряда пря- моугольников с помещенными в них названиями режимов; круговые диаграммы строятся из окружностей и секторов и т.д. Библиотека GRAPH содержит ряд про- цедур, которые, формируют самые различные фигуры на основе задаваемых пара- метров. Цвет, стиль и толщина линии для вычерчивания берутся по умолчанию или устанавливаются соответственно процедурами SetColor и SetFillPattem, SetFillStyle. Для построения прямоугольных фигур имеется несколько процедур. Первая из них — процедура вычерчивания одномерного прямоугольника: Rectangle(Xl, Yl, Х2, Y2 : integer), где XI, Yl — координаты левого верхнего угла, Х2, Y2 — коор- динаты правого нижнего угла прямоугольника. Это очень полезная процедура, с ее помощью, в частности, можно легко построить любую диаграмму для визуального анализа данных. Область внутри прямоугольника не закрашена и совпадает по цве- ту с фоном. В качестве примера приведем фрагмент, который выводит на экран 100 вычер- ченных разным цветом динамически изменяющихся по высоте прямоугольников: for I := 1 to 100 do begin SetColor(Green); {Установка цвета} Rectangle(200,Random(300),250,300) ; {I-й прямоугольник) Delay(50); {Задержка} < ClearDevice {Очистка экрана} end; B этом примере высота прямоугольника изменяется случайным образом. Если в качестве второго параметра процедуры Rectangle будут целочисленные элементы массива (например, суммы ежемесячных платежей учреждения), то получим дина- мическую визуализацию исходных данных. По этому принципу работают простые программы графической интерпретации данных — деловая графика. Более эффектные для восприятия прямоугольники можно строить с помощью процедуры Bar(xl, yl, х2, у2: integer), которая рисует закрашенный столбец. Цвет закраски устанавливается с помощью SetFillStyle.
Графика в Турбо Паскале 397 Пример использования: SetFillStyle(1,3); Ваг(10,10,50,100) ; Еще одна весьма эффектная процедура: Bar3D(xl, yl, х2, у2: integer; Depth: word; Top: Boolean) вычерчивает трехмерный закрашенный прямоугольник. При этом используются тип и цвет закраски, установленные с помощью процедуры SetFillStyle. Параметр Depth представляет собой число пикселей, задающих глубину трехмерного конту- ра. Чаще всего его значение равно четверти ширины прямоугольника: Depth := (Х2-ХЦ) DIV 4; Параметр Тор определяет, строить над прямоугольником вершину (Тор:= True) или нет (Тор:= False). Например: SetFillStyle(1,3); Bar3D(10,10,50,100,10,True); 15.7.2. Построение многоугольников Прямоугольники можно рисовать самыми различными способами, например с - помощью Line или LineTo. Однако в Турбо Паскале имеется процедура DrawPoly, которая позволяет строить любые многоугольники линией текущего цвета, стиля и толщины. Она имеет формат: DrawPoly(NumPoints: word; var PolyPoints) Параметр PolyPoints является нетипизированным параметром, который содер- жит координаты каждого пересечения в многоугольнике. Параметр NumPoints за- дает число координат в PolyPoints. Необходимо помнить, что для вычерчивания замкнутой фигуры с N вершинами нужно передать при обращении к процедуре DrawPoly N+1 координату, где координата вершины с номером п будет равна коор- динате вершины с номером 1. Проиллюстрируем сказанное следующей програм- мой: program DemoDrawPoly; {Программа вычерчивает в центре экрана тре- угольник красной линией) uses Crt, Graph; var DriverVar, ModeVar: integer; pp : array[1..4] of PointType; xM, yM, xMaxD4, yMaxD4: word; begin DriverVar:=Detect;
398 Глава 15 InitGraph(DrivexVar,ModeVar,'') ; xM:=GetMaxX; yM:=GetMaxY; xMaxD4:—xM DIV 4; yMaxD4 : ==yM DTV 4 ; {Определение координат вершин) pp[l].x := xMaxD4; pp[l].y := yMaxD4; pp[2].x := xm — xMaxD4; pp[2].y := yMaxD4; pp[3].x := xm DTV 2; pp[3] .y := yM — ymaxD4; pp[4] := pp[l]; * SetColor(LightRed); {Цвет для вычерчивания) LrawPoly(4,pp); {4 — количество пересечений + 1) Readln; CloseGraph end. В результате работы программы на экране появится красный треугольник на чёрном фоне. Возникает естественное желание его закрасить, т. е. изменить фон внутри треугольника. Это можно сделать с помощью процедуры FillPoly(NumPoints: word; var PolyPoints) Значения параметров те же, что и в процедуре DrawPoly. Действие тоже анало- гично, но фон внутри многоугольника закрашивается. В качестве примера нарису- ем в левой верхней части экрана четырехугольную звезду зеленого цвета: program DemoFillPoly; {Программа вычерчивает четырехугольную звез- ду) uses Crt, Graph; const Star: array[1..18] OF integer = (75, 0, 100, 50, 150, 75, 100, 100, 75, 150, 50, 100, 0, 75, 50, 50, 75, 0); var DriverVar: integer; ModeVar: integer; begin DriverVar:=Detect; InitGraph(DriverVar,ModeVar,’' ) ; SetFillStyle(1,Green); FillPoly(9,Star); {9 — количество пересечений + 1)
Графика в Турбо Паскале 399 Readln; CloseGraph end. 15.7.3. Построение дуг и окружностей Алгоритмы построения кривых весьма сложны для самостоятельной реализа- ции, поэтому во всех случаях рациональнее пользоваться готовыми процедурами модуля GRAPH. Для задания углов используется полярная система координат (рис. 15.2). Процедура вычерчивания окружно- 9 о ста текущим цветом имеет следующий формат: ♦ Circle (X, Y, Radius: word), / \ где X и Y — координаты центра окруж- / \ ности, a Radius — ее радиус. Например, | | о следующий фрагмент обеспечит вывод I ’ 1 ярко-зеленой окружности с радиусом \ / 360 50 пикселей и центром в точке 450,100: \ / SetColor(LightGreen); X. / Circle(450,100,50) ; _______ В ряде случаев, в частности для 270 создания псевдообъемных фигур, ис- _____ __________ тл.,____________ Рис. 15.2. Полярная система координат пользуются дуги. Их можно вычертить к н с помощью процедуры Arc (X,Y: integer; StAngle, EndAngle, Radius: word) где X, Y — центр окружности, StAngle и EndAngle — начальный и конечный угол, Radius — радиус. Цвет для вычерчивания устанавливается процедурой SetColor. Очевидно, что, если StAngle = 0 и EndAngle = 359, вычерчивается полная окружность. В качестве примера выведем дугу красного цвета от 0 до 90 ° в уже вычерченной с помощью Circle(450, 100, 50) окружности: SetColor(Red); Arc(450,100,0,90,50); Работа с различными типами кривых требует информации о координатах по- следнего обращения к Аге. Ее можно получить, выполнив процедуру GetArcCoords (var ArcCoords : ArcCoordsType) Переменная ArcCoords принадлежит к стандартному типу
400 Глава 15 type ArcCoordsType = record x, у : integer; xStart, yStart : integer; xEnd, yEnd : integer end; Процедура возвращает переменную, содержащую точку центра (X,Y), началь- ную xStart, yStart и конечную xEnd, yEnd позиции последней команды Аге. Для построения эллиптических дуг предназначена процедура Ellipse (X,Y: integer; StAngle,EndAngle: word; xR,yR: word) где X, Y — центр эллипса в дисплейных координатах, xR и yR — горизонтальная и вертикальная оси. * Дуга эллипса вычерчивается от начального угла StAngle до конечного угла EndAngle текущим цветом. Значения StAngle=0 и EndAngle=360 приведут к вычер- чиванию полного эллипса. Пример построения эллипса, выведенного ярко-голубым цветом: SetColor(LightCyan); Ellipse(100,100,0,360,30,50); Обратите внимание, что фон внутри эллипса совпадает с фоном экрана. Чтобы создать закрашенный эллипс, используется специальная процедура FillEllipse (X, Y: integer; xR, yR: word), где X, Y — центр эллипса в дисплейных координатах, xR и yR — горизонтальная и вертикальная оси. Заполнитель устанавливается процеду- рами SetFillStyle или SetColor: SetFillStyle(WideDotFill,Green); {Установка стиля заполнения) SetColor(LightRed); {Цвет для вычерчивания эллипса) FillEllipse(300,150,50,50); В этом фрагменте "эллипс вычерчивается ярко-красной кривой и заполняется приведенной в табл. 15.14 стандартной маской WideDotFill (редкие точки) зеленого цвета. В программах деловой графики часто требуется разделить окружность на сек- тора. Это можно сделать с помощью процедуры PieSlice (X,Y: integer; StAngle, EndAngle, Radius: word) которая рисует и заполняет маской сектор круга. Точка X, Y — центр окружности, а сектор рисуется от начального угла StAngle до конечного угла EndAngle. Контур сектора вычерчивается текущим цветом, а при закрашивании используются тип и цвет закраски, заданные процедурами SetFillStyle и SetFillPattem. Пример исполь- зования PieSlice: SetFillStyle(10,LightGreen); {Установка стиля) SetColor(12); {Цвет для вычерчивания) PieSlice(100,100,0,90,50);
Графика в Турбо Паскале 401 Можно создать и заполнить сектор в эллипсе. Для этого используется проце- дура Sector(X, Y: Integer; StAngle, EndAngle, xR, yR: word) где X, Y — центр, xR, yR — горизонтальный и вертикальный радиусы. Сектор вы- черчивается от начального угла StAngle до конечного угла EndAngle текущим цве- том и заполняется стилем, заданным процедурами SetFillStyle или SetFillPattem. Например: SetFillStyle(CloseDotFill,LightBlue); {Установка стиля} SetColor(LightMagenta); {Цвет для вычерчивания) Sector(300,150,180,135,60,70); 15.7.4. Атрибуты графических фигур Установка цвета и стиля. После того как все средства построения фигур изу- чены, возникает желание сделать изображение более красочным и разнообразным. Этого можно добиться различными способами. Один из них — "заливка" замкну- тых площадей экрана различными видами заполнителя. Например, в деловой гра- фике используются столбиковые диаграммы, заполненные разноцветной штрихов- кой. Качественный вывод фигур требует предварительной установки цвета, фона и заполнителя. Для удобства пользователей в модуль GRAPH включена целая группа предопределенных (стандартных) комбинаций символов-заполнителей для запол- нения внутренних и внешних областей графических фигур. Назовем их маской. Маска может окрашиваться в допустимые для установленной палитры цвета. На- пример, экран может быть заполнен штриховкой разного цвета и направления, точ- ками разной плотности и т.д. Комбинацию маска — цвет принято называть стилем заполнения. Для работы с предопределенными стилями, список которых приведен в табл. 15.14, используются функции GetFillSettings и SetFillStyle. Функция SetFillStyle (Pattern: word; Color: word) устанавливает маску Pattern и ее цвет Color, т.е. определяет стиль заполнения. Значения Pattern приведены в табл. 15.14 и могут быть представлены константой или цифрой, Color берегся из уста- новленной палитры. В следующем примере использована 16-цветная палитра для адаптера EGA/VGA: SetFillStyle(SlashFill,Yellow); Bar(10,10,50,150); {Столбец заполнен маской // желтого цвета} Получить информацию о задействованном стиле (о коде маски и цвета) можно с помощью процедуры GetFillSettings (var Inf: FillSettingsType).
402 Глава 15 Стандартные стили заполнения Таблица 15.14 Константа Значение Маска EmptyFill 0 Заполнение цветом фона SolidFill 1 Заполнение текущим цветом LineFill 2 Заполнение символами —, цвет — color LtslashFill 3 Заполнение символами // нормальной толщины, цвет — color SlashFill 4 Заполнение символами // удвоенной толщины, цвет — color BkslashFill 5 Заполнение символами \\ удвоенной толщины, цвет — color LtbkSlashFill 6 Заполнение символами \\ нормальной толщины, цвет — color HatchFill 7 Заполнение вертикально-горизонтальной штриховкой тонкими ли- ниями, цвет-color k XhatchFill 8 Заполнение штриховкой крест-накрест по диагонали "редкими" тон- кими линиями, цвет—color InterLeaveFill 9 Заполнение штриховкой крест-накрест по диагонали "частыми" тон- кими линиями, цвет—color WideDotFill 10 Заполнение "редкими" точками CloseDotFill 11 Заполнение "частыми" точками ' UserFill 12 Заполнение по определенной пользователем маске заполнения, цвет — color Возвращенная информация находится в переменной Inf, имеющей следующий тип: type FillSettingsType — record Pattern : word; ' Color : word end; Пример извлечения информации об установленной в данный момент стандарт- ной маске и ее цвете: program DemoGetFxllSetting; uses Crt, Graph; var DriverVar, ModeVar: integer; Inf: FillSettingsType; begin DriverVar:=Detect; InitGraph(DriverVar,ModeVar,’’); GetFillSettings(Inf); CloseGraph;
Графика в Турбо Паскале 403 with Inf do begin Writeln('Маска =*, Pattern); Writeln('Цвет =', Color) end end. Если вам не подходят предопределенные маски и вы хотите использовать соб- ственные, необходимо создать новую маску и установить ее с помощью процедуры SetFillPattem (Pattern: FillPatternType; Color: word) где Pattern — ваша маска, Color — ее цвет. Маска пользователя занимает матрицу 8x8 (64 пикселя). Для задания используется 8 байт (64 бита), причем каждый бит "зажигает" или "гасит" соответствующий пиксель в матрице 8x8. Для этого обыч- но применяют предопределенный 8-байтовый массив типа FillPatternType: type FillPatternType = array [1..8] OF byte; В следующей программе создается пользовательский стиль MyPattem и ис- пользуется при заполнении прямоугольника, построенйого процедурой Ваг: program DemoFillPattern; {Формирование и использование пользова- тельского стиля заполнения) uses Crt, Graph; const MyPattern: FillPatternType=($10,$38,$7C,$FE,$7c, $38,$10,$00); var DriverVar,ModeVar:integer; begin DriVerVar:=Detect; InitGraph(DriverVar,ModeVar,*') ; SetFillPattem(MyPattern,LightGreen); Bar(10,10,50,100); {Столбец с нестандартным стилем) Readln end. Необходимую информацию о нестандартных стилях можно получить с помо- щью процедуры GetFillPattern (var Inf: FillPatternType) Она выгружает в массив типа FillPatternType все содержимое маски, В качестве примера приведем несколько измененный фрагмент предыдущей программы: var Inf: FillPatternType;
404 Глава 15 I: byte; begin InitGraph(DrivexVar,ModeVar,*’>; SetFillPattem (MyPattem,LightGreen) ; Bar(10,10,50,100); CloseGraph; for I:= 1 to 8 do Writein(I,'-й элемент маски = ', Inf[I]); end. Рассмотрим последнюю установочную процедуру FloodFill. Она служит для за- полнения с помощью SetFillStyle- и SetFillPattem-стилем внутренней или внешней области фигуры и имеет следующий формат: FloodFill (X, Y: integer; Border: word) где X, Y — координаты точки внутри или вне фигуры, Border — цвет. Второй параметр применяют для заполнения области, ограниченной цветом Border, текущим образцом закраски. Если точка (X,Y) находится внутри замкнутой области, то заполняется внутренняя область. Если эта точка находится вне замкну- той области, то заполняется ее внешняя часть (поле экрана, не входящее в область). Следующая программное помощью FloodFill строит в центре экрана прямоуголь- ник, вся остальная площадь заштрихована. Используются только черный и белый цвета: program DemoFloodFill; uses Graph; var Driver, Mode: integer; begin Driver Detect; InitGraph(Driver, Mode, 1'); SetFillStyle(LtSlashFill, 15) ; Rectangle(60, 50, 600, 300); FloodFill(1,1,15); Readln; CloseGraph; end. Коэффициент сжатия. Для каждого драйвера и графического режима имеется связанный с ним коэффициент сжатия, или коэффициент пропорциональности (aspect ratio). Фактически он является мерой геометрических размеров пикселя на
Графика в Турбо Паскале 405 экране монитора и вычисляется как результат деления ширины пикселя на его вы- соту. На первый взгляд, ширина и высота пикселя равны, но это не так. Для многих мониторов световое пятно, которое соответствует пикселю, не яв- ляется строго квадратным, а напоминает по форме вытянутый вверх эллипс. Как следствие этого линия на экране, состоящая из одного и того же числа пикселей, расположенная вертикально, выглядит на экране длиннее, чем линия из того же числа пикселей, расположенная горизонтально. По этой же причине вывод прямо- угольника, имеющего равные (в пикселях) горизонтальную и вертикальную сторо- ны, не приводит к получению на экране квадрата. В качестве примера рассмотрим программу, которая выводит два теоретически одинаковых прямоугольника: program DemoAspectRatio; {Пример для драйвера EGAVGA, разрешение экрана 639x349) * uses Crt, Graph; var DriverVar, ModeVar: integer; procedure Box(xl, yl, x2, y2, Color: integer); begin SetColor(Color); Line(xl,yl,xl,y2); Line(xl,y2,x2,y2); Line(x2,y2,x2,yl); Line(x2,yl,xl,yl) end; {Конец Box) procedure BoxAsp(xl, yl, x2, y2, Color: integer); const AspectRatio = 1.17; begin y2 :— Round (y2/AspectRatio) ; SetColor(Color); Line(xl,yl,xl,y2); Line(xl,y2,x2,y2); Line(x2,y2,x2,yl); . Line(x2,yl,xl,yl) end; {Конец BoxAsp) begin DriverVar := Detect; . InitGraph(DriverVar,ModeVar,•'); Box(0,0,100,100, 14) ; Readln; BoxAsp(200,0,300,100,14) ; Readln;
406 Глава 15 CloseGraph end. Прямоугольники, полученные этой программой, будут различаться из-за раз- личных значений коэффициента сжатия. Еще больший искажающий эффект будет при выводе окружностей, так как идеальный круг на практике выглядит сильно вы- тянутым эллипсом. Система графики Турбо Паскаля позволяет программисту корректировать ко- эффициент сжатия. Если он равен 1, сложные геометрические примитивы, такие, как дуги, окружности, появляются на экране геометрически корректными. При дру- гих значениях коэффициента сжатия в случае вывода, например, окружности будет получен эллипс. По умолчанию после инициализации системы графики автоматически устанав- ливается коэффициент сжатия в соответствии с характеристиками аппаратуры ви- деосистемы. Однако не всегда результат такой установки удовлетворит пользовате- ля. Чаще приходится самому подбирать этот коэффициент путем проб и ошибок, используя специально предназначенные процедуры GetAspectRatio и SetAspectRatio. GetAspectRatio (var xAsp, 'yAsp: word) Процедура возвращает две переменные, которые характеризуют физическую форму пикселя. Для размера пикселя по вертикали yAsp всегда возвращается 10000. Если световое пятно на экране, соответствующее пикселю, является квадрат- ным (как для адаптера VGA), то и значение "ширины" пикселя равно 10000. Для других видеоадаптеров пиксель на экране имеет эллипсообразную форму с большей полуосью, сориентированной по вертикали. Для таких адаптеров в ячейке, на которую указы- вает xAsp, возвращается значение, меньшее 10000. Зная геометрические размеры пикселя, можно так скорректировать параметры для функций вывода, чтобы получить на экране геометрически пропорциональные фигуры, например квадраты. Отметим, что не следует корректировать коэффици- ент сжатия для вывода окружностей, дуг или секторных диаграмм. Изменить коэф- фициент сжатия можно посредством процедуры SetAspectRatio (xAsp, yAsp: word) Она устанавливает новое значение коэффициента сжатия, которое будет ис- пользоваться при выводе геометрических примитивов — прямоугольников, дуг, ок- ружностей, эллипсов. Аргумент xAsp отображает в условных единицах ширину пикселя на экране, yAsp — высоту пикселя. Например, если известно, что пиксел на экране больше по высоте в 1,2 раза, чем по ширине, геометрически корректный вывод будет получен при задании следующего коэффициента сжатия: SetAspectRatio(100, 120); Область применения функции — корректировка вывода графической информа- ции при использовании нестандартных мониторов, для которых Турбо Паскаль не
Графика в Турбо Паскале 407 может автоматически определить корректное значение коэффициента сжатия, а также корректировка графического вывода для мониторов с нестандартной линей- ностью по вертикали и горизонтали. Проиллюстрируем сказанное следующим примером: program DemoSetAspectRatio; uses Crt, Graph, Printer; var DriverVar, ModeVar: integer; procedure CR; var xA, yA: word; » begin GetAspectRatio(xA,yA); Writein(Lst,'x= ',xA,’ y=',yA, ’ Ratio=*, (1.0*xA) /yA: 4:2); Circle(GetMaxX—(getMaxX div 3),GetMaxY div 2,GetMaxY div4); Readln end; {Конец процедуры} begin DriverVar:=Detect; InitGraph(DriverVar,ModeVar,'') ; CR; SetAspectRatio(1,1); CR; SetAspectRatio(1,2) ; CR; SetAspectRatio(2,1); CR; SetAspectRatio(1,1); CloseGraph end. На печатающее устройство будут выведены следующие данные: х=7750 у= 10000 Ratio=0.78 х=7750 у= 10000 Ratio=0.78 х= 1 у= 1 Ratio= 1.00 х= 1 у= 2 Ratio= 0.50 х— 2 у= 1 Ratio= 2.00
408 Глава 15 Очевидно, что действительно правильная окружность будет сформирована только при первом старте процедуры CR. Все остальные "окружности" будут пред- ставлены как вытянутые ito вертикали эллипсы разной высоты, хотя и вычерчива- ются с помощью Circle. Упражнения Упражнение 1. Изучите текст программы, которая переводит видеомонитор в графиче- ский режим, выводит сообщение о режиме, затем рисует на экране прямоугольник, не- сколько окружностей увеличивающегося радиуса, заполняет экран точками случайного цве- та с координатами, заданными случайными числами, после чего вычерчивает график Sin(x) и в завершение переводит видеомонитор в текстовый режим. program Dem_Gral ,* * uses Crt, Graph; var DriverVar, ModeVar, ErrCode,!,J,X,Radius : Integer; X_t, Y_t : string; begin ClrScr; Writeln(’Текстовый режим. Для продолжения нажмите Enter'); Readln; DriverVar:= Detect; InitGraph(DriverVar,ModeVar,’’); {Инициализация графического ре- жима} SetTextStyle(DefaultFont,HorizDir,I) ; {Драйвер шрифта по умолча- нию} ErrCode := GraphResult; if ErrCode = grOk then {Если инициализация графического режима успешна} begin for I:= 0 to ModeVar do {Изменять графический режим от 0 до ModeVar} begin SetGraphMode(I); Str(GetMaxX,X_t); {Преобразовать числовое значение, воз- вращаемое функцией GetMaxX, в строку для вывода на экран в графи- ческом режиме} Str(GetMaxY,Y_t); OutTextXY(I*30,GetMaxY div 2,'Графический режим: • +X_t+ ’ х • +Y_t) ; Rectangle(1*20, 1*20,GetMaxX-I*20,GetMaxY-I*20); for Radius := 1 to 10 do{Нарисовать 10 окружностей}
Графика в Турбо Паскале 409 begin SetColor(Radius); Circle(GetMaxX div 2,GetMaxY div 2,Radius * 20); Delay(200); end; for J:=l to 10000 do {Поставить на экране 10000 точек} PutPixel(Random(GetMaxX), Random(GetMaxY), Random(15)); end; ClearViewPort; {Очистить окно} SetColor(15); Line(0,GetMaxY div 2, GetMaxX,GetMaxY div 2); for X:=0 to GetMaxX do {Начертить график sin(x)} PutPixel(X,Trunc(Sin(X/20)*70)+GetMaxY div 2,2); OutTextXY(150,GetMaxY—100,'Для продолжения нажмите Enter'); Readln; end else {Если инициализация графического режима с ошибкой ErrCode} Writeln('Ошибка графики :', GraphErrorMsg(ErrCode)); CloseGraph; RestoreCRTMOde; {Восстановить текстовый режим} Writeln('Опять текстовый режим > Для продолжения нажмите Enter'); Readln; end. Запустите интегрированную среду программирования, введите текст программы Dem Gral и запишите на диск под этим именем, затем откомпилируйте его и проверьте дей- ствие программы в пошаговом режиме. Обратите внимание на значения переменных DriverVar, ModeVar, ErrCode. Упражнение 2. Изучите программу, которая в текстовом режиме запрашивает значе- ния десяти параметров и строит по ним столбиковую диаграмму (гистограмму) в графиче- ском режиме. program Dem_Gra2; {Построение столбиковой диаграммы} uses Crt, Graph; const Count«10; Width = 40; {Ширина столбца диаграммы} var I,X1,X2,Y1,Y2 : integer; M : array[1..Count] of byte; DriverVar, ModeVar: integer;
410 Глава 15 S_M : string; begin Writein('Ввод данных (целые числа) для построения диаграммы'); for I:«l to Count do begin repeat {Ввод с контролем, входит ли введенное значение в [1..10]} Write('Введите значение ',1,'-го параметра (от 1 до 10) :'); Readln(M[IJ); if not M[I] in [1..10] {Если введенное значение не входит в, интервал [1..10]} then Writein('Значение параметра должно быть от 1 до 10'); until M[I] in [1..10J; * ] end; s DriverVar:"Detect; {Инициализация графического режима} i InitGraph(DriverVar,ModeVar,''); SetViewPort(10,10,630,400,True); {Создать окно} SetTextStyle(DefaultFont,HorizDir,1); й Yl:=325; {Построение гистограммы} j for I:"l to Count do {Повторять, пока не построим все столбики}* begin ; XI:-1*50; ] Str(M[I],S_M) ; {Преобразовать значение М[1] в строку для J вывода в графическом режиме на экран} SetFillStyle(1,1); {Задать стиль и цвет заполнения} Bar3D(Xl,Yl,Xl+Width,Yl—M[I] *30,10,ТорОп); {Построить столби-] ковую диаграмму} । OutTextXY(X1+15,Y1-M[I] *30—15,S_M); {Напечатать над столби- ком значение отображаемой величины} ~ | end; {Конец цикла} {Вывод пояснительных надписей} j SetTextStyle(DefaultFont,HorizDir,2); | OutTextXY (150,20,'Пример гистограммы'); SetTextStyle(DefaultFont,VertDir,l); , J OutTextXY(40,175,'Величина параметра'); ! SetTextStyle(DefaultFont,HorizDir,1); 1 OutTextXY(250,GetMaxY—140,'Параметры'); j OutTextXY(150,GetMaxY—100,'Для Завершения нажмите Enter'); Readln; я CloseGraph; end. J
Трафика в Турбо Паскале 411 Запустите интегрированную среду программирования, введите текст программы uem_Gra2 и запишите на диск под этим именем, затем откомпилируйте его и проверьте дей- ствие программы. Упражнение 3. Изучите текст программы, которая устанавливает новый шрифт и выво- дит на экран строку 'Пример шрифта*. program Dem_Gra3; uses Graph,Crt; var Driver, Mode: Integer; Font: Integer; begin * Font := InstallUserFont('Goth'); {Задать новый шрифт} if GraphResult <> .grOk then begin Writeln('Ошибка инсталляции шрифта (используется шрифт по умол- чанию) ’) ; Readln; end; Driver := Detect; InitGraph(Driver, Mode, ’ ’);{Инициализировать графический режим} if GraphResult <> grOk then Halt(l); SetTextStyle(Font, HorizDir, 7); {Установить текущий шрифт, стиль и размерный коэффициент символа} OutText('Пример шрифта'); ReadLn; CloseGraph; end. Запустите интегрированную среду программирования, введите текст программы Dem_Gra3 и запишите на диск под этим именем, затем откомпилируйте его и проверьте дей- ствие программы, изменяя в строке: Font := InstallUserFontfGoth'); шрифт Goth на Sysv, Sans и другие шрифты, имеющиеся в вашем распоряжении. Упражнение 4. Изучите текст программы, выводящей на экран узоры, образованные рисованием линий, координаты начала и конца которых задаются тригонометрическими функциями. program Dem_Gra4; uses Crt, Graph; var
412 Глава 15 DriverVar, ModeVar, ErrCode,A4,A3,B4,B3,Cl,Dx,Dy : integer; X1,X2,Y1,Y2,D,T : real; Play : boolean; begin ч j DriverVar:= Detect; {Инициализация графического режима} InitGraph(DriverVar,ModeVar,’’) ; ; s repeat {Повторять, пока не нажата любая клавиша} •• if Random(5) =0 then Play:“True else Play:“False; j SetFillStyle(1, White); j Bar (0,0,GetMaxX,GetMaxY); {Заполнить экран цветом White стилем 1} J SetColor(0); A4:“Random(5); A3:“Random(5); B4: “Random (5) ; B3:“Random(5); Cl:“Random(5)*2; T:=0; SetColor(Random(4)); repeat {Повторять, пока T<5} if Play and (Random(5)=l) then SetColor(Random(14)); {Изменение цвета рисунка} D:=T+C1; {Задать диапазон изменения координат концов линий} XI:=(1.5*Cos(А4*Т)+1.5*Cos(АЗ*2*Т))*60; Х2:=(1.5*Cos(A4*D)+1.5*Cos(A3*2*D))*60; Yl: “ (1.5*Sin (B4*T) +1.5*Sin (B3*2*T) ) *60 ; Y2:=(1.5*Sin(B4*D)+1.5*Sin(B3*2*D))*60; Dx:“GetMaxX div 2; Dy:-GetMaxY div 2; {Начертить линию} Line (Round (Yl) +Dx,Round (XI) +Dy,Round(X2)+Dx,Round (Y2) +Dy) ; I:=1+0.01; until (T>=5); {Завершить рисование данной фигуры} SetColor(15); Delay(500); until KeyPressed; {Завершить вывод рисунков так как нажата кла-| виша} CloseGraph; end. Запустите интегрированную среду программирования, введите текст программа! Dem_Gra4 и запишите на диск под этим именем, затем откомпилируйте его и проверьте дем ствие программы.
Графика в Турбо Паскале 413 Упражнение 5. Изучите программу, которая выводит изображение на экран, используя многостраничный вывод. program Dem_Gra5; uses Crt, Graph; var DriverVar, ModeVar, ErrCode,I,Xl,X2,Yl,Y2,Color : Integer; begin DriverVar:= Detect; {Инициализация графического режима} InitGraph(DriverVar, MOdeVar, * *) ,* ErrCode := GraphResult; if ErrCode = grOk then if DriverVar in [HercMono,EGA,EGA64,MCGA,VGA] then begin {Допускается многостраничный режим} if DriverVarOHerdMOno then SetGraphMdde(ModeVar—1) ; .{Использовать многостраничный режим} SetTextStyle(DefaultFont,HorizDir,2); {Задать стиль текста} Randomize; SetActivePage(0); {Заполнить страницу 0} SetBkColor(1); {Установить цвет фона} for I:=l to 1000 do {Нарисовать 1000 случайно расположенных линий} begin SetColor(Random(15)+1); Line(Random(GetMaxX div 10), Random(GetMaxY), Random(GetMaxX), Random(GetMaxY)); end; OutTextXY(50,150,1видеостраница O'); SetActivePage(1); {Заполнить страницу 1} SetColor(Random(GetMaxColor)+1); for I:=l to 100 do begin Color:=Random(15)+1; {Задать случайным образом цвет ри- сунка} SetColor(Color); XI:=Random(GetMaxX); Y1:=Random(GetMaxY); Y2:=Random(GetMaxY); Circle(X1,Y1,Y2 div 5); {Начертить окруж- ность} • SetFillStyle(1,Random(Color)+1); {Установить стиль и цвет закраски}
414 Главам FloodFill(X1,Y1,Color);{Закрасить экран внутри окружно- сти} end; OutTextXY(50,150,'видеостраница 1•); Readln; {Демонстрировать страницы} SetVisualPage(1); Readln; {Ожидать нажатия Enter} SetVisualPage(0); Readln; ClearViewPort; {Очистить текущее окно} end; CloseGraph; {Вбзврат в текстовый режим} end. Запустите интегрированную среду программирования, введите текст програмй Dem_Gra5 и запишите на диск под этим именем, затем откомпилируйте его и проверьте д| ствие программы. Упражнение 6. Изучите программу, которая рисует на экране звездное небо и пер«| щает на его фоне рисунок НЛО. program NLO; uses Crt, Graph; const R = 20; Pause — 50; var DriverVar, ModeVar, ErrCode, Xmin, Xm, Ymin, Ym, X, Y, Tx, Ту, Rx, Ry, Size,I,Dx,Dy ^fidth,Height % integer; Sauser : Pointer; begin Randomize; DriverVar:•» Detect; {Инициализация графического режима} InitGraph(DriverVar,ModeVar,'’) ; ErrCode := GraphResult; if'ErrCode <> grOk then Writeln(GraphErrorMsg(ErrCode)) else begin {Инициализация графического режима выполнена успешно}^ SetTextStyle(DefaultFont,HorizDir,2); OutTextXY(50,10,'Демонстрация движения НЛО');
Графика в Турбо Паскале 415 {Рисуем НЛО} X:=R*5; Y:=R*2; Xm:«GetMaxX - 5; Ym: «GetMaxY - 25; Ellipse(X,Y,O,360,R,R div 3 +2); Ellipse(X,Y—4,190,357,R,R div 3) ; Line(X+7,Y-6,X+10,Y-12); Line(X—7,Y—6,X—10,Y—12); Circle(X+10,Y—10,2); Circle(X—10,Y—10,2); FloodFill(X+1,Y+4, White); {Определяем габариты НЛО и помещаем в кучу} Тх:=Х—R—1; Ty:=Y-14; / Rx:=X+R+l; Ry:=Y+R div 3+3; Width:=Rx — Tx+1; Size:«ImageSize(Tx,Ту,Rx,Ry); GetMem(Sauser,Size); Getlmage(Tx,Ty,Rx,Ry,SauserA); {Стираем построенное изображение} PutImage(Tx,Ty,SauserA,XorPut) ; {Рисуем звездное небо} Xmin: =0 ; Ymin:=30; SetFillStyle(1,Blue); {Установить стиль и цвет закраски го- лубое небо} SetColor(White); {Начертить прямоугольник и открыть окно} 1 Rectangle(Xmin,Ymin,GetMaxX,GetMaxY); SetViewPort(Xmin,Ymin,GetMaxX,GetMaxY,ClipOn); FloodFill(Xmin+1,Ymin+1,White); for I:=l to 500 do {Нарисовать 500 звезд белым цветом} PutPixel(Random(GetMaxX),Random(GetMaxY—Ymin),White); {Задаем начальное положение НЛО} Х:= Xm div 3 — Xmin; Y:= Ym div 3 — Ymin; Dx:=6;
416 Глава 151 ___________________________________._______________ _______________ J ................................ , -j Dy:=6; repeat {Основной цикл: повторять, пока не нажата любая клавиша} PutImage(X, Y, SauserA, XORPut); {Изображаем объект} Delay(Pause); PutImage(X, Y, SauserA, XORPut);{После паузы стираем объ- ект) {Перемещаем объект} if (X<Xmin) or (Y<Ymin) or (X+Width+l>Xm) or (Y+Helght+l>Xm) then begin {Если объект смещается влево-вверх за границы ок-| на, изменить его координаты так, чтобы он оставался в окне) ’ if (X-Dx < Xmin) then x := XmAi else X:=X—Dx; if (Y—Dy < Ymin) then Y := Ymin else Y:=Y-Dy; Dx:“GetMaxX div 10 — Random (GetMaxX div 4); Dy:“GetMaxY div 10 — Random (GetMaxY div 4) ; end else begin{Если объект смещается вправо-вниз за границы окна}| if (X+Dx < Xm) then X:=X+Dx else X:“Random (GetMaxX) —Random(GetMaxX div 4) ; if (Y+Dy < Ym) then Y;=Y+Dy else Y: “Random (GetMaxX)—Random (GetMaxX div 3) ; end; until KeyPressed; {Завершить, Хак только будет нажата любая клавиша} FreeMem(Sauser, size); CloseGraph; end; end. Запустите интегрированную среду программирования, введите текст программы NLO j запишите на диск под этим именем, затем откомпилируйте его и проверьте действие прв граммы. Упражнение 7. Изучите текст программы, которая рисует модель атома." program Model_At; uses Crt,Graph; const Ra=100; . {Радиус атома)
Графика в Турбо Паскале 417 Rc=10; {Радиус ядра} Re=4; {Радиус электрона} К=0.5; {Коэффициент сжатия орбит электронов} Dr=30; {Параметр изменения координат электрона} Stepl=0.2; {Шаг изменения положения электрона} Step=100; {Время задержки — скорость движения электронов} var Сх,Су,Yl,Y,Y2,Х,Х1,Х2,ХЗ, Y3: integer; 1,11,12,13 : real; DriverVar, ModeVar : integer; 1 begin ClrScr; * DriverVar := Detect; InitGraph(DriverVar,ModeVar,’’) ; SetTextStyle(DefaultFont,HorizDir,2); OutTextXY(200,30,'Модель атома'); Cx:=GetMaxX div 2; {Определить центр экрана — положение ядра атома} Cy:=GetMaxY div 2; PieSlice(Сх,Су,0,360,Ro) ; {Нарисовать ядро атома} SetColor(Red); SetLineStyle(0,0,3) ; Line(Cx—7,Су,Сх+7,Су) ; Line(Cx,Cy—5,Cx,Cy+5) ; SetLineStyle(0,0,1) ; SetFillStyle(1,1) ; I:=Pi/4; {Задать начальные положения четырех электронов} Il:=—Pi/4; I2:=—Pi/2; l3:=Pi/2; SetTextStyle(DefaultFont,HorizDir,1); SetColor(Yellow); OutTextXY(180,420,'Для отмены нажмите любую клавишу*); while not KeyPressed do {Повторять, пока не нажата любая кла- виша} begin {Определить координаты электронов} X:=Round(Ra*Cos(I))+Сх; Y :=Round(K*Ra*Sin(I))+Су; XI:=Round((Ra+Dr)*Cos(II))+Cx; Yl:=Round(K*(Ra+Dr)*Sin(II))+Cy; X2:=Round((Ra—Dr)*Cos(12))+Cx; 14-116
418 Глава 15 Y2:-Round(К*(Ra-Dr)* Sin(12))+Cy; X3: ««Round((Ra—Dr)*Cos(13)*2.3)+Cx; Y3:=Round(K*(Ra—Dr)* Sin(13)*2.3)+Cy; {Установить синий цвет и нарисовать электроны} SetColor(1); Circle(X,Y,Re); PutPixel(X,Y,2); Circle(XI,Yl,Re)/PutPixel(XI,Yl,2); Circle(X2,Y2,Re)/PutPixel(X2,Y2,2); Circle(X3,Y3,Re)/PutPixel(X3,Y3,2); Delay(Step); SetColor(0); {Нарисовать электроны цветом фона} Circle(X,Y,Re); Circle(XI,Yl,Re); Circle(X2,Y2,Re); Circle(X3,Y3,Re); {Задать изменение положения электронов) I:=I+Stepl; Il:=I1-Stepl; 12:=I2+Stepl; 13:=I3+Stepl; end; {Конец цикла} CloseGraph; end. Запустите интегрированную среду программирования, введите текст программы Model_At и запишите на диск под этим именем, затем откомпилируйте его и проверьте дей- ствие программы. 'J Контрольные вопросы и задания Вопросы. 1. Чем объяснить разные возможности текстового и графического режимов видеомони-^ тора? - J 2. За счет каких элементов компьютера ^обеспечивается аппаратная поддержка графики?^ 3. Какие основные типы видеомониторов персональных компьютеров типа IBM PC вид знаете? Их отличия. | 4. Что такое видеоадаптер, каково его назначение, из каких основных частей он соски ит? 5. Отличия адаптеров: MDA, CGA, EGA, VGA. 6. Зачем видеоадаптеру нужна видеопамять? Что такое видеостраница? Что такое види^ мая страница, активная страница? 7. Какие процедуры предназначены для работы с видеостраницами? 8. Каково назначение графических драйверов? В каких файлах они находятся? Назначе| ние процедуры RegisterBGIDriver? .
Графика в Турбо Паскале 419 9. Назначение стандартного библиотечного модуля GRAPH? Как запустить графиче- скую систему? 10. Как включить графический режим? Назначение процедуры InitGraph н ее парамет- ров: DriverVar, ModeVar, PathToDriver? Как перейти в текстовый режим и обратно? Как за- крыть графический режим? 11. Какие стандартные функции можно использовать для своевременного обнаружения и нейтрализации ошибок в графическом режиме? 12. Какова система координат, используемая при построении графических изображений на экране? 13. Какую роль выполняет в графическом режиме невидимый текущий указатель СР (Current Pointer)? Как его переместить? 14. Что такое окно? Как создать окно в графическом режиме? Как установить фон гра- фического окна? - 15. Какие процедуры используются для работы с графическим примитивом—точкой? 16. Какие процедуры используются для работы с графическим примитивом — линией? 17. Какие процедуры используются для вывода текста в графическом режиме? Каковы особенности вывода численных данных в графическом режиме? 18. Каково отличие растровых и векторных шрифтов? Как установить шрифт, задать его размер, ориентацию? 19. Какая система используется В CGA-адаптерах для работы с цветом? Что такое па- литра? 20. За счет чего в EGA/VGA-адаптерах расширяется гамма цветов по сравнению с CGA? 21. Как можно получить информацию о текущей палитре? Как задать цвет фона, рисун- ка? Как изменить всю палитру? 22. Какие процедуры используются при рисовании прямоугольников и многоугольни- ков? 23. Опишите полярную систему координат. Какие стандартные процедуры используют- ся при рисовании окружностей, дуг, эллипсов, секторов? 24. Какие процедуры используются для установки цвета н стиля геометрических фигур? Задания. . > 1. Установите графический режим и с помощью Random заполните экран 30000 точек случайного цвета. 2. Постройте желтую линию с координатами краев (0,0),(630,0) с помощью процедуры PutPixel. 3. Постройте рамку цвета LightCyan по краям экрана с помощью процедуры Line. 4. В верхней части экрана создайте область, ограниченную рамкой цвета LightGreen и текстом в центре: "Банк данных школы № 86 г.Воронежа". Цвет для текста—LightCyan.
420 Глава 15 5. В нижней части экрана создайте область, ограниченную рамкой цвета LightGreen и ? текстом в центре: "Для продолжения программы нажмите Enter". Выводимый текст должен трижды изменить цвет с LightCyan на LightRed. 6. Разметьте экран на три области: заголовочную, диалогов, подсказки, как размечена j оболочка IDE Turbo Pascal 5.5/6.0. Для рамок используйте цвет Yellow. Соответственно вы- ведите сообщения: "Заголовок", "Рабочая область", "Подсказка" цветом LightCyan. 7. Используя произвольный цвет, постройте двухмерный закрашенный прямоугольник и внизу сделайте подпись белого цвета: "1992". Параметра для SetFillStyle = (1,3). 8. Нарисуйте "бублик" желтого цвета произвольного радиуса. 9. Разметьте экран на три области: заголовочную, диалогов, подсказки. Для рамок ис- пользуйте цвет Yellow. Соответственно выведите сообщения: "Заголовок", "Рабочая об- ласть”, "Подсказка" цветом LightCyan. Центральную часть, исключая рамку, оформите как окно с фоном Cyan и заполните его 30000 точек случайного цвета. Для очистки и установки фона графического окна используйте процедуру Ваг. 10. Нарисуйте круг цвета произвольного радиуса, закрасив цветом Cyan все, что лежит! все этого круга. | 11. В центре экрана нарисуйте эллипс, закрашенный цветом LightGreen, всю остальную! площадь экрана сделайте фиолетовой. 12. Находясь в графическом режиме, присвойте двум переменным целые значения, сло-1 жите их и выведите результат в центр экрана. Так как вывести число в графике нельзя, пре-| образуйте результат в строку отдельной пользовательской функцией. Используйте процедур ру Str. 13. Находясь в графическом режиме, присвойте двум переменным дробные значении сложите их и выведите результат в центр экрана. Так как вывести число в графике нельзяд преобразуйте результат в строку отдельной пользовательской функцией. Используйте про-| цедуру Str. 14. Постройте оси X,Y и начертите цветом LightRed график функции Y=X длЯ Х=1..4ОО. Учитывая то, что отсчет будет вестись от левой нижней точки экрана, функций X =Y будет выглядеть как Y=349—X. 15. Заданы пять значений количества заболевших гриппом в классе за последние гопЯ лет: 12 (1990), 30 (1991), 11 (1992), 21 (1993), 4 (1994). С помощью Bar3D постройте дни грамму, отражающую динамику (в высоте столбца) заболеваемости. Под каждым столбцов укажите год. Исходные данные опишите в типизированном массиве.
Часть V ВВЕДЕНИЕ В ПРОГРАММИРОВАНИЕ НА ТУРБО ПАСКАЛЕ В СРЕДЕ WINDOWS В 90-х годах 20 века среди массовых пользователей стала популярна операци- онная среда Microsoft Windows. До недавнего времени большинство программи- стов для среды Windows использовали язык программирования С и множество ин- струментальных средств, которые работают в среде DOS. В результате этого про- цесс разработки программ для Windows был очень длительным и сложным. В от- личие от рассмотренного в главах 2-15 Турбо Паскаля версии 7.0 Турбо Паскаль для Windows содержит ряд важных новых методов, которые устраняют сложности разработки приложений Windows. Наряду со всеми характеристиками, которые нам знакомы по Турбо Паскалю версии 7.0, Турбо Паскаль для Windows обеспечивает использование и создание библиотек динамической компоновки (DLL), прямое включение файлов ресурсов Windows, ряд новых типов данных и модернизированную версию стандартных мо- дулей DOS. Первая особенность, на которую обращает внимание программист,- это то, что интегрированная среда разработки (IDE) Турбо Паскаля работает в Windows как программа Windows. Это означает, что вы можете в ней писать, компилировать и тестировать ваши приложения, полностью используя возможности многозадачного режима Windows. Для разработки и модификации таких элементов задачи, как меню, диалоги, пиктограммы и курсоры (все это называется ресурсами) визуально, без какого-либо программирования используется полный набор редакторов ресурсов и компилятор ресурсов фирмы Microsoft. Компилятор ресурса позволяет вам скомпилировать сценарий ресурса, написанный другим программистом Windows. Примечание: ресурс—элементы задачи: меню, диалоги, побитовые распределения, пиктограммы, курсоры и тому подобное. В отличие от MS DOS Windows содержит большое количество нововведений, например, работа с текстом и графикой в окнах изменяющегося размера, взаимо- действие с другими программами в многозадачной среде и так далее. Для того что- бы программа была квалифицирована, как программа Windows, от нее требуется достаточно много. Например, нельзя осуществлять запись непосредственно на эк-
422 Глава 16 ран. Нельзя непосредственно модифицировать память. Кроме того, приложение Windows должно "знать" как следует реагировать на поток сообщений, которые Windows посылает приложению в ответ на события, генерируемые пользователем, например, выбор варианта из меню. Большинство вариантов поведения, которыми должно обладать каждое окно, описано в объектно-ориентированной библиотеке ObjectWindows. Замечательной особенностью является возможность наследования всех этих способностей вместо их разработки заново при начале написанияновой программы. Использование сре- ды программирования Турбо Паскаль для Windows дает возможность сконцентри- роваться на содержательной части программы вместо непроизводительной затраты усилий на тех частях программы, которые являются общими для всех приложений Windows. Для отладки ваших программ можно использовать мощный отладчик Turbo Debugger для Windows. Он обеспечивает полное распознавание выражений Турбо Паскаля и ассемблера, специальные средства отладки приложений Windows и объ- ектно-ориентированных программ. • Отличительным способом программирования в среде Турбо Паскаль для Windows является использование объектно-ориентированного программирования, поэтому главу 16 мы посвятим рассмотрению введения в объектно- ориентированное программирование.
Глава 16. ВВЕДЕНИЕ В ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ 16.1. Идея объектно-ориентированного программирования Объектно-ориентированное программирование (ООП) представляет собой спо- соб программирования, который напоминает процесс человеческого мышления. ООП возник в результате естественной эволюции более ранних нововведений в разработке языков программирования. ООП более структурированный, чем дру- гие способы программирования и позволяет создавать модульные программы с представлением данных на определенном уровне абстракции. Объектно- ориентированный язык программирования характеризуется тремя основными свой- ствами. Инкашуляция — это объединение записей с процедурами и функциями, ра- ботающими с полями этих записей, которое формирует новый тип данных—объ- ект. Наследование — определение объекта и дальнейшее использование всех его свойств для построения иерархии порожденных объектов с возможностью для ка- ждого порожденного объекта, относящегося к иерархии, доступа к коду и данным всех порождающих объектов. Полиморфизм — присваивание определенному действию одного имени, ко- торое затем совместно используется по всей иерархии объектов сверху донизу, причем каждый объект иерархии выполняет это действие характерным именно для него способом. Язык Турбо Паскаль предоставляет программисту все средства объектно-ориентированного программирования: высокую степень структурирован- ности,. модульность, абстрактность и возможность повторного использования (на- следования), — которые встроены непосредственно в язык. Программы, написан- ные с использованием ООП, являются более гибкими и более открытыми для вне- сения изменений.
424 Глава 16 16.1.1. Что такое объекты? Весь окружающий нас мир состоит из объектов, предметов живой и неживой' природы, которые представляются как единое целое, а отдельные части объектов образуют сложное взаимодействие друг с другом. При структурном подходе про- граммист обычно разделяет (структурирует) описываемый объект на составные части, стараясь описать свойства отдельных частей, не вдаваясь в подробности взаимодействия между ними, что, вообще говоря, не является лучшим способом ‘ программирования. Назовем объектом понятие, абстракцию или любую вещь С четко очерченными границами, имеющую смысл в контексте рассматриваемой прикладной проблемы. Например: форточка, Банк "Империал", Петр Сидоров, дело № 7461, сберкнижка и?; т.Д. » -j Все объекты могут быть отличны один от другого: пусть у нас есть два? яблока, имеющие одинаковый цвет, форму, вес и вкус; все равно это два яблока (as не одно), в чем легко убедиться, съев одно из них (другое останется). Между] объектами можно установить отношение тождества: объекты, удовлетворяющие этому отношению, одинаковы (тождественны), как вышеупомянутые яблоки. j Объекты являются высшим уровнем абстракции данных. Объект можно разде-: лить на части, но тогда он перестанет быть объектом. Отношения частей к целому^ и взаимоотношения между частями становятся понятнее тогда, когда все содер-> жится вместе как единое целое. Это называется инкапсуляцией и является очень^ важным понятием. > Не менее важным является и тот факт, что объекты могут наследовать характерна стики и поведение других объектов, называемых порождающими, или родители скими объектами (или "предками"). Здесь происходит качественный скачок —н№ следование, которое является самым существенным различием между обычным программированием на Паскале и объектно-ориентированным программирование*^ в Турбо Паскале. Классы объектов Два яблока из предыдущего примера принадлежат одному и тому же класс#" объектов (именно с этим связана их одинаковость). Цвет, форма, вес и вкус яблоке - это его атрибуты: совокупность атрибутов и их значений (например, красное^ овальное, стограммовое, кисло-сладкое) характеризует объект. Все объекты с одинаковыми наборами атрибутов принадлежат к одномЙГ классу. Однако объединение объектов в классы определяется не наборам! атрибутов, а семантикой (смыслом). Так, например, объекты конюшня и лошади могут иметь одинаковые атрибуты: цена и возраст. При этом они могут относиться к одному классу, если рассматриваются в задаче просто как товар, либо к разны|| классам, что более естественно. Объединение объектов в классы позволяет ввести в задачу абстракцию | рассмотреть ее в более общей постановке. Класс имеет имя (например, лошаДЙ
Введение в объектно-ориентированное программирование 425 которое относится ко всем объектам этого класса. Кроме того, в классе вводятся имена атрибутов, которые определены для объектов. В этом смысле описание класса аналогично описанию типа структуры (записи); при этом каждый объект имеет тот же смысл, что и экземпляр структуры (переменная или константа соответствующего типа). Иерархия объектов Обычно, классифицируя некоторый объект, мы задаем следующие вопросьп'Чем этот объект похож на другие объекты общего класса и чем он отличается от других объектов?иКаждый конкретный класс имеет свои особенности поведения и харак- теристик, определяющих этот класс. Например, класс геометрических фигур можно представить как два подкласса: плоские фигуры и объемные фигуры. Плоские фи- гуры могут иметь вершины и не иметь их. НЬ имеющие вершин плоские фигуры представляются окружностями и эллипсами. Геометрический объект Объемный Плоский Окружность Эллипс Рис. 16.1. Иерархия геометрических фигур Задавая себе приведенные выше вопросы, программист продвигается от верши- ны иерархического дерева данного класса и проходит по дочерним подклассам. Наивысший уровень — самый общий,а вопросы—самые простые. Например, фигу- ры плоские или объемные? Каждый последующий уровень более специфический, чем предыдущий, и менее общий. На самом последнем уровне программист опре- деляет цвет, стиль заполнения, величину радиуса окружности и другие конкретные детали. При использовании ООП следует помнить утверждение: если характеристика однажды определена, то все категории, расположенные ниже данного определения, содержат эту характеристику. Поэтому если определена окружность, то нет необ- ходимости узнавать, сколько у нее вершин, так как она относится к подклассу фи- гур, не имеющих вершин. Объектно-ориентированное программирование является наилучшим инструмен- тарием построения иерархических деревьев для структур данных. Одной из важ- ных особенностей, которые объектно-ориентированное программирование добав- ляет к традиционным языкам типа Паскаль, является механизм, с помощью которЬ-
426 Глава 16 го типы данных могут наследовать характеристики более простых, то есть более общих типов. Этим механизмом является наследование. 16.1.2. Наследование записей В терминах Паскаля объект наиболее схож с типом Record, который является структурированным типом для объединения нескольких связанных элементов под одним именем. Предположим, вы хотите написать программу, которая подсчиты- вает размер стипендий и заработной платы в вашем институте или университете. Запись переменной TPerson, содержащей данные об имени студентов или сотрудников, дате и размере выплаты,можно организовать следующим образом: TPerson ® Record Name : String[30]; Date : String[10]; * Rate : Real; end; Каждое значение, присвоенное переменной TPerson, является экземпляром типа Record.B дальнейшем термин "экземпляр" будет часто использоваться при описа- нии программ, созданных объектно-ориентированным методом. TPerson представляет два уровня абстракции. Можно рассматривать поля Name, Date и Rate по отдельности, а когда речь вдет о полях, работающих одновременно для описания конкретного человека, можно рассматривать их совокупность как TPerson. Предположим, что ваша программа должна учитывать выплаты денег студен- там, преподавателям и сотрудникам кафедры. В каждой группе выплаты произво- дятся особым способом. Можно создать другой тип записи для каждой группы. Например, для получения данных о том, сколько денег должен получить студент, необходимо в первую очередь, знать его средний балл. Можно построить запись TStudent вида: TStudent “ Record Name : String[30]; Date : String[10]; Rate : Real; Ball : Real; end; Однако можно сохранить тип TPerson путем создания поля Student типа TPerson внутри типа TStudent. TStudent = Record Student : TPerson; Ball : Real; end; Такая конструкция удобна и проста, поэтому постоянно используется в про- граммировании. Но она не учитывает специфику данных, обрабатываемых в про-
Введение в объектно-ориентированное программирование 427 грамме. Необходимо учесть, что выплата денег студентам отличается от выплаты другим лицам. Естественно, что студент, как и преподаватель или другой служа- щий кафедры, имеет имя, фамилию, год рождениями ему полагается определенная сумма денег. Для студента тип TStudent должен содержать все поля, который имеются в за- писи TPerson, при этом тип TStudent является типом-потомком для типа TPerson. TStudent наследует все, что принадлежит TPerson, и, кроме того, имеет новые по- ля, которые делают TStudent уникальным. Этот процесс, с помощью которого один тип наследует характеристики другого типа, называется наследованием.. Наследующий тип (в данном случае TStudent) называется порожденным типом или потомком, а тип, который наследуется (TPer- son), называется родительским типом или предком. Итак, мы познакомились с новой категорией структуры данных, связанной с за- писями и имеющей механизм наследования. Типы данных в этой новой категории определяются с помощью нового зарезервированного слова object. Объектный тип может быть определен как полный, самостоятельный тип, подобно описанию запи- сей в Паскале, но он может определяться и как потомок существующего типа объ- екта путем помещения имени родительского типа в скобки после зарезервирован- ного слова object. В приводимом примере два связанных типа объектов могли бы определяться следующим образом: type TPerson = Object Name : String[30]; Date : String[10]; Rate : Real; end; Здесь TPerson является родительским типом, a TStudent — дочерним. Факти- чески, процесс наследования может продолжаться сколь угодно долго. К дочерне- ; му типу TStudent при необходимости можно определить еще один дочерний тип. Все типы, наследующие тип TPerson, называются его дочерними типами, но TStu- k dent является непосредственным дочерним типом от TPerson, a TPerson является I непосредственным родителем типа TStudent. Основное различие между переменными типа object и record в том, что исполь- зование зарезервированного слова object при создании объектов в Паскале позво- ляет не указывать явно поля Name, Date и Rate типа TPerson в типе TStudent, при этом тип TStudent будет содержать эти поля, благодаря свойству наследования.
428 Глава 16 Экземпляры типа object Экземпляры типа object описываются так же, как и описывается любая статиче- ская или динамическая переменная, ссылающаяся на размещенную в динамической памяти переменную: type PStudent = ATStudent; vjar Stat_St : TStudent; Din_St : PStudent; Перед использованием переменной Din_St необходимо выделить память для нее с помощью функции New. Поля объектов * К полю объекта можно обратиться, как и к полю обычной записи, с помощью оператора with либо используя префикс с именем объекта. Например: Student.Ball :=4.5; with Student do begin Name := 'Иванов Николай Петрович'; Date := '25-06-1995'; end; Даже если поля Name, Date и Rate не являются частью описания типа TStudent, благодаря тому, что они наследуются от типа TPerson, на них можно ссылаться как на описанные в TStudent: Student.Name := 'Николай Иванов'; К полям объекта можно обратиться непосредственно, но лучше этого избегать. Принципы объектно-ориентированного программирования требуют, чтобы поля объектов были исключены из исходного кода, насколько это возможно. Поля мето-: да можно объявить скрытыми, ограничив возможность доступа к ним пределами, модуля, в котором они определены. Наиболее рациональным способом получения доступа к полям данных в Паска- • ле является использование процедур или функций, описанных внутри объекта. 16.2. Операции и методы J Функция (или преобразование), которую можно применять к объектам данного класса, называется операцией. Примеры операций: проверить, снять, поместить : (для объектов класса счет), открыть для чтения, читать, закрыть (для объектов1 класса файл) и т.п. । Все объекты данного класса используют один и тот же экземпляр каждой^ операции (т.е. увеличение количества объектов некоторого класса не приводит к j увеличению количества загруженного программного кода). Объект, из которого, вызвана операция, передается ей в качестве ее неявного аргумента (параметра). | 1
в объектно-ориентированное программирование 429 Одна и та же операция может, вообще говоря, применяться к объектам разных классов: такая операция называется полиморфной, так как она может иметь эазные формы для разных классов. Например, для объектов классов “вектор” и ‘комплексное число” можно определить операцию +; эта операция .будет полиморфной, так как сложение векторов и сложение комплексных чисел—разные 16.2.1. Методы. Инициализация полей объектов Обычно при работе с записями возникает проблема инициализации полей запи- :ей. Предположим, имеется следующая структура: TPerson = Object Name : String[30]; Date : String [t.0] ; Rate : Real; end; Можно использовать оператор with для присвоения полям Name, Date и Rate тачальных значений, но при необходимости инициализировать более одной записи гипа TPerson придется использовать большое число операторов with, которые бу- Е(ут выполнять одни и те же действия. Поэтому естественным является создание инициализирующей процедуры, которая обобщает применение оператора with к йобому экземпляру типа TPerson, передаваемого в качестве параметра: procedure begin with Init(var Person:TPerson; Nm, Dt: String; Rt: Real); Person do begin Name := Nm; Date := Dt; Rate := Rt; end; end; Процедура Init, включенная в объект специально для обслуживания типа ТРег- и^называется методом, то есть метод — это процедура или функция, включенная объект таким образом, что экземпляр данного типа становится доступным для нее шутри. В определение типа включается только заголовок метода. При определе- ии метода он дополнительно идентифицируется именем типа. Поля и методы яв- ' иотся двумя составными частями новой структуры, называемой объектом. С уче- >м вышесказанного объект TPerson можно описать следующим образом: type TPerson = Object Name : String[30]; Date : String[10]; Rate : Real; procedure Init(Nm,Dt: String; Rt: Real);
430 Глава 16 end; procedure TPerson.Init(Nm,Dt:String; Rt:Real); begin Name:=Nin; {Поле Name объекта TStudent) Date:=Dt; {Поле Date объекта TStudent} Rate:=Rt; {Поле Rate объекта TStudent} end; t Таким образом, каждой операции соответствует метод - реализация этой операции для объектов данного класса. Можно сказать, что операция - это спецификация метода, а метод - реализация операции. Например, в классе файл7 может быть определена операция печать (print). Эта операция может быть реализована разными методами: (а) печать двоичного файла; (б) печать текстового файла и др. Логически эти методы выполняют одну и ту же операцию, хотя, реализуются они разными фрагментами кода. Каждая операция имеет один неявный аргумент - объект, к которому она; применяется. Кроме того, операция может иметь и другие аргументы (параметры). Эти дополнительные аргументы параметризуют операцию, но не связаны выбором метода. Метод связан только с классом и объектом.. j Теперь для инициализации экземпляра типа TStudent достаточно просто вызвал^ его метод. var j Person : TPerson; Person.Init('Николай Иванов* ,*25-06-1995 *,400000); j 4 Определение методов 4 Процесс определения методов объектов напоминает модули Турбо Паскаля^ Внутри объекта метод определяется заголовком процедуры или функции, дейста вующей как метод: i type | TPerson = Object 1 Name : String[30]; { Date : String[10]; I Rate : Real; 1 procedure Init(Nm,Dt: String; Rt: Real); function GetName : String; j function GetDate : String; 1 function GetRate : Real; J end; , I Поля данных должны быть объявлены перед объявлением методов. Подобна интерфейсной части модуля описание методов внутри объекта только указывает действия, но не определяет, каким образом они будут выполнены. Сами метод» описываются вне определения объекта,как отдельная процедура или функция. При
Введение в объектно-ориентированное программирование 431 определении метода его имени должно предшествовать имя типа объекта, которо- му принадлежит данный метод, с последующей точкой: procedure TPerson.Init(Nm, Dt: String; Rt: Real); begin Name;=Nm; Date:=Dt; Rate:=Rt; end; function TPerson.GetName: String; begin GetName :=Name; end; ' * function TPerson.GetDate: String; begin GetDate :=Date; end; function TPerson.GetRate: Real; begin GetRate :=Rate; end; B данной программе при необходимости можно определить уже существующую функцию, например GetName, не связывая ее с типом TPerson. Область действия метода и параметр Self Заметьте, что ни в одном из предыдущих примеров конструкция with объект do...; не встречается в явном виде. Поля данных объекта легко доступны с помо- щью методов объекта. Хотя в исходном коде поля данных объекта и тела методов разделены, на самом деле они совместно используют одну и ту же область дейст- вия. Именно поэтому один из методов TPerson может содержать оператор GetName := Name; без какого-либо квалификатора перед Name. Это происходит потому, что Name принадлежит тому объекту, который вызывает метод. Если объект вызывает метод, то выполняется неявный оператор with Self do метод; связывающий объект и его методы в одну область действия. Неявный оператор with выполняется путем передачи невидимого параметра ме- тоду всякий раз, когда этот метод вызывается. Этот параметр называется Self и в действительности является 32-разрядным указателем на экземпляр объекта, осуще- ствляющего вызов метода4. Относящийся к TPerson метод GetName приблизительно эквивалентен описанию:
432 Глава 16 function TPerson.GetName (var Self: TPerson): String; begin GetName := Self.Name ; «d'- v v. хотя такое описание не совсем корректно. Фактически, параметр Self является как бы невидимым полем объекта типа TPerson (это относится к любому типу), доступ к которому осуществляется так же, как и к любому другому полю объекта. Обычно нет необходимости в использовании этого параметра, так как генери- руемый Турбо Паскалем код обрабатывает его автоматически. Однако в некоторых ситуациях, когда, например, объекты разных типов имеют поля с совпадающими именами, можно использовать параметр Self явно. Параметр Self является частью области стека при^всех вызовах методов. Мето- ды, используемые как внешние, написанные на языке Ассемблера, должны учиты- вать этот параметр при получении доступа к параметрам метода в стеке. 16.2.2. Поля данных объектов и формальные параметры методов Поскольку методы и их объекты имеют общую область действия, формальные параметры метода не могут быть идентичными любому из полей данных объекта. Аналогично запрещено для формальных параметров процедуры быть идентичны- ми локальным переменным этой процедуры. В этом случае выдается сообщение Duplicate identifier (Повторное использование идентификатора). Та же ошибка возникает при попытке присвоить формальному параметру метода имя поля объек- та, которому данный метод принадлежит. Включение объектов в модули Синтаксис объявления объектов делает их удобными для использования в моду- лях, когда в интерфейсной части модуля описывается тип объекта, а тела методов объектов описываются в исполнительной части. Для определения объекта в модуле не требуется никаких специальных соглашений. Модули могут иметь свои собст- венные (частные) определения типов объектов внутри исполнительной части. Эти типы объектов имеют те же ограничения, что и любые типы, определенные в этой части модуля. Типы объектов, определенные в интерфейсной части модуля, могут иметь объекты-потомки, определенные в исполнительной части. Если модуль В использует модуль А, модуль В также может определять потомков любого типа объекта, экспортируемого модулем А. Предположим, что описанные ранее типы объектов определены в модуле Per- sons. Чтобы использовать типы объектов и методы, определенные в данном моду- ле, можно просто объявить этот модуль в своей программе и описать экземпляр ти- па TStudent в разделе переменных программы: program UseObjects; uses Persons; var Student : TStudent;
Введение в объектно-ориентированное программирование 433 16.2.3. Сокрытие данных в объектах Одним из важнейших принципов объектно-ориентированного программирова- ния является то, что программист при разработке программы должен думать о коде и о данных одновременно. Они не должны существовать раздельно. Данные управ- ляют потоком кода, а код управляет структурой и значениями данных. Если код и данные являются разделенными элементами, то всегда существует опасность вызо- ва правильной процедуры с неверными данными или ошибочной процедуры с пра- вильными данными. Турбо Паскаль не производит подобной проверки, за исклю- чением проверки типов. Но использование объектных типов осуществляет синхро- низацию кода и данных путем совместного построения их описанйй. Как уже гово- рилось, хороший стиль программирования требует, чтобы доступ к полям объекта осуществлялся только через методы, работающие с данными полями. Чтобы полу- чить значение одного из полей объекта, необходимо вызвать соответствующий ме- тод, который возвращает значение нужного поля. Чтобы присвоить полю значение, также необходимо вызвать метод, который назначает данному полю новое значе- ние. Иногда при использовании объектов внутри модулей могут встретиться части описаний объектов, которые экспортировать нежелательно, например, в коммерче- ских приложениях. Необходимо предусмотреть объекты, методы которых доступ- ны, но непосредственный доступ к данным объекта запрещен. В Турбо Паскале для этих целей используются скрытые (частные) поля и методы. Скрытые поля и мето- ды доступны только внутри того модуля, в котором описан объект. Если бы в пре- дыдущем примере тип TStudent содержал скрытые поля, то доступ к ним можно было бы получить только в модуле Persons. Другие части объекта TStudent можно было бы экспортировать, но поля и методы, описанные как скрытые, были бы не- доступными. Поля и методы, которые следуют непосредственно за заголовком объектного типа или директивой public, не имеют никаких ограничений на область действий. В отличие от них поля и метод, объявленные после директивы private, считаются частными (скрытыми) и ограничены использованием в пределах модуля. Полное описание объекта будет выглядеть следующим образом: type NewObject = Object {родитель} поля; {общедоступные} методы; {общедоступные} private поля; (частные) методы; {частные} public поля; {общедоступные} методы; {общедоступные} end;
434 Глава 16 16.2.4. Инкапсуляция Объединение в объекте кода и данных называется инкапсуляцией. При работе с объектами необходимо создавать достаточное количество методов, обеспечи- вающих работу со всеми полями данных, чтобы не возникала необходимость об- ращаться к ним непосредственно. При этом необязательно использовать директиву private, достаточно создать столько методов, сколько необходимо для работы с по- лями объекта. Объекты TPersosi и TStudent написаны таким образом, что нет необходимости в прямом обращении к их внутренним полям данных: type TPerson = Object Name : String[30]; * Date : String[10]; Rate : Real; procedure Init(Nm,Dt:String; Rt:Real); function GetName : String; function GetDate : String; function GetRate : Real; procedure ShowName; procedure ShowDate; procedure ShowRate; end; TStudent “ Object(TPerson) Ball : Real; procedure Init(Nm,Dt:String; Rt,Bl:Real); function GetBall : Real; functiom GetSum : Real; procedure ShowBall; procedure ShowSum; procedure ShowAll; end; Здесь присутствуют только четыре поля данных: Name, Date, Rate и Ball. Мето- ды SbowName, ShowtDate, ShowRate и ShowBall выводят фамилию, дату выплаты, размер ставки и средний балл соответственно. Метод GetSum использует Ball для вычисления суммы выплат студенту в зависимости от среднего балла. Метод ShowSum выводит данное значение. Процедура ShowAll выводит значение всех полей одновременно и уже нет необходимости обращаться непосредственно к по- лям данных. Для экземпляра Student типа TStudent можно использовать набор методов для косвенной работы с полями данных, например:
Введение в объектно-ориентированное программирование 435 with Student do begin Init ('Петр Петров*, '25-06-1995', 400000,4.5); ShowAll; end; ' Обратите внимание, что доступ к полям объекта осуществляется только с по- мощью методов этого объекта. 16.2.5. Оптимизация представления методов Добавление методов косвенного обращения к полям типа TStudent несколько увеличивает объем результирующего кода, однако развитый компоновщик Турбо Паскаля отбрасывает код метода, который ни разу не вызывается в программе. По- этому не следует сомневаться в том, включать или не включать в объект метод, ко- торый может быть как использован* так и не использован в программе, в которой задействован данный тип объекта. Неиспользуемые методы не влияют ни на быст- родействие программы, ни на размер ЕХЕ-файла: если они не используются в про- грамме, то они просто не включаются в него. Этим обеспечивается возможность полностью скрыть данные объекта типа TStudent от внешнего доступа. Если вне объекта его внутреннее представление неизвестно, то можно изменять части этого представления, не изменяя при этом логику работы программы, до тех пор, пока заголовок метода остается неизменным. , Переопределение методов При использовании стандартных средств Турбо Паскаля очень трудно, если во- обще возможно, создавать гибкие процедуры, которые работали бы с формальны- ми параметрами переменных типов, так как это делает, например, процедура Writein, которая может выводить на экран или в файл данные типа string, real, inte- ger, boolean и родственные им. Эта проблема решается объектно-ориентированным программированием с помощью механизма наследования: если определен порож- денный тип, то методы порождающего типа наследуются, однако, при желании они могут переопределяться. Для переопределения наследуемого метода просто описы- вается новый метод с тем же именем, что и наследуемый метод, но с другим телом и (при необходимости) с другим множеством параметров. Например, ранее был оп- ределен тип TStudent, являющийся потомком типа TPerson: TStudent = Object(TPerson) Ball : Real; procedure Init(Nm,Dt : String; Rt,Bl : Real); function GetBall : Real; function GetSum : Real; procedure ShowSum; procedure ShowBall; procedure ShowAll; end;
436 Глава 16 Процедура Init наследуется от предка, но в данном случае ее необходимо пере- определить: procedure TStudent,Init(Nm,Dt : String; Rt,Bl : Real); begin TPerson.Init(Km,Dt,Rt); Ball := Bl; end; Студент имеет все характеристики, которые используются для определения объ- екта TPerson (фамилию, дату выплат, ставку), но для объекта TStudent требуется еще и поле среднего балла Ball, чем он и отличается от других типов лиц, описы- ваемых в программе. Так как TStudent определяет новое поле Ball, его инициализация требует нового метода Init, который инициализирует и средний балл, и наследованные поля. Вме- сто того, чтобы непосредственно присвоить значения наследованным полям, таким, как Name, Gate и Rate, проще использовать метод инициализации объекта TPerson. Синтаксис вызова наследуемого метода следующий: Предок.Метод; где Предок — это идентификатор типа родительского объекта, а Метод—иден- тификатор метода этого типа. Необходимо обратить внимание на то, что вызов переопределяемого метода не является обязательным. Возможно, в общем рлучае TPersonJnit выполняет важную, но скрытую инициализацию.' При вызове переопределяемого метода необходимо быть уверенным в том, что порожденный тип объекта учитывает особенности функционирования родителя. Кроме того, любое изменение в родительском методе автоматически оказывает влияние на все порожденные. После вызова TPerson.Init TStudent.Init может выполнить свою собственную инициализацию, которая в данном случае заключается в присвоении значения, переданного в параметре В1. Другим примером переопределяемого метода является функция TStu- dent.GetSum, вычисляющая размер стипендии студенту в зависимости от среднего балла. В действительности каждый порожденный тип объекта TPerson имеет свой метод GetSum, так как расчет производится в каждом случае по-разному. Метод TStudent.GetSum должен учитывать средний балл студента. Метод TTeacher.GetSum должен учитывать количество лекционных часов, часовую ставку и премиальные выплаты преподавателя. Метод TStaff.GetSum должен учитывать только размер премиальных выплат иным сотрудникам кафедры. С учетом выше- ; сказанного получим: unit Persons; 1 interface 1 type TPerson = Object
Введение в объектно-ориентированное программирование 437 private Nane : String[30]; Date : String[10]; Rate : Real; public procedure Init (Nm,Dt : String; Rt,Bl : Real) ; procedure ShowAll; end; TStudent = Object(TPerson) private Ball : Real; public procedure Init (Nm,D^ : String; Rt, Bl : Real); function GetSum : Real; procedure ShowSum; procedure ShowAll; end; TStaff = Object(TPerson) private Bonus : Real; public procedure Init(Nm, Dt :String; Rt,Bl: Real); function GetSum : Real; procedure ShowSum; procedure ShowAll; end; TTeacher'= Object(TStaff) private Hours : Word; HourRate: Real; public procedure Init(Nm,Dt : String; Rt,Bn,Hrt : Real; Hr : Word); function GetSum : Real; procedure ShowSum; procedure ShowAll; end; implementation Ниже приводится метод TStudent.GetSum, в котором учитывается средний балл: function TStudent.GetSum : Real; begin GetSum : = Rate*Ball; end;
438 Глава 16 В методе TStaff.GetSum к ставке прибавляется размер премиальных выплат: function TStaff.GetSum : Real; begin GetSum : = Rate+Bonus; end; Метод TTeacher.GetSvm вызывает TStaff.GetSum и добавляет к этому значению размер часовой ставки, умноженный на количество часов: function TTeacher.GetSum : Real; begin GetSum := TStaff.GetSum+Hours*HourRate; end; Необходимо помнить, что?хотя методы могут быть переопределены, поля дан- ных переопределяться не могут. После того как поле данных в иерархии объекта определено, никакой дочерний тип не может определить поле данных с таким же именем. Наследование статических методов Все описанные до сих пор методы, относящиеся к типам объектов TPerson, TStudent, TStaff и TTeacher, являются статическими методами. С ними сЬязана проблема наследования. Рассмотрим процедуру ShowSum. Для объекта типа TStaff она имеет вид: procedure TStaff.ShowSum; begin WriteLn(GetSum); end; Для объекта типа TTeacher эта процедура имеет тот же самый вид: procedure TTeacher.ShowSum; begin WriteLn (GetSum) ; end; Действительно, есть ли необходимость выполнять в этой процедуре какие-либо дополнительные действия? По сравнению с типом TStaff не изменилось ничего, кроме копирования процедуры и подстановки квалификатора TTeacher перед иден- тификатором ShowSum. Возникает резонный вопрос, нет ли здесь логической ошибки? Поскольку методы одинаковы, нет нужды помещать ShowSUm в TStaff и TTeacher. Ведь TTeacher автоматически наследует ShowSum от TStaff, поэтому нет необходимости переопределять этот метод. Но именно здесь и возникает проблема, связанная со статическими методами. Проблема заключается в следующем: пока копия метода ShowSum не будет по- мещена в область действия TTeacher для подавления метода ShowSum объекта TStaff, метод не будет работать правильно, если он будет вызываться из объекта типа TTeacher. Если TTeacher запускает метод ShowSum объекта TStaff, то И функ-
Введение в объектно-ориентированное программирование 439 ция GetSum, используемая в методе, будет принадлежать объекту TStaff, и зарплата будет рассчитана неправильно, без учета количества часов. Это объясняется способом, которым компилятор осуществляет вызов методов. Поскольку тип TTeacher является потомком типа TStaff, то сначала в сегмент кода будет скомпилирована функция TStaff.GetSum. Затем будет скомпилирована про- цедура TStaff.ShowSum, вызывающая TStaff.GetSum. Как и при вызове любой про- цедуры, компилятор замещает ссылки на TStaff.GetSum и TStaff.ShowSum в исход- ном коде на их адреса в сегменте кода. Таким образом, при вызове код TStaff.ShowSum в свою очередь вызывает TStaff.GetSum, что и составляет пробле- му. Фактически наследуется следующая процедура: procedure TStaff.ShowSum; begin i WriteLn(TStaff.GetSum); end; Попросту говоря, метод объекта TStaff ничего не знает о существовании объекта TTeacher. Таким образом, метод ShowSum нельзя наследовать. Вместо этого он должен быть переопределен своей второй копией, вызывающей уже правильный метод. Вызывая методы, компилятор работает так: сначала он ищет метод, имя ко- торого определено внутри типа объекта. Тип TTeacher определяет методы с имена- ми Init, GetSum, ShowSum и ShowAll. Если бы объект типа TTeacher должен был вызвать один из этих четырех методов, компилятор заменил бы его вызов на адрес одного из собственных методов TTeacher. Если в типе объекта не определен метод с таким именем, то компилятор подни- мается выше к непосредственному родительскому типу в поисках метода с указан- ным именем. Если метод с таким именем найден, то адрес родительского метода в исходном коде замещается адресом дочернего метода. Если метод с таким именем не найден, то компилятор продолжает продвигаться вверх по родительским объек- там в поисках метода. Если компилятор достигает самого первого (высшего) типа объекта, то он выдает сообщение об ошибке, указывающее, что ни одного такого метода не определено. Однако если статический наследуемый метод найден и используется, то необ- ходимо помнить, что вызываемый метод является в точности таким, каким он был определен и скомпилирован для родительского типа. Если родительский метод вы- зывает другие методы, то вызываемые методы будут также родительскими метода- ми, даже если дочерний объект содержит методы, которые переопределяют роди- тельские. Виртуальные методы и полиморфизм Решение проблемы наследования возможно при использовании динамических методов (виртуальных), ссылки на которые определяются во время выполнения. Виртуальные методы предоставляют чрезвычайно мощный инструмент для обоб- щения, называемый полиморфизмом. Полиморфизм является способом присвое-
440 Глава 16 ния действию имени, которое используется всеми объектами иерархии, причем ка- ждый объект иерархии использует это действие определенным образом. Описанная ранее простая иер&рхия геометрических фигур является хорошим примером полиморфизма в действии, предоставляемого с помощью виртуальных методов. Каждый тип объекта в иерархии представляет отдельный тип фигуры на экране: окружность, эллипс или другие. Если возникнет необходимость определить объекты для представления на экране других типов фигур, таких, как прямоуголь- ник, треугольник и т.д., можно написать метод для каждого из них, который будет выводить объект на экран. Можно сказать, что все типы фигур имеют общую спо- собность отображать себя на экране. Особым для каждого типа объекта является способ отображения самого себя на экране. Можно отобразить на экране любой тип фигуры, но механизм рисования каждой является сугубо индивидуальным. Од- но слово "отобразить" используется для вывода многих фигур. То же самое в при- мере с платежной ведомостью — функция GetSum вычисляет размер выплат для различных типов лиц. Это и есть полиморфизм, а виртуальные методы реализуют его в Турбо Паскале. Раннее и позднее связывание Различие между вызовом статического метода и динамического метода заклю- чается в том, что в первом случае компилятору заранее известна связь объекта с методом, и он устанавливает ее на этапе компиляции. Во втором — компилятор как бы откладывает решение до момента выполнения программы. Вспомните проблему, связанную с процедурой ShowSum, когда предлагалось описать эту процедуру один раз для типа TStaff и наследовать ее в типе TTeacher. Вызов процедуры TTeacher.ShowSum мог привести только к выполнению Show- Sum, ближайшей в объектной иерархии. В этом случае TTeacher.ShowSum по- прежнему вызывалась определением ShowSum для TStaff, так как TStaff являлся ближайшим к TTeacher типом вверх по иерархии. Если предположить, что дочер- ний тип, который описывает собственный метод, переопределяющий ShowSum» ти- па TStaff, не был определен, то любой порожденный по отношению к TStaff тип будет по-прежнему вызывать тот же самый экземпляр метода ShowSum. Решение об этом может быть принято во время компиляции. Однако все происходит иначе, когда метод ShowSum вызывает GetSum. Каждый тип объектов имеет свой собственный экземпляр GetSum, поэтому от того, какой экземпляр вызывается методом ShowSum, полностью зависит тип объемов, вызы- ваемый этим методом. Именно поэтому решение о вызове метода GetSum внутри процедуры ShowSum необходимо отложить. При компиляции кода ShowSum нель- зя принимать решение о том, какой метод GetSum вызывать. Эта информация не- доступна во время компиляции, поэтому решение откладывается до тех пор, пока программа не начнет выполняться и пока нельзя будет запросить экземпляр объек- та, вызывающий ShowSum.
Введение в объектно-ориентированное программирование 441 Процесс, с помощью которого вызовы статических методов связываются ком- пилятором во время компиляции в один метод, называется ранним связыванием. При раннем связывании вызывающий и вызываемый методы связываются при пер- вой же возможности, то есть во время компиляции. При позднем связывание вы- зывающий и вызываемый методы не могут связываться во время компиляций, по- этому включается механизм, позволяющий осуществить связывание несколько позднее, когда вызов действительно произойдет. 16.2.6. Совместимость объектных типов Наследование несколько изменяет правила совместимости типов в Турбо Пас- кале. Кроме того, порожденный тип наследует совместимость со всеми своими ро- дительскими типами. Эта совместимость бывает трех видов: между экземплярами объектов; между указателями на экземпляры объектов; между формальными и фактическими параметрами. Необходимо помнить, что во всех трех случаях совместимость типов расширя- ется только от потомка к родителю. Другими словами, дочерние типы могут сво- бодно использоваться вместо родительских, но не наоборот. В модуле Persons тип TStaff является потомком TPerson, а тип TTeacher является потомком TStaff. Учитывая это, рассмотрим следующие описания: type PPerson ATPerson; PStaff = ATStaff; PTeacher = ATTeacher; var Person : TPerson; Staff : TStaff; Teacher : TTeacher; PPtr : PPerson; Sptr : PStaff; TPtr : PTeacher; В этом случае справедливы следующие операторы присваивания: Person г** Staff; Staff Teacher; Person := Teacher; Эта концепция является новой для Паскаля, поэтому необходимо помнить, в ка- ком порядке следует совмещать типы. Принцип совмещения таков: поля данного источника должны полностью заполнить поля данных приемника. Порожденные типы содержат все поля, имеющиеся в порождающих типах, благодаря свойству наследования. Поэтому размер порожденного типа может быть таким же, как и размер родителя, либо (что встречается чаще всего) — больше размера родителя, но никогда не бывает меньше. Присвоение порождающего (родительского) объекта
442 Глава 16 порожденному (дочернему) может привести к тому, что некоторые поля порожден- ного объекта останутся неопределенными, что является недопустимым. В операторах присваивания из источника в приемник будут копироваться толь- ко поля, являющиеся общими для обоих типов, В операторе присваивания Person Teacher; только поля Name, Date и .Rate из переменной Teacher будут скопированы в пе- ременную Person, так как они являются общими для TTeacher и TPerson. Совмес- тимость типов работает также для указателей типов объектов и подчиняется тем же общим правилам, что и для экземпляров объектов. Указатель на потомка может присваиваться указателю на родителя. Если использовать предыдущие определе- ния, нижеприведенные присваивания указателей являются допустимыми: SPtr := TPtr; k PPtr := SPtr; PPtr := TPtr; Обратные присваивания недопустимы. Формальный параметр (конкретное значение либо параметр-переменная) данно- го объектного типа может принимать в качестве фактического параметра объект своего же типа или объекты всех дочерних типов. Если определить заголовок про- цедуры следующим образом: procedure Show_Fields(Obj: TStaff); допустимыми типами фактических параметров могут быть TStaff или TTeacher, но не тип TPerson. Obj также может быть параметром-переменной. При этом вы- полняются те же правила совместимости. Необходимо помнить, что параметры-значения существенно отличаются от па- раметров-переменных. Параметр-переменная является указателем на действитель- ный, посылаемый в качестве параметра объект, а параметр-значение — только ко- пией фактического параметра. Эта копия включает только те поля, которые входят в тип формального параметра-значения, то есть фактический параметр преобразуется к типу формального параметра. Для параметра-переменной фактическое значение остается неизменным. Аналогично, если формальный параметр является указателем на тип объекта, фактический параметр может быть указателем на этот тип объекта или на любой дочерний тип. Если заголовок процедуры имеет вид: procedure Show_Fields (Obj PStaff) ; то допустимыми типами фактических параметров могут быть PStaff или PTeacher, но не тип PPerson. 16.2.7. Виртуальные методы. Конструктор Полиморфизм является необходимым средством при обеспечении расширяемо- сти типов в приложениях. Он позволяет не переделывать структуру программы, ес- ли возникает необходимость, скажем, определения нового типа данных, например нового типа геометрической фигуры. Использование полиморфизма необходимо,
Введение в объектно-ориентированное программирование 443 если процедура построения фигур работает с фиксированным набором объектов, описанных в модуле, а пользователь, работающий с модулем, хочет определить но- вый тип фигуры. Для этого применяются виртуальные методы. Метод становится виртуальным, если за его объявлением в типе объекта сто- ит зарезервированное слово virtual. Необходимо помнить, что если метод объявлен в родительском типе как virtual, то все методы с аналогичными именами в дочер- них типах также должны объявляться виртуальными, во избежание ошибки компи- лятора. Обратите внимание на использование при объявлении виртуальных методов за- резервированного слова constructor (конструктор), заменившего зарезервированное слово procedure для процедур Init. Он является специальным типом процедуры, которая выполняет некоторую установочную работу для механизма виртуальных методов. Конструктор должен вызываться перед вызовом любого виртуального ме- тода. Вызов виртуального метода без предварительного вызова конструктора мо- жет привести к блокированию системы, а у компилятора нет способа проверить порядок вызова методов. Каждый тип объекта, имеющий виртуальные методы, обязан иметь конструктор. Понятие destructor (деструктор), обратное понятию constructor, будет объяснено далее. Ниже приведены объекты из примера платежной ведомости, уже объявленные как виртуальные: unit New_persons; interface type PPerson = ATPerson; TPerson = Object Name : String[30]; -Date : String[10]; Rate : Real; constructor Init(Nm, Dt : String; Rt : Real); destructor Done; Virtual; procedure ShowAll; Virtual; end; PStudent = ATStudent; TStudent = Object(TPerson) Ball : Real; constructor Init (Nm,Dt : String; Rt, Bl : Real); destructor Done; Virtual; function GetSum : Real; procedure ShowSum; procedure ShowAll;. Virtual;
444 Глава 16 end; PStaff = A TStaff; TStaff = Object(TPerson) Bonus : Real; public constructor Init(Nm, Dt :String; Rt,Bl: Real); destructor Done; Virtual; function GetSum : Real; Virtual; procedure ShowSum; Virtual; procedure ShowAll; Virtual; end; PTeacher = ATTeacher; TTeacher = Object(TStaff) * Hours : Word; HourRate: Real; constrictor Init(Nm,Dt : String; Rt,Bn,Hrt: Real; Hr : Word); destructor Done; Virtual; function GetSum : Real; Virtual; procedure ShowAll; Virtual; end; Обратите внимание, что метод ShowSum, показанный для типа TTeacher, теперь удален из его определения. Типу TTeacher уже не нужно переопределять метод ShowSum типа TStaff. Вместо этого ShowSum может просто наследоваться от TStaff со всеми вложенными в него вызовами, которые в этом случае уже будут вы- зывать методы из TTeacher, а не из TStaff, как это происходило в полностью стати- ческой иерархии объектов. Каждый экземпляр объекта должен инициализироваться отдельным вызовом кон- структора. Недостаточно инициализировать один экземпляр объекта и затем при- сваивать этот экземпляр другим. Другие экземпляры, даже если они содержат пра- вильные данные, не будут инициализированы оператором присваивания и заблоки- руют систему при любых вызовах их виртуальных методов. Например: var One,Two: TPerson; begin One.Init ('Петр Петров*,'25-06-1996',40000); Two := One; {Неправильный вызов!} end; Каждый тип объекта, содержащий виртуальные методы, имеет таблицу вирту- альных методов (ТВМ), хранящуюся в сегменте данных. ТВМ содержит размер ти- па объекта и для каждого виртуального метода указатель кода, исполняющий дан- ный метод. Конструктор устанавливает связь между вызывающим его экземпляром объекта и ТВМ этого Объекта.
Введение в объектно-ориентированное программирование 445 Необходимо учесть, что имеется только одна ТВМ для каждого типа объекта. Отдельные экземпляры объекта (т.е. переменные данного типа) содержат только адрес ТВМ, но не саму ТВМ. Конструктор устанавливает значение этого адреса. Поэтому вызов виртуального метода до вызова конструктора приведет к ошибке, так как к этому моменту поле адреса ТВМ еще не инициализировано и содержит неопределенный адрес. При отладке программы для контроля правильности вызовов виртуальных ме- тодов можно использовать директиву компилятора $R. Если директива $R нахо- дится во включенном состоянии {$R+}, то все вызовы виртуальных методов будут проверяться на состояние инициализации объекта, выполняющего вызов метода. Если выполняющий вызов объект еще не был инициализирован конструктором, то произойдет ошибка выполнения. По умолчанию установлено значение {$R-}. Однако отрицательной стороной использования данной директивы является за- . медление работы программы. Это происходит из-за того, что при каждом вызове виртуального метода дополнительно выполняется процедура проверки правильно- сти инициализации. Поэтому, если скорость работы программы является критиче- ским параметром, то рекомендуется использовать проверку виртуальных методов только на этапе отладки. Когда программа отлажена и гарантировано отсутствие вызовов виртуальных методов до вызова конструктора, директиву $R можно пере- вести в пассивное состояние {$R-}, что ускорит выполнение программы. Обратите внимание на то, что все заголовки методов GetSum объявлены вирту- альными и снабжены зарезервированным словом virtual. Как только родительский тип объекта объявит метод виртуальным, все его потомки также должны объявить этот метод виртуальным. Другими словами, статический метод никогда не может переопределить виртуальный метод. Если вы попытаетесь сделать это, компилятор выдаст сообщение об ошибке. Следует помнить, что после того, как метод стал виртуальным, его заголовок не может изменяться в объектах более низкого уровня иерархии. Определение вирту- ального метода можно представить как шаблон для всех родственных ему методов. Поэтому заголовки всех реализаций одного и того же виртуального метода в отли- чие от статического метода должны быть идентичными, включая число параметров и их типы. Расширяемость объектов Как уже отмечалось, преимуществом использования виртуальных методов явля- ется то, что типы объектов и методы, определенные в модуле, могут поставляться пользователю в виде TPU-файла, т. е. без исходного кода. Для работы с объек- тами модуля необходимо знать содержание только интерфейсной части модуля. Используя полиморфные объекты и виртуальные методы, пользователь TPU-файла может свободно добавлять новые методы к уже существующим. Свойство программы, связанное с добавлением новых функциональных харак- теристик без модификации ее исходного кода, называется способностью к росши-
446 Глава 16 рению. Способность к расширению является естественным продолжением насле- дования: наследуются все свойства, которыми обладают порождающие типы, а за- тем добавляются новые по мере необходимости. Позднее связывание позволяет но- вые методы связать с уже существующими во время выполнения программы, бла- годаря чему расширение существующего кода выглядит как бы невидимым, требуя лишь небольшого увеличения таблицы виртуальных методов. Преимущества и недостатки виртуальных методов В общем случае рекомендуется делать методы виртуальными. Использование статических методов имеет смысл, если требуется получить оптимальную эффек- тивность скорости выполнения и использования памяти. Однако в этом случае, как было отмечено, теряется возможность расширения. Иногда при написании программы точно не известно, является метод виртуаль- ным или нет. В таких случаях лучше определить его виртуальным, особенно если имеется предположение, что метод будет перекрываться кем-либо из потомков, а его код должен быть доступным в дальнейшем. С другой стороны, необходимо помнить, что если у объекта имеются любые виртуальные методы, то для этого объекта в сегменте данных будет создана табли- ца виртуальных методов, и любой экземпляр этого объекта будет с ней связан. Ка- ждый вызов виртуального метода должен проходить через ТВМ, тогда как статиче- ские методы вызываются непосредственно. Хотя просмотр ТВМ весьма эффекта-. вен, вызов статического метода все равно остается более быстрым, чем вызов вир- туального. Если в объекте нет виртуальных методов, то и ТВМ отсутствует в сег- менте данных, и программа будет работать быстрее. Дополнительная скорость и эффективное использование памяти для статиче- ских методов должны уравновешиваться гибкостью, которая присуща виртуаль- ным методам, ведь имеющийся код можно расширить спустя большой промежуток времени с момента его компиляции. Поэтому при выборе методов всегда необходи- мо учитывать возможность последующей модификации программы. 16.2.8. Динамические объекты Все приведенные до сих пор экземпляры объектов были статического типа (не путать со статическими методами!), они описывались зарезервированным словом var, им присваивались имена, и сами объекты размещались в сегменте данных или в стеке: var Teacher: TTeacher; Но точно так же, как и любые типы данных в Паскале, объекты можно разме- щать в динамической памяти и работать с ними, применяя указатели. Специально для работы с динамическими объектами Турбо Паскаль включает несколько усо- вершенствованных процедур для размещения и удаления объектов из памяти наи- более эффективными способами. Одним из самых простых способов размещения: объектов в памяти является ис-, пользование процедуры New, традиционно применяемой для работы с указателями::
Введение в объектно-ориентированное программирование . 447 var Sum : Real; Р : ATPerson; New(P) ; Как и для других типов данных, процедура New выделяет в динамической памя- ти область, достаточную для хранения экземпляра типа, определяемого указателем, и возвращает адрес этой области в указателе. Если динамический объект содержит виртуальные методы, он должен инициа- лизироваться с помощью вызова' конструктора, перед тем как будет вызван любой из его методов: РА.Init(’Иван Петров',*25-06-1^95',400000); Затем вызовы методов могут происходить в обычном порядке, с использованием имени указателя и ссылочного символа л вместо имени экземпляра объекта, кото- рые использовались бы при обращении к статически размещенному объекту: Sum := РА.GetSum; Расширенное использование оператора New Турбо Паскаль использует расширенный синтаксис процедуры New, который позволяет одновременно выделять память для объекта в динамической области и инициализировать сам объект с помощью вызова его конструктора. Теперь проце- дура New может вызываться с двумя параметрами: имя указателя используется в качестве первого параметра, а имя конструктора — в качестве второго параметра: New(P, Init('Иван Петров*, '5-06-1995', 400000)); При использовании расширенного синтаксиса процедуры New конструктор Init выполняет динамическое размещение объекта, используя специальный сгенериро- ванный код, вызываемый оператором constructor и выполняемый до основного ко- да конструктора. Имя экземпляра объекта не может использоваться в качестве пер- вого параметра процедуры, так как во время вызова процедуры New экземпляр, инициализируемый с помощью Init, ещё не существует. Компилятор определяет правильность вызова конструктора, проверяя тип указателя, передаваемого в каче- стве первого параметра. Процедура New также может использоваться в качестве функции, которая воз- вращает значение указателя. Передаваемый New параметр при этом должен быть типом указателя на объект, а не самим указателем: type PPerson = ATPerson; var P : PPerson; P := New(PPerson); Использование процедуры New как функции применимо ко всем типам данных, а не только к объектам.
448 Глава 16 В качестве второго параметра процедура New может содержать конструктор объектного типа: Р := New(PPerson,Init('Иван Петров','25-06-1995',400000)); Обнаружение ошибок конструктора При использовании конструкторов Турбо Паскаль позволяет задать пользова- тельскую функцию обработки ошибок динамической памяти с помощью перемен- ной НеарЕггог, которая является стандартной и не требует описания в разделе пе- ременных. Она содержит адрес стандартной функции обработки ошибок в Паскале, которая может быть замещена. Пользовательская функция обработки ошибок должна иметь формат: function HeapFunc (Size :Word): Integer; FAR; Наличие директивы FAR обязательно, это говори^ о том, что для функции, по- сле которой она расположена, должен быть использован дальний тип вызова. Новая функция обработки ошибок устанавливается путем присваивания ее адре- са переменной НеарЕггог следующим образом: НеарЕггог := вHeapFunc; По умолчанию, если не хватает памяти для размещения экземпляра динамиче- ского объекта, вызов конструктора, использующий расширенный синтаксис стан- дартной процедуры New, генерирует фатальную ошибку выполнения с кодом 203. Если устанавливается пользовательская функция обработки ошибок динамической памяти, которая возвращает 1, а не стандартный результат 0, то вызов конструктора через New будет возвращать NIL в том случае, если конструктор не сможет завер- шить запрос (вместо прекращения выполнения программы). Код, который* выполняет размещение и инициализацию поля таблицы виртуаль- ных методов динамического экземпляра, является частью кода, располагающейся непосредственно за точкой входа в конструктор. Когда управление достигает нача- ла операторной секции конструктора, экземпляр уже размещен и инициализирован. Если размещение завершилось неудачно, и если функция ошибки динамически распределяемой области памяти возвратила 1, то конструктор пропускает выпол- нение операторной секции и возвращает указатель NIL. Таким образом, указатель, который задан в расширенной процедуре New, вызывающей конструктор, будет ус- тановлен в NIL (это означает, что память не выделена). Если управление передается в начало операторной секции конструктора, это оз- начает, что экземпляр объектного типа успешно размещен и инициализирован. Од- нако сам конструктор может Попытаться разместить динамическую переменную, чтобы инициализировать поле указателя экземпляра, но эта попытка может ока- заться неудачной. Если это произошло, то корректно работающий конструктор должен отменить предварительно произведенное выделение памяти и удалить уже размещенный экземпляр объектного типа, чтобы конечным результатом операции явился указатель NIL. Чтобы сделать возможным такую отмену, Турбо Паскаль1 предоставляет новую стандартную процедуру Fail, которая не имеет параметров и
Введение в объектно-ориентированное программирование 449 может вызываться только изнутри конструктора. Вызов Fail заставляет кон- структор удалить динамический экземпляр, который был размещен при входе в конструктор, и приводит к возврату указателя NIL для индикации неудачной попытки. Если динамические экземпляры размещаются с помощью расширенного син- таксиса New, то результирующее значение NIL, передаваемое указателю, свиде- тельствует о неудачной операции. Но нет таких переменных типа указатель, кото- рые можно было бы проверить после создания статического экземпляра или после вызова унаследованного конструктора. Вместо этого Турбо Паскаль в качестве функций позволяет использовать конструкторы, которые возвращают результат типа boolean. Возвращаемое значение TRUE означает успех, a FALSE — неудачу, благодаря вызову Fail внутри конструктора^ Рассмотрим, как можно описать последовательный вызов конструкторов типа TPerson, TStaff и TTeacher. Первый пример конструкторов не использует обнару- жение ошибок: constructor TPerson.Init(Nm,Dt : String; Rt : Real); begin Name := Nm; Date := Dt; Rate := Rt; end; constructor TStaff.Init(Nm,Dt : String; Rt,Bn : Real) ; begin TPerson.Init (Nm, Dt, Rt) ; Bonus := Bn; end; constructor TTeacher.Init(Mn,Dt:String; Rt,Bn,Hrt:Real; Hr: Word); begin TStaff.Init(Nm,Dt, Rt, Bn) ; Hours := Hr; HourRate := Hrt; end; При таком описании, если, к примеру, инициализируется экземпляр типа TTeacher, то он вызывает конструктор типа TStaff, который в свою очередь вызы- вает конструктор типа TPerson. Если произойдет ошибка при вызове последнего конструктора, придется отменить вызовы всех трех конструкторов. Следующий пример демонстрирует, как можно переписать конструкторы типа TStaff и TTeacher с учетом обнаружения ошибок. constructor TStaff.Init(Nm,Dt : String; Rt,Bn : Real); begin if not TPerson.Init(Nm,Dt,Rt) then Fail; 15—116
450 Глава 16 Bonus := Bn; end; constructor TTeacher.Init(Nm,Dt:String; Rt,Bn,Hrt:Real; Hr: Word); begin if not TStaff.Init(Nm,Dt,Rt,Bn) then Fail; Hours := Hr; HourRate := Hrt; end; function HeapFunc (Size: Word): Integer; FAR; begin HeapFunc := 1; end; Обратите внимание на то, что функция HeapFunc выполняет одну-единственную операцию: при любом вызове возвращает 1. В выполнении других операций в дан- ном случае нет необходимости. В приведенном примере вложенные вызовы конст- рукторов осуществляются путем указания имени предка, за которым следует точка и имя конструктора. Турбо Паскаль предоставляет специальное слово INHERITED, используя которое, можно вызывать методы предка без указания имени типа предка и точки, например: constructor TStaff.Init(Nm,Dt : String; Rt,Bn : Real); begin if not INHERITED Init(Nm,Dt,Rt) then Fail; Bonus := Bn; end; Это может пригодиться при использовании большой иерархии объектов, когда запомнить все связи типа "предок-потомок" невозможно. 16.2.9. Деструкторы Подобно другим типам данных, размещаемые в динамической памяти объекты могут удаляться в случае необходимости с помощью процедуры Dispose: Dispose(Р); Однако при удалении ненужного объекта может понадобиться нечто большее, чем простое освобождение занимаемой им динамической памяти. Объект может содержать указатели на динамические структуры или объекты, которые нужно ос- вободить или очистить в определенном порядке, особенно если используется сложная динамическая структура данных. Все операции, необходимые для очистки динамического объекта, должны объединяться в один метод таким образом, чтобы объект мог быть уничтожен с помощью единственного вызова: Person.Done; Метод Done должен инкапсулировать все детали очистки своего объекта, а так- же всех структур данных и вложенных объектов. Для обозначения таких методов обычно рекомендуется использовать идентификатор Done.
Введение в объектно-ориентированное программирование 451 Часто полезно и допустимо определять несколько методов очистки для данного типа объекта. В зависимости от их размещения или использования или в зависи- мости от состояния и режима на момент очистки сложные объекты могут потребо- вать очистки несколькими различными путями. Турбо Паскаль предоставляет специальный тип метода, называемый "сборщи- ком мусора" или деструктором, для очистки и удаления динамических объектов. Деструктор объединяет этап удаления объекта с другими действиями или задачами, необходимыми для данного типа объекта. Для одного типа объекта можно опреде- лить несколько деструкторов. Деструктор размещается вместе с другими методами объекта в определении ти- па объекта: type k TPerson = Object Name : String[30]; Date : String[10]; Rate : Real; constructor Init(Nm,Dt : String; Rt: Real); destructor Done; Virtual; procedure ShowAll; Virtual; end; Деструкторы можно наследовать, и они могут быть либо статическими, либо виртуальными. Поскольку различные программы завершения обычно требуют раз- личных типов объектов, рекомендуется всегда определять деструкторы виртуаль- ными, чтобы для каждого типа объекта выполнялся правильный деструктор. Отметим, что нет необходимости указывать зарезервированное слово destructor для каждого метода очистки, даже если определение типа объекта содержит вирту- альные методы. Деструкторы в действительности работают только с динамиче- скими объектами. При очистке таких объектов деструктор осуществляет некоторые специальные функции, гарантируя, что в динамической памяти всегда будет осво- бождаться правильное число байтов. Но применение деструктора к статическим объектам также не является ошибкой и не может привести к некорректной работе программы. Основное преимущество использования деструктора заключается в удалении из памяти полиморфных объектов. Полиморфными являются те объекты, которые были присвоены экземпляру родительского типа, благодаря правилам совместимо- сти типов Турбо Паскаля. Экземпляр объекта типа TStudent, присвоенный пере- менной типа TPerson, является примером полиморфного объекта. Эти правила мо- гут применяться и к указателям на объекты: указатель на TStudent может свободно присваиваться указателю на TPerson, а адресуемый им объект также будет поли- морфным.
452 Глава 16 Термин “полиморфный” означает, что компилятор, строя код объекта, во время компиляции точно не знает, какой тип объекта будет в действительности использо- ван. Единственное, что он знает, это то, что объект принадлежит иерархии объек- тов, являющихся, потомками указанного типа предка. Очевидно, что размеры типов объектов отличаются. Во время компиляции из полиморфного объекта нельзя извлечь какую-либо информацию о его размере. Информация о размере удаляемого объекта становится доступной для деструктора в момент удаления, благодаря обращению к тому месту, где эта информация запи- . сана, то есть к таблице виртуальных методов экземпляров объектов определенного - типа. В каждой ТВМ содержится размер в байтах данного типа объекта. Таблица виртуальных методов любого объекта доступна через скрытый параметр Self, со- держащий адрес ТВМ, который передается методу при вызове. Деструктор также является методом, поэтому^ когда объект вызывает его, деструктор получает копию Self через стек. Таким образом, если объект является полиморфным во время ком- . пиляции, он никогда не будет полиморфным во время выполнения, благодаря . позднему связыванию. Для выполнения освобождения памяти при позднем связывании деструктор;! нужно вызывать как часть расширенного синтаксиса процедуры Dispose: Dispose (Р, Done); Деструктор объекта, на который указывает Р, 'выполняется как обычный метод.: Однако как только последнее действие будет выполнено, деструктор начнет искать - размер экземпляра указанного типа в ТВМ и передаст его размер процедуре Dis-, pose. Процедура Dispose завершает процесс путем удаления правильного числа байтов области динамической памяти, которая относилась к Рл. Число освобож-• даемых байтов будет правильным, независимо от того, указывал ли Р на экземпляр ? типа TStaff или на один из его дочерних типов, например на TTeacher. Поэтому де- структор рекомендуется делать виртуальным, даже если других виртуальных мето--; дов объект не содержит. Обратите внимание, что вызов деструктора вне процедуры Dispose не приведет ( к автоматическому освобождению памяти. Необходимо отметить, что поскольку основная информация содержится не в те- ле деструктора, а связана с его заголовком, содержащим слово destructor, сам по' себе метод деструктора может быть пустым и выполнять только функцию связи с процедурой Dispose: destructor TPerson.Done; begin end; ” Деструктор дочернего типа, например TStaff, также должен последним действи- ем вызывать соответствующий деструктор своего непосредственного предка, что- бы освободить поля всех наследуемых указателей объекта:
Введение в объектно-ориентированное программирование 453 destructor TStaf f.Done; begin INHERITED Done; end; 16.2.10. Динамические методы В Турбо Паскале имеется дополнительный класс методов позднего связывания, которые называются динамическими. Фактически, динамические методы являются подклассом виртуальных методов и отличаются от них только способом вызова на этапе выполнения. Во всех других, отношениях динамический метод можно рас- сматривать как эквивалентный виртуальному. Описание динамического метода аналогично описанию виртуального за исклю- чением того, что оно должно включать в себЙ индекс динамического метода, кото- рый указывается сразу за ключевым словом virtual. Индекс динамического метода должен задаваться целочисленной койстантой в диапазоне значений от 1 до 65535 и представлять собой уникальное значение среди индексов других динамических методов, содержащихся в объектном типе или его предках. Например: function GetSum : Real; Virtual 10; Переопределение динамического метода должно точно соответствовать поряд- ку, типу и именам параметров, а также типу результата функции, если метод явля- ется функцией. Переопределение должно включать в себя директиву virtual, за ко- торой следует тот же индекс динамического метода, что и заданный в объектном типе предка. Использование динамических методов целесообразно при создании длинной ие- рархии объектов с большим количеством виртуальных методов, для которых будут создаваться очень длинные ТВМ с указанием всех виртуальных методов предков, хотя переопределяться может только часть из них. Это требует большого объема используемой памяти для хранения ТВМ. При использовании динамических методов создается альтернативная таблице виртуальных методов таблица динамических методов (ТДМ), в которой указыва- ются только те виртуальные методы, которые переопределяются, что позволяет экономить память. 16.3. Внутреннее представление объектов в Турбо Паскале Внутренний формат данных объекта имеет сходство с внутренним форматом за- писи. Поля объекта записываются в порядке их описаний как непрерывная после- довательность переменных. Любое поле, унаследованное от родительского (поро- ждающего) типа, записывается перед новыми целями, определенными в дочернем (порожденном) типе. Если объектный тип определяет виртуальные методы, конструкторы или дест- рукторы, то компилятор размещает в нем дополнительное поле данных. Это
454 Глава 16 16-биговое поле, называемое полем таблицы виртуальных методов, используется для запоминания смещения таблицы виртуальных методов в сегменте данных. Поле таблицы виртуальных методов располагается непосредственно после обычных по- лей объектного типа. Если объектный тип наследует виртуальные методы, конст- рукторы или деструкторы, то он также наследует и поле ТВМ, благодаря чему до- полнительное поле таблицы виртуальных методов не размещается. Инициализация поля таблицы виртуальных методов экземпляра объекта осуще- ствляется конструктором (или конструкторами) объектного типа. Программа нико- гда не инициализирует поле таблицы виртуальных методов явно и не имеет к нему доступа. Следующий пример иллюстрирует представление в сегменте данных объектов типов TPerson, TStaff и TTeacher. k TPerson TStaff TTeacher Name Name Name Date Date Date Rate Rate Rate ТВМ TBM TBM Bonus Bonus Hours HourRate Рис. 16.2. Представление объектов типов TPerson, TStaff u TTeacher Таблица виртуальных методов Каждый объектный тип, содержащий или наследующий виртуальные методы, конструкторы или деструкторы, имеет таблицу виртуальных методов, в которой запоминается инициализируемая часть сегмента данных программы. Для каждого объектного типа (но не для каждого экземпляра) имеется только одна таблица вир- туальных методов, однако два различных объектных типа никогда не разделяют одну таблицу виртуальных методов, независимо от того, насколько эти типы иден- тичны. Таблицы виртуальных методов создаются компилятором автоматически, и программа никогда не манипулирует ими непосредственно. Указатели на таблицы виртуальных методов также запоминаются автоматически в экземплярах объект- ных типов с помощью конструкторов, программа никогда не работает непосредст- венно с этими указателями. Первое слово таблицы виртуальных методов содержит размер экземпляров со- ответствующего объектного типа. Эта информация используется конструкторами и деструкторами для определения количества байтов, которое выделяется или осво- ' бождается при использовании расширенного синтаксиса стандартных процедур. New и Dispose. л Второе слово таблицы виртуальных методов содержит отрицательный размер] экземпляров соответствующего объектного типа. Эта информация используется]
Введение в объектно-ориентированное программирование 455 программой контроля вызовов виртуальных методов для выявления неинициализи- рованных объектов (экземпляров, для которых не был произведен вызов конструк- тора) и для проверки правильности таблицы виртуальных методов. Если разрешен контроль виртуального вызова с помощью директивы {$R+}, компилятор генери- рует вызов подпрограммы контроля обращения к таблице виртуальных методов перед каждым вызовом виртуального метода. Эта подпрограмма проверяет нера- венство первого слова ТВМ нулю и равенство суммы первого и второго слов нулю. Если каждая из проверок обнаруживает несовпадение, то генерируется ошибка вы- полнения с кодом 210. Разрешение проверок границ диапазонов и проверок вызовов виртуальных ме- тодов замедляет выполнение программы и делает размер ЕХЕ-файла немного большим, поэтому рекомендуется использовать директиву {SR+} только во время отладки и переключать эту директиву в состояние {SR-} в окончательной версии программы. Третье слово ТВМ содержит смещение сегмента данных объектного типа в таб- лице динамических методов (ТДМ) или 0, если объект не содержит динамических методов. Четвертое слово ТВМ резервируется и всегда равно 0. Наконец, начиная со смещения 8 таблицы виртуальных методов, следует список 32-разрядных указа- телей методов (один указатель на каждый виртуальный метод в порядке их описа- ния). Каждая позиция содержит адрес точки входа соответствующего виртуального метода. Следующий пример демонстрирует размещение таблиц виртуальных методов типов TStaff u TTeacher. Каждый маленький прямоугольник соответствует одному слову памяти, а каждый большой прямоугольник —двум словам памяти. ТВМ TStaff ТВМ TTeacher 16 16 -16 -16 0 0 0 0 TStaffDone TTeacher.Done TStaff.GetSum TTeacher.GetSum TStaff.ShowSum TStaff.ShowSum TStaff.ShowAll TTeacher.ShowAll Рис.16.3. Схемы таблиц виртуальных методов для TStaff и TTeacher Обратите внимание на то, как TTeacher наследует метод ShowSum типа TStaff и как он переопределяет методы Done, GetSum и ShowAll. Как уже упоминалось, конструкторы объектных типов содержат специальный код, который запоминает смещение таблицы виртуальных методов объектного ти- па в инициализируемых экземплярах. Например, если имеется экземпляр S типа TStaff и экземпляр Т типа TTeacher, то вызов S.Init будет автоматически записы- вать смещение таблицы виртуальных методов типа TStaff в поле таблицы вирту-
456 Глава 16 альных методов экземпляра S, а вызов T.Init точно также запишет смещение табли- цы виртуальных методов типа TTeacher в поле таблицы виртуальных методов эк- земпляра Т. Эта автоматическая инициализация является частью кода входа конст- руктора, поэтому если управление передается в начало операторной секции, то по- ле таблицы виртуальных методов параметра Self также будет установлено. Таким образом, при необходимости конструктор может выполнить вызов виртуального метода. Функции работы с таблицей виртуальных методов Для непосредственной работы с ТВМ используются две функции: function SizeOf(Obj) : Word; И function TypeOf(Obj) : Pointer; Примененная к экземпляру объектного типа, имеющего таблицу виртуальных методов, стандартная функция SizeOf вернет записанный в таблице виртуальных методов размер. Таким образом, для объектов, имеющих таблицу виртуальных ме- тодов, функция SizeOf всегда возвращает действительный размер экземпляра, а не приведенный в описании. Стандартная функция TypeOf возвращает указатель на таблицу виртуальных ме- тодов объектного типа. Функция TypeOf принимает единственный параметр, кото-: рый может быть либо идентификатором объектного типа, либо экземпляром об^>-. ектного типа. В обоих случаях результат типа pointer является указателем на таб-? лицу виртуальных методов объектного типа. TypeOf может применяться только iq объектным типам, имеющим таблицы виртуальных методов. Применение этой: функции к другим типам приведет к ошибке. 1 Функция TypeOf может использоваться для проверки фактического типа экзем^ пляра. Например: if TypeOf(Self) — TypeOf (TPerson) then .. Таблица динамических методов Таблица виртуальных методов объектного типа содержит для каждого описан^ ного в объектном типе виртуального метода и его предков четырехбайтовую за- пись. Когда в порождающих типах (предках) определяется большее число вирту- альных методов, в процессе создания производных типов может использовать достаточно большой объем памяти, особенно если создается много производны; типов. Хотя в производных типах могут переопределяться только некоторые из на следуемых методов, таблица виртуальных методов каждого производного типа со держит указатели метода для всех наследуемых виртуальных методов, даже ecni они не изменялись, что приводит к использованию большей памяти. Динамические методы обеспечивают в таких ситуациях альтернативу. Для таки методов в Турбо Паскале вводится новый формат таблицы методов и новЬш спосо обработки вызовов методов с поздним связыванием. Вместо кодирования всех тодов объектного типа с поздним связыванием в таблице динамических методе (ТДМ) кодируются только те методы, которые были в объектном типе переопред лены. Если в производных типах переопределяются только некоторые из большей ,ч
Введение в объектно-ориентированное программирование 457 числа методов с поздним связыванием, формат таблицы динамических методов ис- пользует меньшее пространство, чем формат таблицы виртуальных методов. Предположим, что типы TStaff и TTeacher переопределены с динамическими методами следующим образом: TStaff = Object (TPerson) Bonus : Real; constructor Init(Nm,Dt : String; Rt,Bn : Real); destructor Done; Virtual; function GetSum : Real; Virtual 10; procedure ShowSum; Virtual 20; procedure ShowAll; Virtual 30; end; * TTeacher = Object (TStaff) Hours : Word; HourRate : Real; constructor Init(Nm,Dt : String; Rt,Bn,Hrt : Real; Hr : Word); destructor Done; Virtual; functiorKGetSum : Real; Virtual 10; procedure ShowAll; Virtual 30; end; Схемы таблиц виртуальных и динамических методов для TStaff и TTeacher выглядит следующим образом:__________________________________ ТВМ TStaff ТДМ TStaff 4 0 -4 Кэшируемый индекс Смещение ТДМ TStaff Кэшируемое смещение 0 3 TStaff.Done 10 20 30 TStaff.GetSum TStaff.ShowSum TStaff.ShowAll Рис. 16.4. Схемы таблиц виртуальных и динамических методов для TStaff Вызов динамических методов Обработка вызова динамического метода более сложна и требует больше вре- мени, чем вызов виртуального метода. Вместо использования инструкции call для вызова через указатель метода по статическому смещению в таблице виртуальных методов, таблица динамических методов объектного типа и таблица динамических методов его предка должны просматриваться в поиске "самого верхнего" вхожде- ния индекса конкретного динамического метода, а вызов затем должен выполнять-
458 Глава 16 ся через соответствующий указатель метода. Этот процесс требует использования значительно большего числа инструкций, которые можно записать как встроенные (inline), поэтому Турбо Паскаль содержит подпрограмму обработки вызовов, ис- пользуемую при ^вызове динамического метода. Если бы метод GetSum типа TStaff описывался как динамический метод с ин- дексом динамического метода 10, то вызов StaffA.Getsum, где Staff имеет тип PStaff, привел бы к генерации следующего кода: les DI, Staff; {загрузить Staff в ED:DI} push ES; {передать как параметр Self} push DI mov DI,ES : [DI+6]; {извлечь смещение ТДМ из поля ТВМ} aov АХ, 10; {загрузить в АХ индекс динамического метода} call Dispatch; {вызов подпрограммы обработки вызовов} Обработчик выбирает сначала смещение таблицы динамических методов из таблицы виртуальных методов, на которую указывает регистр DI. Затем использу- ется кэшируемый индекс — поле таблицы динамических методов. Обработчик проверяет, является ли индекс вызванного динамического метода индексом того динамического метода, который вызывался последним. Если это так, он немедлен- но передает управление этому методу, используя косвенный переход с помощью указателя метода, записанного по смещению, заданному полем кэшируемое сме- щение. Если динамический индекс вызванного метода не совпадает с тем, который записан в кэше, то обработчик просматривает собственную и родительские табли- цы динамических методов, используя поле ТДМ со смещением родительской ТДМ, пока он не найдет запись с данным индексом динамического метода. Индекс и смещение соответствующего указателя метода записываются затем в кэшируемые поля таблицы динамических методов, а управление передается методу. Если по ка- ким-то причинам обработчик не может найти запись с данным индексом динамиче- ского метода, он завершает прикладную программу с кодом ошибки выполнения 210. Несмотря на использование кэширования и хорошо оптимизированной подпро- граммы обработки вызовов, обработка вызова динамического метода может потре- бовать значительно больше времени, чем вызов виртуального метода. Однако в тех случаях, когда сами действия, выполняемые динамическим методом, требуют мно- го времени, дополнительное пространство, сохраняемое таблицами динамических методов, может оказаться более существенным. Вызоп конструкторов и деструкторов Конструкторы и деструкторы используют те же соглашения о вызовах, что и обычные методы, за исключением того, что дополнительный параметр размером в слово, называемый параметром ТВМ, передается через стек непосредственно перед параметром Self.
Введение в объектно-ориентированное программирование 459 Для конструкторов параметр ТВМ содержит смещение таблицы виртуальных методов для запоминания в поле таблицы виртуальных методов параметра Self, чтобы его инициализировать. Если конструктор вызывается для размещения динамического объекта с помо- щью расширенного синтаксиса стандартной процедуры New, через параметр Self передается указатель NIL. Это заставляет конструктор размещать новый динамиче- ский объект, адрес которого передается вызывающей программе через DX:AX при возврате из конструктора. Если конструктор не может разместить объект, то в DX.AX возвращается пустой указатель NIL. Наконец, если конструктор вызывается с использованием собственного иденти- фикатора метода (то есть идентификатора типа объекта, за которым следуют точка и идентификатор метода), то в параметре таблицы виртуальных методов передает- ся нулевое значение. Это является указанием конструктору на то, что ему не следу- ет инициализировать поле Self таблицы виртуальных методов. Для деструкторов нулевое значение параметра таблицы виртуальных методов означает обычный вызов, а ненулевое указывает, что деструктор был вызван с ис- пользованием расширенного синтаксиса стандартной процедуры Dispose. Это за- ставляет деструктор удалить Self непосредственно перед возвратом (размер Self определяется из первого слова Self в ТВМ). Итак, объектно-ориентированный подход помогает справиться с'такими слож- ными проблемами, как уменьшение сложности программного обеспечения, повы- шение его надежности, обеспечение возможности модификации и повторного ис- пользования отдельных компонентов программного обеспечения без изменения ос- тальных его компонентов. Систематическое применение объектно-ориентированного подхода позволяет разрабатывать хорошо структурированные, надежные в эксплуатации, достаточно просто модифицируемые программные системы. Этот подход является одним из наиболее интенсивно развивающихся направлений теоретического и прикладного программирования. ‘ В следующей главе на примере создания простого приложения Windows мы рассмотрим введение в принципы написания программ Windows на Турбо Паскале. Контрольные вопросы и задания 1. В чем заключается основная идея объектно-ориентированного программирования? 2. Каково назначение объектно-ориентированной библиотеки ObjectWindows? 3. Перечислите основные свойства объектно-ориентированных языков программирова- ния. 4. Что такое объект? Что общего имеет тип object с типом record? Что их отличает? 5. В чем заключается наследование? 6. В чем заключается полиморфность операций? 7. Что такое метод? Каковы особенности описания методов?
460 Глава 16 8. Что называется инкапсуляцией? 9. Чем отличаются статический и динамический методы? 10. Опишите назначение таблиц динамических методов. 11. Что такое конструктор, деструктор?
Глава 17. ПРОСТОЕ ПРИЛОЖЕНИЕ WINDOWS 17.1. Что представляет собой приложение Windows Приложение Windows является специальным типом программы для персо- нального компьютера, которая: должна иметь файл специального исполняемого типа формата; работать только с Windows; в общем случае работать в прямоугольном окне на экране; должна быть способна работать одновременно с другими приложениями Windows и не Windows, включая свои версии; уметь осуществлять связь и обмен данными с другими приложениями Windows. На рис. 17.1 показаны основные компоненты приложения Windows. program WinM in, uses UinTypes. UinProcs; const Ap] File Save As Param:Word;LParam:Longint):Long: {Oxt Г~~“T funt beg. W: s: Ci' ' |winmin.pas| Рис.17.1. Экранные компоненты приложения Windows
462 Глава 17 17.1.1. Особенности и преимущества Windows Windows обладает рядом особенностей как для пользователя, так и для разра- ботчика. Для пользователя они состоят в следующем. Стандартные и предсказуемые действия. Если вы знаете как пользоваться одним приложением Windows, то вы знаете как использовать все приложения. Нет необходимости устанавливать устройства и драйверы для каждого при- ложения. Windows содержит все драйверы, которые необходимы для работы пери- ферийных устройств. Взаимодействие и связь между несколькими приложениями. Многозадачный режим: возможность одновременной работы нескольких приложений. Доступ к дополнительной памяти. Windows обеспечивает защищенный ре- жим работы. Особенности Windows для разработчика состоят в следующем: Независимая от аппаратуры графика, поэтому графические приложения ра- ботают на всех стандартных типах адаптеров дисплея. Гарантированное обеспечение работы широкого диапазона принтеров, мони- торов и мышек. Развитая библиотека графических программ. Дополнительная память для больших программ. Обеспечение работы с меню, пиктограммами, побитовыми распределениями и другие возможности. Управляемая событиями архитектура Windows основана на управляемой событиями архитектуре. Это значит, что любой осуществляемый пользователем ввод рассматривается как событие. Состоит ли это событие в нажатии кнопки на мыши или в нажатии клавиши на клавиатуре, для Windows это событие, и Windows генерирует соответствующее сообщение. На- пример, если пользователь нажмет на мыши левую кнопку, Windows генерирует сообщение wm_LButtonDown. Если пользователь нажмет клавишу на клавиатуре, j Windows генерирует сообщение wm_KeyDown. Windows рассматривает все коман-| ды управления и меню, задаваемые с помощью мыши или клавиатуры, как сооб-^ щения wmCommand. J Независимая от аппаратуры графика . I Windows унифицирует процесс вывода информации на экран и принтер и сво-j дит его к единому модулю, называемому интерфейсом графического устройств® (GDI), который обеспечивает общий интерфейс для каждой программы Windows! Более того, Windows обладает драйверами для большинства стандартных графиче*! ских адаптеров и принтеров, поэтому приложение Windows будет работать без ка-1 ких-либо изменений на большинстве имеющихся в мире аппаратных средств. I
Простое приложение Windows 463 Многозадачный режим Windows разрешает пользователю одновременно запускать несколько прило- жений, устраняя необходимость программ, которые после прерывания их выполне- ния остаются резидентными (TSR). Windows не просто обеспечивает многозадач- ный режим работы, но и обеспечивает ряд возможностей межпроцессорного обме- на, таких, как буфер вырезанного изображения и динамический обмен данными (DDE). Windows организует работу нескольких приложений таким образом, что каж- дое приложение вместо полного экрана использует один или несколько прямо- угольников, называемых окнами. Эти окна можно сдвинуть на экране, изменить их размер, спрятать в качестве пиктограмм, что дает пользователю возможность быст- ро осуществлять переход от одной задачи к другой. С точки зрения программиста, это означает, что программа выводит текст или графику не в конкретное место эк- рана, а внутри окна—некоторой области, ограниченной рамкой. Также приложение должно уметь делить имеющуюся свободную память компьютера с другими при- ложениями. Управление памятью В типичном сеансе работы с Windows множество приложений запускается на выполнение и заканчивает свою работу множество раз, но при этом не происходит загрузка в память каждого приложения после предыдущего, так как в этом случае Windows скоро не хватило бы памяти. Вместо этого Windows перемещает большую часть памяти приложения либо в другую часть памяти, либо на диск, для обеспече- ния работы других приложений или самой Windows. Таким образом, приложение Windows должно соответствовать правилам Windows по динамическому управлению памятью и не использовать непосредст- венного доступа к конкретным адресам памяти. Например, традиционный указа- тель на адрес памяти может довольно скоро стать неверным при переразмещении памяти Windows, так как в этом случае указатель будет указывать на место в памя- ти, которое вероятно уже используется для хранения чего-то другого. Приложения Windows вместо указателя используют регуляторы, которые представляют собой указатели на указатели. Регуляторы—это числа, которые служат индексами в таб- лице указателей, управляемой Windows. Существуют регуляторы на элементы при- ложения, строки, инструменты рисования и ресурсы, такие, как меню и пиктограм- мы. При нормальном использовании вам не придется иметь дело с регуляторами непосредственно. Вы можете размещать и высвобождать динамическую область памяти, используя обычные программы New, Dispose, GetMem и FreeMem, а Турбо Паскаль будет взаимодействовать с Windows для определения объектов, на кото- рые в действительности указывают указатели. Одно из основных преимуществ менеджера памяти Windows состоит в воз- можности разделения скомпилированного кода между приложениями. Например, если пользователь запустит два экземпляра одного и того же приложения, то эти
464 Глава 17 два приложения будут использовать один скомпилированный код в памяти. Анало- гично, приложение может динамически загрузить модуль библиотеки, который может быть разделен между различными приложениями. Этот прием известен в качестве библиотек динамической компоновки, или DLL. 17.1.2. Ресурсы Описания интерфейсов устройств приложения Windows пользователя: его ме- ню, блоки диалога, курсоры, пиктограммы, побитовые распределения, строки и клавиши акселераторов называются ресурсами. В Windows имеется средство обра- ботки этих описаний вне исходного кода приложения. Ресурсы приложения объе- диняются с его скомпилированным исполняемым файлом до запуска приложения. Для уменьшения использования памяти приложение вызывает свои ресурсы в па- мять только тогда, когда они необходимы. Выделение спецификации ресурса из исходного кода дает дополнительное преимущество: внешний вид приложения может быть изменен без воздействия на исходный код программы. В действительности для модификации ресурсов прило- жения нет необходимости даже иметь его исходный код. Это значительно упроща- ет процесс настройки и перевода существующих приложений Windows. Турбо Паскаль для Windows имеет полный набор редакторов для создания и адаптации ресурсов—Resource Toolkit. На рис. 17.2 показан вид его окна. Динамическая компоновка Windows допускает использование приложений, включая программы на Турбо Паскале, которые загружают и высвобождают библиотечные модули в процессе своего выполнения. Эти модули должны иметь специальный исполняемый формат, называемый библиотекой динамической компоновки (DLL). Часто эти библиотеки выполняют специфические и сложные задачи, например, такие, как преобразование формата файла. Если это нужно для дела, DLL можно использовать в качестве фильтров для импортирования и экспортирования файлов. В Турбо Паскале для Windows можно создавать библиотеки динамической компоновки. Буфер вырезанного изображения Для передачи информации (текста, изображения и данных) между приложе- ниями, между разными частями приложения или во временную память для после- дующего использования пользователь может задействовать в Windows буфер выре- занного изображения. Например, приложение по обработке текстов будет исполь- зовать буфер вырезанного изображения для операций вырезки, копирования и вставки. Динамический обмен данными Другим протоколом динамического обмена данными является динамический обмен данными (DDE). Если буфер вырезанного изображения находится под пол- ным управлением пользователем, то DDE работает под управлением программы и
Простое приложение Windows 465 внешне никабе не проявляется. Одно приложение передает информацию другому путем посылки соответствующих сообщений DDE. Рис. 17.2. Окно Resource Toolkit Множественный интерфейс документа Множественный интерфейс документа (MDI)— это набор соглашений пользова- телей относительно создания окон, которые содержат внутри себя дочерние окна. Примером MDI является интегрированная среда разработки (IDE) Турбо Паскаля. При работе с Турбо Паскалем пользователь может открыть одновременно несколь- ко окон редактирования. 17.1.3. Типы данных в Windows Схема работы с данными в Windows и ее связь с языком программирования С привела к развитию некоторых специализированных типов данных при програм- мировании для Windows на Турбо Паскале. Например, регулятор окна хранится в типе HWnd. Турбо Паскаль и ObjectWindows определяют новые типы, включаю- щие в себя типы, подобные HWnd. Справка по всем этим новым типам и структу- рам данных находится в "Справочном руководстве по Windows" и может быть в любой момент получена из справочной системы Турбо Паскаля для Windows, как показано на рис. 17.3. 17.1.4. Объектно-ориентированное использование окон Как вы уже могли видеть, программирование в среде Windows требует очень аккуратного использования событий, форматов, регуляторов и других приложений, поэтому разработка программы для Windows может оказаться достаточно сложной задачей. Эта задача существенно облегчается благодаря объектно-ориентирован- ному программированию, позволяя разработчику приложения сосредоточиться на
466 Глава 17 функциях приложения, а не на его форме. Используя объекты для представления сложных структур, подобных окнам, программа на Турбо Паскале может объе- динять свои операции и хранение данных. HBitmnp HHrush Н Cursor НРС HFont Н1СВ.П HMenu HPalette HPen HBan. HStr HWnd ИИИИИМОММИММщ Г д.'-'- -11 HWnci (type) (WinTypes unit) » Declaration HVnd - THandle; Remarks HWnd defines a handle type for window handles These are commonly used by ObjectWindows interface objects to keep ’ track of their associated Windows interface elements. Many API function calls require a window handle to indicate which window they are to acton. Рис.17.3. Окна справочной системы Объектно-ориентированное программирование создает среду, в которой про- граммист использует объекты для представления достаточно сложных элементов интерфейса пользователя программы Windows. Например, окно является объектом. Объектно-ориентированное окно и типы объектов приложения управляют обработ- кой сообщений, которая требуется от программы Windows, что в значительной сте- пени упрощает взаимодействие программиста й пользователя. В действительности объекты представляют собой не только окна, но и блоки диалога и управления (блоки списков и клавиши). 17.1.5. Лучший интерфейс для Windows Для работы с программным интерфейсом приложения Windows используется объектно-ориентированное расширение Турбо Паскаля (API), скрывая от вас дета- ли программирования Windows, а также библиотека ObjectWindows. В результате этого вы можете использовать Турбо Паскаль для написания программ Windows в значительно более короткие сроки и с меньшими усилиями, чем если бы использо- вался не объектно-ориентированный подход. Примечание: API - Application-Execution Procedures and Functions (Набор процедур и функций Windows для выполнения приложений). \ 17.1.6. Абстрагирование функций Windows Приложения Windows воздействуют на свое появление и поведение путем вы- зова соответствующих функций Windows, которые представляют собой набор из
Простое приложение Windows 467 почти 600 функций, составляющих программный интерфейс приложения Windows (API). Каждая из функций требует множества разных параметров многих различ- ных типов, что уже само по себе может вызвать головную боль. Но, хотя и можно вызвать любую из функций Windows непосредственно из Турбо Паскаля, библио2 тека ObjectWindows упрощает задачу, предлагая объектные методы, которые абст-' рагируют вызовы функций. ObjectWindows позволяет сгруппировать связанные вы- зовы функций в общую процедуру, которая выполняет задачу верхнего уровня. Предлагаемый метод существенно уменьшает вашу зависимость от сотен функций API Windows, но он не мешает вам вызывать API непосредственно. ObjectWindows является наилучшим решением: объектно-ориентированная разра- ботка на высоком уровне плюс максимальное управление графической средой. 17.1.7. Регулятор окна Поскольку Object Windows определяет объекты для окон, блоки диалога и управления, она предоставляет по объектам только их поведение, атрибуты и хра- нение данных. Физическая реализация и визуальное появление элементов на экране управляется самой Windows. Объекты ObjectWindows, которые мы будем называть объектами интерфейса, формируют взаимодействие с соответствующими визуаль- ными элементами. Связь объекта и элемента называется регулятором окна. При конструировании объекта интерфейса он дает инструкцию Windows на создание элемента интерфейса. Windows возвращает регулятор, идентифицирующий этот элемент, который объект хранит в поле с именем HWindows. Многие из функций Windows требуют в качестве своего параметра регулятор окна, поэтому хране- ние его в соответствующем поле делает его доступным для объектов окна. Аналогично, поля интерфейса объекта могут использоваться для хранения инстру- ментов рисования или информации о статусе для конкретного окна. 17.1.8. Автоматизация реакции системы в виде сообщений Кроме инструкций для среды Windows о необходимости проведения опреде- ленных действий большинство приложений должно иметь возможность реагиро- вать на сотни сообщений Windows, которые являются результатом действий поль- зователя (например, нажатие на кнопку мыши), других приложений и от прочих источников. Обработка сообщений и корректная реакция на них служит опреде- ляющим фактором правильной работы вашей программы. Объекты с их заранее определенным поведением (методы) прекрасно согласу- ются с решением задачи реагирования на внешние стимулы (сообщения Windows). ObjectWindows воспринимает сообщения Windows и переводит их в вызовы метода Турбо Паскаля. Таким образом, используя ObjectWindows, вы просто определяете метод реакции на каждое .сообщение, которое нужно обработать вашему приложе- нию. Например, когда пользователь нажимает на мыши левую кнопку, Windows генерирует сообщение wm_LButtonDown. Если вам требуется, чтобы окно или управление вашей программы реагировали на такое действие, вы просто опреде- ляете метод WMLButtonDown, связанный с сообщением wm_LbuttonDown,H каж-
468 Глава 17 дый раз, когда Windows пошлет это сообщение, ваш объект будет автоматически вызывать определенный вами метод. Подобные методы называются методами реакции на сообщения. Без объектно- ориентированного программирования и ObjectWindows вам пришлось бы писать длинный оператор case для каждого окна и управления, распознающий, что при- шло сообщение, определяющий тип поступившего сообщения и затем решающий, что с ним делать. 17.1.9. Элементы интерфейса пользователя В этом разделе мы рассмотрим элементы, из которых состоит интерфейс пользователя, работающего со средой Microsoft Windows, дадим их краткое описа- ние, опишем назначение и способы создания. Окно * Окно—это прямоугольная область экрана, выделяемая прикладной программе для осуществления операций ввода/вывода. Окна составляют основу пользователь- ского интерфейса Microsoft Windows и являются основным средством взаимодей- ствия пользователя с прикладной программой. Большинство остальных элементов интерфейса являются частными случаями окна. Окна могут быть перекрывающи- мися (cascade) и заполняющими экран (tile). Окно может содержать следующие компоненты. Заголовок окна — специальная область, обычно содержащая строку текста и располагаемая в верхней части окна. Заголовок может содержать кнопку системного меню и кнопки управления размерами окна.Системноеменю'—это ме- ню, наличие которого в окне отображается специальной кнопкой. В этом меню со- держатся команды для перемещения, изменения размеров окна или его закрытия. Томка —«прямоугольная окантовка окна, используемая для указания границ окна и их изменения.Строчное меню—набор команд, из которых можно сделать выбор с помощью клавиатуры или манипулятора "мышь". Результатом выбора команды меню может быть либо выполнение какого-либо действия, либо появление вло- женного меню, также содержащего команды. Полосы прокрутки—вертикальная или горизонтальная полоса с передвигающимся бегунком посередине и кнопками- стрелками по краям. Полосы прокрутки предназначены для позиционирования той части изображения, которая должна быть видна в окне. Окно может содержать в себе дочерние окна. При закрытии основного окна за- крываются и его дочерние окна. В прикладной программе окно всегда принадлежит определенному классу окон —структуре данных, содержащей общие свойства окон, принадлежащих данному классу: наличие и тип иконки, курсора, шаблон заполнения фона, меню, и имя класса. После заполнения полей класса он регистрируется в системе с помощью функ- ции RegisterClass. После успешной регистрации класса возможно создание окон на основе этого класса. Окна данного класса создаются с помощью функции Create- Window, которой в качестве аргументов указываются заголовок окна, координаты
Простое приложение Windows 469 левого верхнего угла и размер окна, ссылка на родительское окно и ряд других па- раметров. Функция создания окна возвращает ссылку на созданное окно, которая затем используется в качестве одного из аргументов для всех функций, работаю- щих с окнами. После создания окно отображается на экране с помощью функций ShowWindow. Для уничтожения окна используется функция DestroyWindow, Ядро Windows предоставляет большое количество функций, позволяющих опре- делить характеристики созданного окна, его класса и изменить ряд характеристик. Функция GetClassWord возвращает слово или длинное целое (функция Get- ClassLong), хранящееся в классе окна по указанному в качестве параметра адресу, функция GetWindowWord возвращает из структуры данных, описывающей окно, слово или длинное целое (GetWindowLong), хранящееся в классе окна по указан- ному в качестве параметра адресу. Аналогично функции SetClassWord, Set- ClassLong, SetWindowWord и SetWindowLong позволяют изменить значения дан- ных в соответствующих структурах. Функция GetClassName позволяет узнать имя класса для указанного окна. Функция IsWindow позволяет узнать, является ли ссылка, указанная в качестве параметра, ссылкой на окно. Большая группа функций и сообщений предназначена для управления окнами. В эту группу входят функции, позволяющие отображать окно на экране, перемещать окно по экрану, выполнять прокручивание изображения внутри окна и тому подоб- ное. Подробные справки по функциям в среде программирования можно получить в справочной системе Турбо Паскаля для Windows. Панель диалога Панель диалога —это специальный тип окна, используемый для отображения и ввода информации пользователем. В большинстве прикладных программ панели диалога связаны с определенными командами меню. Панель диалога в отличие от окна не имеет кнопок изменения размеров, но ее положение на экране может быть изменено с помощью комбинаций клавиш или путем выбора соответствующих ко- манд из системного меню. Обычно панели диалога содержат одну или несколько кнопок, позволяющих пользователю выполнить определенные действия. Часто в состав панели диалога включаются комбинированные списки, строки редактирова- ния, группы кнопок с зависимой и независимой фиксацией и другие элементы управления. Панели диалога могут быть модальными и немодальными. Модальная панель диалога—это панель диалога, которая в отличие от немодальной панели не позво- ляет пользователю продолжать работу с программой до тех пор, пока не будет за- кончена работа с панелью диалога. В прикладной программе панель диалога создается на основе специального шаб- лона. Для визуального создания панелей диалога используются редакторы ресурсов —Dialog Edit, Whitewater Resource Toolkit или Resource Workshop. Ресурс типа па-
470 Глава 17 нели диалога компилируется с помощью специального компилятора ресурсов и подключается к исполняемому файлу или динамически загружаемой библиотеке. Ядро Windows предоставляет большое число функций для работы с панелями диалога. Существуют функции для создания панели диалога—CreateDialog для соз- дания немодальной панели диалога и DialogBox для создания модальной панели диалога. Ряд функций и процедур используется для контроля элементов управления, рас- положенных внутри панели диалога: CheckDlgButton и CheckRadioButton для кно- пок, SetDlgltemText и GetDlgltemText для строк редактирования и DlgDirList для , отображения списка файлов. Панель сообщения Панель сообщения является частным случаем йанели диалога, используемая для вывода информации типа сообщения о статусе операции или об ошибке. По- мимо сообщения, такая панель может содержать одну или несколько кнопок, по- зволяющих пользователю указать свою реакцию на выводимое в панель сообще- ние. Чаще всего используются кнопки Ok, Cancel, Retiy. Панель сообщения создается и отображается с помощью функции MessageBox, j которой в качестве параметров указывается заголовок окна, текст сообщения и на- ' бор кнопок, включаемых в панель. Меню < Меню—это совокупность команд, из которых можно делать выбор при помощи ? клавиатуры или манипулятора мышь. Различают строчное (горизонтальное) меню ] и вложенное (вертикальное). Глубина вложенности является довольно большой ве-J личиной и позволяет создавать комплексные меню. Обычно выбор каждой коман- ды меню приводит либо к выполнению каких-либо действий, либо к появлению вложенного меню или панели диалога. Меню создаются при помощи редакторов ресурсов и подключаются к при- кладной программе. Для использования меню, подключенного таким образом, 4 применяется функция LoadMenuIndirect. Имеется возможность динамического соз- ] дания меню с помощью функции CreateMenu, создающей пустое меню, к которому затем подключаются необходимые команды. Для этого используются функции In-- sertMenu, AppendMenu и ModifyMenu. Также имеются функции для удаления меню' (DeleteMenu, DestroyMenu), запрещения и разрешения использования отдельных команд меню в зависимости от контекста прикладной программы (EnableMenuItem) и доступа к системному меню окна (GetSystemMenu). Кнопка Кнопка—это элемент управления прямоугольной формы, имеющий название и посклающий соответствующее сообщение при его нажатии (активизации). Обычна одна из букв названия кнопки выделяется специальным образом и служит для ими- тации нажатия кнопки с клавиатуры. Нажатие кнопки с помощью манипулятора "мышь" осуществляется перемещением курсора в область кнопки и нажатием ле*
Простое приложение Windows 471 вой клавиши манипулятора. Обычно кнопки создаются как один из атрибутов па- нели диалога с помощью редактора ресурсов. Кнопка является частным случаем окна и создается при помощи функции CreateWindow. В качестве параметра класса окна указывается "Button". Среди кнопок можно выделить несколько наиболее час- то используемых: обычная кнопка, кнопка со значением по умолчанию, кнопка с фиксацией, кнопка с зависимой фиксацией, кнопка, отображаемая программой, и группа кнопок. Список Список-—это набор элементов; один или несколько из которых могут быть вы- браны из него. Типичным примером такого набора элементов может служить спи- сок имен файлов. Список создается при помощи функции CreateWindow. В качест- ве параметра класса окна указывается "ListBox". Ядро Windows предоставляет функции для добавления и удаления элементов списка, работы со списком элемен- тов и возможность одновременного выбора нескольких элементов. Строка редактирования Строка редактирования — это интерфейсный элемент, предназначенный для ввода-вывода символьной информации с возможностью ее редактирования. При указании соответствующих стилей строки редактирования возможно редактирова- ние нескольких строк и прокручивание текста. Статический текст Статический текст-— это отображаемый объект, содержащий какое-либо сообщение и не реагирующий на внешние события. Обычно статический текст используется для создания заголовков других элементов управления, в качестве разделителя для групп средств управления и т.п. Иконка Иконка^—это небольшое графическое изображение (пиктограмма) , используе- мое для наглядного определения некоторой функции, состояния и тому подобное. Чаще всего иконки использу^отся для отображения смысла содержимого окна, на- ходящегося в минимизированном состоянии. Иконки создаются с помощью редак- торов ресурсов и подключаются к прикладной программе. При определении класса окна возможно указание иконки, которая будет отображать окно в минимизиро- ванном состоянии. В Windows существует ряд предопределенных иконок, исполь- зуемых в панелях сообщений для указания на тип сообщения (информационное со- общение, предупреждение, ошибка и т.п.). Для использования иконки ее необходимо загрузить с помощью функции Loadi- con. Эта же функция используется для получения ссылки на загруженную иконку.
472 Глава 17 Курсор Курсор ।— это небольшое графическое изображение, которое позволяет опреде- лить место, где будет выполнять действие манипулятор "мышь". В Windows суще- ствует несколько типов предопределенных курсоров. Для изменения типа курсора можно использовать функцию SetCursor. Ресурсы этого типа создаются с помощью специальных редакторов ресурсов и используются в прикладной программе с по- мощью функции LoadCursor. Графическое изображение Графические изображения в среде Windows используются в двух случаях—для отображения изображений, которые проще загрузить в виде ресурса, чем рисовать с помощью вызовов графических функций, и для создания специальных типов шаблонов заполнения фона. Графические изображения, как и другие ресурсы, соз- даются при помощи редакторов ресурсов. Загрузка графических изображений осу- ществляется при помощи функции LoadBitmap. Для создания шаблона заполнения фона на основе графического изображения используется функция CreatePattem- Brush. 17,2. Архитектура Windows-программы Программист, впервые взявшись за создание Windows-программы, обнаружит, что этот процесс существенно отличен от процесса создания программы для среды DOS. Это вызвано тем, что среда Windows—это не просто набор функций, облег- чающих реализацию графического интерфейса пользователя, а целое операционное окружение, по сложности не уступающее настоящей операционной системе. Архи- тектура, управляемая событиями, накладывает отпечаток и на структуру самой прикладной программы. Для сравнения представим на рис. 17.4 программу стандартной архитектуры (А) и управляемой событиями (В). А) стандартная архитектура В) архитектура, управляемая событиями Рис. 17.4. Архитектура стандартной программы и архитектура программы, управляемой событиями
Простое приложение Windows 473 Как видно нарис.17.4А,в программе стандартной архитектуры каждая процеду- ра обрабатывает некоторые данные и в результате работы образуются новые дан- ные, на основе которых производится выбор процедуры, которая должна выпол- няться следующей. В начале и в конце программы определяются действия' rio ини- циализации и деинициализации. Из рис. 17.4 В видно, что программа, управляемая событиями, не имеет привыч- ных переходов от процедуры к процедуре. В этом случае сразу после выполнения инициализации начинает работать обработчик, событий, который, производя анализ поступившего события, передает управление процедуре-обработчику события, а после обработки события управление возвращается системе. После этого обработ- чик готов к приему следующего события. Информация о наступившем событии пе- редается в виде сообщения, которое раскрывает характер события и содержит ин- формацию, необходимую для его обработки. По окончании работы программы производится деинициализация. Таким образом, для программы работающий в среде Windows, необходимо соз- дать ряд процедур: • процедуру инициализации, • процедуру-обработчик событий, • диспетчер событий, • процедуры-обработчики конкретных событий, • процедуру деинициализации. Весь вывод информации на экран и прием сообщений программа, работающая в среде Windows, производит через окна, поэтому помимо работы с сообщениями необходимо выполнять работу с окнами. В этом разделе мы познакомимся с процессом создания простейшей Windows- программы, имеющей все необходимые части, но сначала рассмотрим некоторые обозначения и соглашения, используемые при программировании в среде Windows. 17.3 . Венгерская нотация Венгерская нотация —это соглашение о наименовании переменных и функций. Это соглашение широко используется при программировании в среде Windows, так как делает код программы более понятным. Свое название венгерская нотация по- лучила в честь родины ее создателя—Чарльза Симонаи (Charles Simonyi). Примечание: Вопрос об удобном наименовании переменных и функций является более важным, чем это кажется изначально, так как выбор удобного именования позволяет значительно снизить трудозатраты по созданию программы за счет уменьшения обращений к справочной информации. Венгерская нотация основывается на следующем принципе: имена переменных должны содержать префикс, описывающий тип данных переменной. В ряде случа-
474 Глава 17 ев префикс может служить указателем на способ использования переменной. При- ведем примеры использования соглашения о наименовании: 'achFile:Array[0..127] of Char; IpszName: PString; Первая переменная achFile содержит префикс, который расшифровывается как массив символов (array of characters), префикс переменной IpszName указывает на то, что переменная является указателем типа long на строку формата ASCIIZ (long pointer to string zero). Помимо предоставления возможности сделать код программы более понятным, венгерская нотация позволяет также избежать ряда ошибок. Например, присвоение типа IpszName = achFile; является допустимым, а присвоение IpszName == cbCount; недопустимо, так как префикс переменной cbCount указывает на то, что перемен- ная должна содержать байт. Ниже приводится список префиксов, наиболее часто используемых при про- граммировании в среде Windows.___________________________________ Префикс Описание а Массив (array) ch Символ (char) by Байт (byte) n Целое (short/int) i Целое (int) х.У Короткое целое для координат (short) ex,су Короткое целое для координат (short, count) b Булевское (bool) w Слово (word) 1 Длинное целое (long) dw Двойное слово (dword) fh Функция (function) P Указатель (pointer) s Строка (string) sz Строка, оканчивающаяся байтом 0 (string) Отметим, что такое же наименование используется при присвоении имен раз- личным константам, но при этом в качестве префикса указывается группа принад- лежности константы; например, wmXXX означает, что константа принадлежит к группе оконных сообщений (window message), а префикс константы cs XXX ука- зывает на ее принадлежность к группе стилей класса окна (class style). В заключение отметим, что. даже если вы не будете использовать предлагае- мой системы выбора имен в своих программах, понимание основных принципов этого соглашения позволит вам легче разобраться в смысле параметров функций Windows API и в большом числе прикладных программ, распространяемых в ис- ходном виде.
Простое приложение Windows 475 17.4 . Ссылки Понятие ссылки (handler) широко используется при создании Windows- программ. В контексте Windows, ссылка—это 16-разрядное слово, однозначно оп- ределяющее какой-либо объект1 среды Windows. Эквивалентом ссылок на Объекты являются ссылки на файлы в MS-DOS. Ссылка представляет собой число, имеющее смысл только в указанном контексте и определяющее объект в таблице объектов, которая поддерживается и используется ядром Windows. Ряд функций Windows API возвращает ссылки на соответствующие объекты. В файле WINTYPES.PAS тип данных ссылка определен как THandle= Word. Наиболее важными и часто используемыми являются ссылки на окна (window handle) и ссылки на контекст устройства (device context handle). Ссылка на окно по- зволяет однозначно определить каждое окно, используемое в системе. Все функ- ции, управляющие окнами, используют в качестве одного из параметров ссылку на окно. Имея ссылку на окно, вы можете выполнять над окном все необходимые дей- ствия—перемещать его, изменять его размер, делать окно невидимым, перерисовы- вать изображение внутри окна и тому подобное В файле WINTYPES.PAS ссылка на окно определена как HWnd=THandle. Ссылка на контекст устройства используется для отображения графической ин- формации. Все функции отображения» включенные в модуль управления графикой GDI, используют в качестве первого параметра ссылку на контекст устройства. Та- ким образом, прежде чем использовать функции вывода на экран или устройство печати, необходимо получить ссылку на контекст этого устройства. Контекст устройства определен как HDC=Thandle. Помимо перечисленных выше ссылок, также используются ссылки на ряд дру- гих объектов. Могут использоваться ссылки на объекты интерфейса: меню (HMenu), иконки (Шсоп) и курсоры (HCursor). Кроме того, могут использоваться ссылки на различные средства отображения: кисть (HBrush), шрифт (HFont), гра- фическое изображение (HBitmap), область (HRGN), палитра (HPalette) и карандаш (НРеп). Динамическое выделение памяти также осуществляется через ссылки. Ссылки, используемые в Windows:__________________________ Ссылку Назначение HWnd Ссылка на окно HDC Ссылка на контекст HMenu Ссылка на меню HIcon Ссылка на иконку HCursor Ссылка на курсор HBrush Ссылка на кисть HFont Ссылка на шрифт HBitmap Ссылка на растровое изображение HRgn Ссылка на область HPalette Ссылка на палитру HPen Ссылка на карандаш
476 Глава 17 17.5 . Простое приложение Windows Рассмотрим простейшую программу, работающую в среде Windows, которая бу- дет служить основой для дальнейшего изложения материала при обсуждении про- граммирования с использованием функций Windows API, а также аналогичную программу, созданную с использованием объектно-ориентированной библиотеки ObjectWindows. Практически каждая Windows-программа состоит из ряда обязательных частей: 1}функции WinMain—являющейся точкой входа в программу. (Для Pascal это то, что находится между begin... end. головной программы.) 2) создания и описания атрибутов класса окна;. 3) создания экземпляра окна данного класса; 1 4) цикла обработки сообщений; 5) оконной функции, обрабатывающей сообщения. WinMin—простейшая Windows-программа, использующая вызовы Windows API может быть записана следующим образом: program WinMin; uses WinTypes, WinProcs; const AppName='WinMin' ; {Оконная функция} function WindowProc(window : HWnd; Message, WParam : Word; LParam:Longint) : Longint; export; begin WindowProc:=0; case Message of {Обработка сообщения} wm_Destroy: begin Pos tQuitMOs sage (0)<; Exit; end; end; WindowProc:=DefWindowProc(Window,Message, WParam,LParam); end; procedure WinMain; {Точка входа в программу} var Window:HWnd; Message:TMsg; {Описание класса} WindowClass: TWndClass; begin {Только для первого экземпляра программы} if HPrevInst*O then {Определение атрибутов класса окна}
Простое приложение Windows 477 begin WindowClass.Style:=cs_HRedraw or cs_VRedraw; WindowClass.IpfnWndProc:=@WindowProc; WindowClass.cbClsExtra:=0; WindowClass. cbWndExtra .* =0 ; WindowClas s.binstance:—Hinstance; WindowClass.hicon:=LoadIcon(0,idi_Application); WindowClass.hCursor:=LoadCursor(0,idc_Arrow); WindowClass .hbrBackground: =GetStockObject (white_Brush) ; WindowClass.IpszMenuName:='*; WindowClass. IpszClassName: =AppName; {Регистрация класса} if not Registerclass (WindowClass) then Halt(255); {Создание окна с определенными атрибутами} Window: =CreateWindow (AppName, 'WinMin' ,ws_OverlappedWindow, cw_UseDefault,cw_UseDefault,cw_UseDefault, cw_UseDefault,O,O,Hinstance,nil) ; ShoWWindow (Window, CmdShow) ; UpdateWindow(Window); {Цикл обработки оеобщений} while GetMessage (Message, 0,0,0) do begin TranslateMessage(Message); DispatchMessage(Message); end; Halt(Message.wParam); end; end; begin WinMain; end. Структура программы WinMin отражает структуру практически любой Win- dows-программы и может быть использована как отправная точка для создания бо- лее сложных программ. Как видно из текста программы, она состоит из функции WindowProc, процедуры WinMain и вызовов 11 функций Windows API. Рассмотрим - действия, выполняемые каждой частью программы. г Функция WindowProc—это специальная "оконная" функция, обрабатывающая : сообщения, посылаемые окну. Эта функция вызывается непосредственно ядром ? Windows. Функции, вызываемые Windows, называются косвенно-вызываемыми - (callback function). • Процедура WinMain —это точка входа в нашу программу, которая получает управление от ядра Windows. Каждая Windows-программа должна иметь такую
478 Глава 17 процедуру. Она создает и регистрирует класс окна, затем создает и отображает ок- но на экране и активизирует цикл работы с сообщениями. Создание и регистрация класса окна Окно всегда создается на основе класса. В этом классе указывается адрес "окон- ной" функции, обрабатывающей поступающие от ядра Windows сообщения, а так- же атрибуты всех окон, принадлежащих этому классу, то есть задаются основные свойства класса. Перед созданиям окна класс должен быть зарегистрирован вызо- вом функции RegisterCiass. Класс окна представлен структурой TWndClass. Когда всем полям структуры TWndClass присвоены необходимые значения (оп- ределены атрибуты окна), для' регистрации класса окна производится вызов функ- ции RegisterCiass. Этой функции передается структура типа TWndClass, содержа- щая атрибуты окон данного класса. Создание и отображение окна Созданный класс окна определяет основные характеристики окна. При создании конкретного окна данного класса, используя вызов функции CreateWindow, можно указать более детальные характеристики для данного конкретного окна. Функция CreateWindow возвращает ссылку на окно типа HWnd, сохраняемую в переменной Window. Эта ссылка затем используется большинством функций Windows, рабо- тающих с этим окном. После того как окно создано, его необходимо отобразить на экране. Для этого используется функция ShowWindow, а для перерисовки изображения внутри окна— функция Update Window. Функция ShowWindow принимает в качестве параметра ссылку на окно, возвращенную функцией CreateWindow, и параметр cmdShow, оп- ределяющий,как окно отображается на экране. Вызов процедуры UpdateWindow вызывает перерисовку рабочей области окна. Цикл обработки сообщений После того как окно отображено на экране, управление передается циклу обра- ботки сообщений. В этом цикле при помощи функции GetMessage сообщения из- влекаются из очереди и помещаются в структуру типа TMsg. Для всех сообщений, отличных от сообщения wm Quit (завершение работы программы), эта функция возвращает ненулевое значение,и цикл продолжает обработку сообщений. Процедура TranslateMessage передает структуру типа' TMsgsm ядру Windows для преобразования сообщений о введенных символах. Ядро Windows посылает сооб- щение оконной функции нашей программы, указанной при создании класса окна. После того как оконная функция обработала сообщение, управление возвращается в цикл обработки сообщений. Оконная функция Оконная функция выполняет непосредственную обработку сообщений. Пара- метры, передаваемые этой функции, эквивалентны полям структуры типа TMsg.
простое приложение Windows z Каждое получаемое оконной функцией сообщение имеет уникальный идентифи тор (см. файл WINTYPES.INT). Обычно для обработки сообщений используе конструкция типа переключателя. Все сообщения, не обрабатываемые оконной функцией, передаются специальн функции ядра Windows DefWindowProc. Наша программа обрабатывает всего лишь одно сообщение—wmJDestroy. 3 сообщение означает, что ядро Windows пытается закрыть окно в ответ на коман Close системного меню или нажатие клавиш Alt-F4. В ответ на это сообщение в зовом процедуры PostQuitMessage, наша программа помещает в очередь сообщен wm Quit. Когда функция GetMessage получает это сообщение, цикл обработки < общений заканчивает работай программа завершается. Упражнение. Введите текст программы WinMin, откомпилируйте ее и запустите на i полнение. На экране появится окно, которое можно перемещать, изменять его размеры наконец, закрыть при помощи системного меню окна. Выводы Как можно увидеть из приведенного текста программы и дальнейших рассужг ний, создание Windows-программ не является простой задачей. В отличие, от обы ных программ, работающих в среде DOS, среда Windows управляет нашей пр граммом посредством посылки сообщений. Прикладная программа, обрабатыв Посылаемые ей сообщения, выполняет необходимые действия, заложенные функциональным назначением* Абстрагируясь, можно сказать, что Windows-программа, как и обычная пр грамма, состоит из трех частей: ' Инициализация—регистрация класса окна, создание и отображение окна. Выполнение—цикл обработки сообщений. Завершение—закрытие окна и возврат в среду Windows. 17.6. Простейшая программа, использующая ObjectWindows Как мы отметили выше, процесс создания даже простейшей Window Программы является достаточно трудоемким. Для облегчения этого процесса комплекте с компилятором TPW поставляется объектно-ориентированная библи< йека ObjectWindows (OWL), которая берет на себя всю рутиную работу по начал: иному созданию Windows-программ. Обработка сообщений в этом случае выполн Ьтся с помощью механизма динамических виртуальных методов. I Библиотека объектов OWL содержит набор объектов, необходимых для созд вния приложений любой степени сложности, работающих под управлением Wii flows. Она включает объекты, соответствующие всем интерфейсным элемента Windows, таким, как окна, диалоговые панели, элементы управления, а также обт ркт, представляющий собственно приложение (TAppUcation). Любое приложен»
480 Глава 17 создаваемое при помощи OWL, основывается на объекте-потомке объекта TAppli- cation. Для создания простого приложения достаточно воспользоваться только тремя методами этого объекта: }) конструктор Init (инициализация); 2) метод, Run (выполнение); 3) деструктор Done (завершение). Простейшая -программа, созданная при помощи OWL, будет выглядеть следую- щим образом: program WinDemo; Uses WObjects; { Подключение библиотеки OWL} Var MyApp: TApplication; Begin MyApp.Init('Demo'); MyApp.Run; MyApp.Done; End. Результат работы этой программы аналогичен рассмотренной нами выше. . Рассмотрим назначение ее элементов: Конструктор Init Конструктор Init выполняет весь набор действий, которые необходимы как для инициализации самого объекта с виртуальными методами, так и для инициализа- ции'конкретного экземпляра приложения, создания и отображения главного окна и подключения оконной функции. Параметр конструктора задает имя приложения, которое хранится в поле Name объекта TApplication. . Метод Run Метод Run содержит цикл обработки сообщений. Программная реализация не отличается от той, которая была рассмотрена выше. Деструктор Done Деструктор Done выполняет ряд действий по завершению работы программы и > деинициализации объекта. Помимо приведенных трех методов, объект TApplication имеет широкий набор методов для полного контроля над созданием конкретного приложения и обеспе- чением его работы. Упражнение. Введите текст программы WinDemo, откомпилируйте ее и запустите на исполнение. На экране появится окно, над которым вы можете выполнять различные опера- ции: перемещать, изменять его размеры и, наконец, закрыть при помощи системного меню окна.
Простое приложение Windows 481 Контрольные вопросы и задания 1. Перечислите основные компоненты приложения Windows и опишите их назначение. 2. Каковы основные особенности Windows для программиста? 3. В чем суть архитектруы программы, управляемой событиями. 4. Что такое ресурсы программы, какие инструментальные средства имеет Турбо Пас- каль для Windows для создания или редактирования ресурсов? 5. Что такое API? Каково его назначение? 6. Каково назначение объектно-ориентированной библиотеки ObjectWindows? 7. Что такое регулятор окна, каково его назначение? 8. Перечислите элементы интерфейса пользователя, работающего со средой Microsoft Windows, опишите их назначение и способы создания. 9. Перечислите набор обязательных процедур для программы, работающий в среде Windows. Опишите назначение каждой из них. 10. Зачем используется венгерская нотация ина каких принципах она основывается? Задание Изучите текст демонстрационных программ из коллекции фирмы Borland, поставляе- мых со средой программирования Turbo Pascal for Windows. Откомпилируйте и про- верьте их действие. Обратите внимание на использование в них библиотеки ObjectWindows.
482 i Таблица ASCII 1 ПРИЛОЖЕНИЯ Приложение А. ТАБЛИЦА ASCII 0 - 32 64 - @ 96 С 128 - A 160 - а 192 . L 224 -Р 1 - ® 33 - ! 65 - А 97 - а 129 - Б 161 - 6 193 _ _L 225 -с NT Ф 34 - “ 66 - В 98 - Ь 130 - В 162 - в 194 - т 226 -т 3 - V 35 - # 67 - С 99 - с 131 - Г 163 - г 195 - * 227 - У " 4 - ♦ 36 - $ 68 - D 100 - d 132 - Д 164 - Д 196 - — 228 - Ф 5 - * 37 - % 69 - Е 101 - е 133 - E 165 - е 197 - + 229 - X 6 - 4 38 - & 70 - F 102 - f 134 - Ж 166 - ж 198 - Is 230 - ц 7 - • 39 - * 71 - G 103 - 9 135 - 3 167 - 3 199 - II- 231 - ч 8 - □ 40 - ( 72 - Н 104 - h 136 - И 168 - и 200 - IL 232 - ш 9-0 41 - ) 73 - I 105 - I 137 - Й 169 - й 201 ’ IF 233 - «ц 10 -0 42 -* 74 - J 106 - j 138 - К 170 - К 202 _ JL 234 - ъ 11 -сГ 43 - + 75 - К 107 - к 139 - Л 171 - л 203 _ у 235 - ы 12 -? 44 - , 76 - L 108 - I 140 - М 172 - м 204 . L г 236 - ь 13 - J 45 - - 77 - М 109 - m 141 - Н 173 - н 205 237 - 3 14 - Л 46 - . 78 - N 110 - n 142 - 0 174 - о 206 . JL V 238 - ю' 15 - {> 47 - / 79 - 0 111 - о 143 - П 175 - п 207 . 1 239 - я , 16 - > 48 - 0 60 - Р 112 - P 144 - Р 176 — Й! 208 _ JL 240 -е 17 -◄ 49 - 1 81 - Q 113 - 4 145 - С 177 ЦЕ “ Ж 209 241 - ё ! 18-$ 50 - 2 82 - R 114 - r 146 - Т 178 210 - т 242 -S' 19 - || 51 - 3 83 - S 115 - s 147 - У 179 - > 211 . IL 243 - e'i 20 - 1 52 - 4 84 - Т 116 -1 148 - Ф 180 --I 212 . к 244 тП 21 - § 53 - 5 85 - и 117 - u 149 - X 181 -=| 213 - Г 245 -1 22 - а 54 - 6 86 - V 118 - V 150 - Ц 182 я 214 - 1Г 246 - "Г ' 23 - 1 55 - 7 87 - W 119 - w 151 - Ч 183 - К 215 - 247 24 - Т 56 - 8 88 - X 120 - X 152 - Ш 184 -1 216 248 • 57 г 9 ео »! Y 1П .153 - Щ 185 -I 1 217 • J 249 _ • 26 - 58 - : 90 - Z 122 - z 154 - Ъ 186 218 250 27 - <- 59 - ; 91 - [ 123 - { 155 - Ы 187 1 219 251 28 - Ц 60 - < 92 \ 124 - I 156 - Ь 188 220 - 252 57 2 29-о 61 - = 93 - ] 125 -} 157 - Э 189 . JI 221 г 253 30 - А 62 - > 94 - А 126 158 - Ю 190 . =J 222 1 254 - *1 31 - ▼ 63 - ? 95 - _ 127 -□ 159 - Я 191 -1 223 в и 255 - О
Приложение В. УПРАВЛЕНИЕ КЛАВИАТУРОЙ Стандартная клавиатура имеет три типа клавиш: • символьные (буквы, цифры); • управляющие (функциональные клавиши, перемещения курсора, вставка, [рдение и т.д.); • сдвига (УПР(С1т1), ДОП(Ак), ЦИФ(ЫшпЬоск), ФПБ(Сар8Ьоск) и др.). Обработка кода сканирования При нажатии какой-либо клавиши микропроцессор клавиатуры вырабатывает ртветствующий стандартам для персонального компьютера код, который преоб- зуется системой Турбо Паскаль в так называемый код сканирования. Каждая кла- апа имеет свой уникальный код сканирования, который зависит от местоположе- 1Я клавиши на панели клавиатуры и не связан с изображенным на клавише симво- iM. Код сканирования может подвергаться анализу в программе и является осно- й алгоритмов управления клавиатурой. 1 Каждая группа клавиш вырабатывает коды сканирования определенной струк- ры. Символьные клавиши возвращают при нажатии одно значение, которое при- рго называть простым кодом. Управляющие клавиши при нажатии возвращают а значения, причем первое из них равно 0 (это и есть признак управляющей кла- 1а второе — фиксированная соответствующая нажатой клавише величина. эзврата специальных клавиш принято называть расширенными. )авление клавиатурой осуществляется посредством специализированных й модуля CRT: ReadKey и KeyPressed. Разработка качественных пользова- IX интерфейсов для работы с экраном невозможна без использования этих . Кроме них в управлении могут участвовать и стандартные процедуры вво- и Readln. икция ReadKey считывает символ с клавиатуры и возвращает значение типа 'ение символа не сопровождается отображением его на экране дисплея, что । используется при разработке экранных интерфейсов типа меню и многих угих приложений, требующих постоянного управления посредством клавиатуры. ^Ъ¥о^1м стандартным средством угфаЬлёнйя* клавиатурой' являете» функции ^Pressed. Функция возвращает булевское значение True, если была нажата какая- во клавиша, и False — в противоположном случае. Связь с функцией ReadKey уществляется следующим образом: если нажатие клавиши произошло и lyPressed возвращает True, то считывание по ReadKey происходит немедленно и приводит к ожиданию нажатия. Если же KeyPressed возвращает значение False, действие ReadKey сводится к ожиданию нажатия. , Структура возвращаемых функциями KeyPressed и ReadKey значений пред-, явлена в табл. В.1.
484 Приложение В Таблица В. 1 Структура возвращаемых клавиатурой значений Функции Клавиши символьные управляющие сдвига KeyPressed True True False ReadKey 1 значение #0+1 значение - Обработку кодов сканирования символьных и управляющих клавиш иллюстри- рует следующий пример: program DemoScanCode; uses Crt; const ' Null = #0; Esc = #27; Fl = #59; FIO = #68; InsKey — #82; var ExtendKey: boolean; Ch: char; function GetKey: char; {Функция ждет нажатия клавиши} var * Ch: char; begin ExtendKey:= False; Ch:= ReadKey; if Ch — Null then begin ExtendKey:= True; Ch:= ReadKey * end; GetKey:— Ch end; {GetKey} begin {Основная программа} ClrScr; repeat Ch := GetKey; if NOT ExtendKey then Writeln(* Нажата символьная клавиша с кодом *, byte(Ch)) else case Ch of Fl..FIO : Writeln(’Нажата функциональная клавиша*);
Управление клавиатурой 485 InsKey : Writeln (' Нажата клавиша <Insert>') else Writeln('Расширенный код #00+',byte(Ch)); end; (Case} until (Ch <> Esc); end. Обработка нажатия клавиш сдвига Клавиши сдвига возвращают коды, которые не распознаются средствами Тур- - бо Паскаля, и поэтому их применение представляет определенную сложность. Для того чтобы обнаружить нажатие клавиш сдвига, нужно протестировать со- держимое двух последовательных байтов памяти, которые расположены в области переменных BIOS. Каждый бит из тестируемых отвечает за нажатие какой-либо од- ной клавиши сдвига. В связи с особым статусом прерываний, обрабатываемых че- рез BIOS, в области памяти, начиная с адреса $0000:$0400, располагаются перемен- ные BIOS, которые дублируют все наиболее важные характеристики, получаемые с помощью этих прерываний. Работа всех прерываний BIOS невозможна без этой области памяти, поэтому какая-либо некорректная запись в эту область мсЛкет на- рушить работоспособность машины. Если, например, нужно обработать нажатие клавиши ЦИФ (NumLock), то нуж- но будет просмотреть 5-й бит байта памяти с абсолютным адресом $0000:$0417, который соответствует этой клавише. Программа обработки клавиши ЦИФ (NumLock) может выглядеть следующим образом: program DemoNumLock; uses Crt; const Esc = #27; var Ch : char; ShiftByte: byte absolute $0000:$0417; begin repeat if KeyPressed then Ch:= ReadKey; if ShiftByte AND $20 <> 0 then Write('Клавиша NumLock нажата'); until Ch <> Esc; end. Коды клавиатуры При возвращении расширенных кодов в качестве первого знака возвращается ‘ нулевой байт, а второй знак соответствует нажатой клавише или комбинации кла- виш. В табл. В.2 приведено соответствие второго возвращаемого кода нажатым ^'клавишам.
486 Приложение В Таблица В.2 Расширенные коды клавиатуры Код Зйачение Код Значение 16 — 25 Alt-Q/W/E/R/T/Y/U/1/О/Р 114 Ctrl - PrtScr 30 — 38 Alt-A/S/D/F/G/H/1/J/K/L 115 Ctrl - LeftArrow(CrpenKa влево) 44 — 50 Alt-Z/X/C/V/B/N/M 116 Ctrl - RightArrow(CTpenKa впрвво) 59 — 68 F1-F10 117 Ctrl-End 71 Ноше (Исходная позиция) 118 Ctrl-PgUp 72 UpArrow (Стрелка вверх) 119 Ctrl-Home 73 PgUp (Страница вверх) 120—131 Alt-l/2/3/4/5/6/7/8/9/О/ — /= 75 LeftArrow(CTpenKa влево) 132 Ctrl-PgDn 77 RightArrow (Стрелка вправо) 133 F" 1 79 End (Конец) 134 F12 80 DownArrow (Стрелка вниз) 135 Shift-Fl 1 81 PgDn (Страница вниз) 136 Shift-F12 82 Ins (Вставка) 137 Ctrl-Fl 1 83 Del (Удаление) 138 Ctrl-F12 84 — 93 Fl 1-F20 (Shift-Fl по Shift-F10) 139 Alt-Fll 94—103 F21-F30 (Ctrl-Fl по Ctrl-FlO) 140 AH-F12 104—113 F31-F40 (Alt-Fl по Alt-F10) Коды опроса клавиатуры — это стандартные коды персонального компьютера, генерируемые микропроцессором клавиатуры при нажатии какой-либо клавиши. Этот код является входным для системы Турбо Паскаль, которая преобразует их в •коды, представленные в табл. В.2. Коды опроса клавиатуры, приведенные в табл. В.З, указаны в шестнадцатеричном виде. Таблица В.З Коф>1 опроса клавиатуры Клавиша в шестнадцатеричном виде Код опроса Клавиша в шестнадцатеричном виде Код опроса Esc 01 02 " Q 10 !1 W 11 @2 03 Е 12 #3 04 R 13 $4 05 Т 14 %5 06 Y 15 Л6 07 и 16 &7 08 I 17 ♦8 09 О 18 (9 . 0А р 19 )0 ОВ {[ 1А
Управление клавиатурой 487 Продолжение Клавиша в шестнадцатеричном виде Код опроса Клавиша а шестнадцатеричном виде Код опроса — ОС }] IB += OD Enter 1C bs ; ОБ м 2B CTRL ID Z 2C А IE X 2D S IF с 2E. D 20 V 2F F 21 в 30 G 22 N 31 ’ Н 23 м » 32 J 24 <, 33 К 25 >. 34 L 26 ?/ 35 •» 27 RightShift 36 ш 28 Prtscr 37 29 Alt 38 Л. Shift 2A Home 47 Space 39 UpArrow 48 CapsLock ЗА PgUp 49 Fl ЗВ — 4A F2 ЗС LeftArrow 4B F3 3D 5 4C F4 ЗЕ RightArrow 4D F5 3F + 4E F6 40 End 4F F7 41 DownArrow 50 F8 42 PgUp 51 F9 43 Ins 52 FIO 44 Del 53 Fil D9 NumLock 45 F12 DA ScrollLock 46 Tab OF . • 1 t : r. 1
Приложение С. МЕНЮ ИНТЕГРИРОВАННОЙ СИСТЕМЫ ПРОГРАММИРОВАНИЯ ТУРБО ПАСКАЛЬ 7.0 Турбо Паскаль дает возможность легко и эффективно писать, редактировать, компилировать, редактировать связи и отлаживать программы. Все это позволяет делать интегрированная среда программирования фирмы Borland, коротко IDE. Для ознакомления со средой запустите программу TPTOUR. Она продемонст- рирует возможности интегрированной среды программирования и покажет, как от- крывать файлы, редактировать их, компилировать, запускать и отлаживать програм- мы, к тому же сформирует у вас навыки общего управления окнами. Для детального изучения постоянно используйте встроенную систему справоч- ной информации Турбо Паскаль. Так как она является контекстно-ориентирован- ной, то, выбрав нужный пункт меню интегрированной среды и нажав клавишу F1, вы получите справочную информацию о назначении данного пункта. После запуска среды программирования Турбо Паскаль 7.0 в верхней части эк- рана выводится меню: File Edit Search Run Compile Debug Tools Options Window Help Пункт меню File (файлы) выбирается нажатием FIO — File или Alt+F. Меню File позволяет открывать и создавать файлы с программами в окнах ре- дактора, а также сохранять изменения, выполнять другие файловые функции, осу- ществлять временный выход в DOS и выходить совсем из среды программирова- ния. После выбора этого пункта меню на экран выводится выпадающее меню. New Open... F3 Save F2 Save as... Save all Change dir... Print Printer setup... DOS shell Exit Alt+X Команда New открывает новое редакционное окно с именем по умолчанию NONAMEXX.PAS (вместо XX ус- танавливаются числа от 0 до 99) и автоматически делает его’активным. Команда Open (открыть) — F3 показывает диалого- вое окно с выбором программных файлов для их откры- тия в окне редактора. Это диалоговое окно содержит ок- но ввода, список файлов, кнопки, помеченные как Open (Открыть), Replace (Заменить), Cancel (Отказ) и Help (Справочная информация), и информационную панель, описывающую выбранный файл. Вы можете выполнять любое из следующих дейст- вий: • Наберите полное имя файла и выберите Replace (за- менить) или Open (открыть). Open загружает файл в но-
Меню интегрированной системы программирования 489 вое окно редактора. При выборе Replace окно редактора должно быть активным; содержимое окна заменяется выбранным файлом. • Наберите имя файла со спецификатором (знаком вопроса или звездочкой), что фильтрует список файлов в соответствии с вашими спецификациями. • Выберите стрелку вниз для выбора спецификации файла из архивного спи- ска файловых спецификаций, который вы ввели ранее. • Просмотрите содержимое различных справочников путем выбора имен справочников из списка файлов. Для выбора имени нужно дважды его отметить мышью или применить клави- ши со стрелками и нажать Enter. Как только вы набрали или выбрали требуемый файл, отметьте кнопку Open (выберите Cancel в том случае, если вы изменили Ъвое решение). Также можно на- жать Enter, как только файл был выбран, или можно дважды отметить имя файла. Примечайие. Если вы выбрали Replace вместо Open, то выбранный файл заменит файл в активном окне редактора вместо открытия нового окна. Команда Save (сохранить) — F2 сохраняет файл, находящийся в активном ок- не редактора, на диск. (Этот элемент меню недоступен, если нет активного окна редактора.) Если файл имеет имя по умолчанию (NONAMEOO.PAS или подобное), Турбо Паскаль откроет диалоговое окно Save File As (Сохранить файл как) для то- го, чтобы позволить вам переименовать и сохранить его в текущем справочнике или на другом устройстве. Это диалоговое окно подобно окну, открывающемуся для команды Save As (сохранить как), описанной ниже. Команда Save As (сохранить как) позволяет сохранить файл, находящийся в активном окне редактора, под другим именем, в другом справочнике, на другом устройстве. После выбора этой команды можно увидеть диалоговое окно Save File As, в котором нужно набрать новое имя, необязательно с устройством и справочни- ком, и нажать или выбрать ОК. Все окна, содержащие этот файл, обновятся с этим новым именем. Если вы выберете имя существующего файла, этот файл будет перекрыт. Команда Save all (сохранить все) работает точно так же, как команда Save, за исключением того, что она сохраняет содержание всех модифицированных фай- лов, а не только файл, находящийся в активном окне редактора. Эта команда недос- тупна, если ни одно окно редактора не открыто. 1' ’' ' ’f ‘ : Команда Change dir (изменить справочник) позволяет с помощью диалогово- го окна Change Directoiy задать устройство и справочник как текущий. Текущий справочник — это справочник, который Турбо Паскаль использует для сохране- ния файлов и их поиска. (При использовании относительных путей в Options/Directories они относятся только к текущему справочнику). При этом су- ществуют два способа смены справочников: набрать путь доступа нового справоч- ника в окне ввода и нажать Enter, или выбрать требуемый справочник в дереве справочников (если вы используете клавиатуру, нажмите Enter, чтобы сделать
490 Приложение С справочник текущим), а затем выбрать ОК или нажать Esc для выхода из диалого- вого окна. Если вы изменили свое решение по выбранному справочнику и хотите вернуть- ся к предыдущему (но вы ещ^ не вышли из диалогового окна), выберите кнопку Revert. Команда Print (печать) позволяет печатать содержание активного окна редак- тора. Турбо Паскаль расширяет табуляции (заменяет символы табуляции соответст- вующим количеством пробелов) и затем посылает их на печатающее устройство DOS. Эта команда недоступна, если активное окно нельзя распечатать. Используй- те Ctrl+K+P для печати только выбранного текста. Команда Printer setup открывает диалоговое окно установки принтера. Команда DOS shell (временный выход в DOS) позволяет временно покинуть Турбо Паскаль для введения команды DOS или программы. Для возврата в Турбо Паскаль наберите EXIT и нажмите Enter. Примечание. Вы можете столкнуться с тем, что во время отладки будет недостаточно памяти для выполнения этой команды. В этом случае завершите сеанс отладки посредст- вом выбора Run/Program Reset (Ctrl+F2). Предупреждение. Не инсталлируйте никакие TSR-программы (такие, как SideKick), ес- ли временно вышли в DOS, так как память может стать нераспределяемой. Команда Exit (выход) — Alt+X приводит к выходу из Турбо Паскаля, удаляет его из памяти и возвращает вас в командную строку DOS. Если вы не сохранили какие-то изменения, то Турбо Паскаль запрашивает, хотите ли вы их сохранить пе- ред выходом. Пункт меню Edit (редактирование) выбирается нажатием Alt+E. Он позво- ляет вырезать, копировать и вставлять текст в окне редактора. Можно также от- крыть окно Clipboard (карман) для просмотра и редактирования его содержимого. После выбора этого пункта меню на экран выводится выпадающее меню. . Undo Alt+BkSp Redo Перед использованием большинства команд это- го меню вам необходимо выбрать текст (потому что большинство действий редактора применяются к вы- бранному тексту). Для выбора текста с помощью клавиатуры можно использовать следующие методы: • нажмите клавишу Shift во время нажатия кла- виш со стрелками; • нажмите клавишу Ctrl+K+B для отметки начала блока. Затем передвиньте курсор к концу блока и на- жмите Ctrl+K+K; Cut Shift+Del Copy Ctrl+Ins Paste Shift+Ins Clear Ctrl+Del Show clipboard • для выбора отдельного слова передвиньте кур- сор к слову и нажмите Ctrl+K+T;
Меню интегрированной системы программирования 491 • для выбора отдельной строки нажмите Ctrl+K+L. Для выбора текста с помощью мыши: • тащите указатель мыши над требуемым текстом. Если необходимо продол- жить выбор за пределами окна, тащите мышь в нужную сторону, и окно автомати- чески будет сдвигаться; • для выбора отдельной строки дважды отметьте ее; • для выбора текста строка за строкой нажмите кнопку мыши и тащите мышь по тексту; I • чтобы расширить или уменьшить выбранный кусок, одновременно нажмите Shift и клавишу мыши в любом месте документа. Как только текст выбран, команды в меню Edit становятся доступными, а кар- ман становится полезным. Карман является чудом после вырезания и вставки тек- ста. Это специальное окно в Турбо Паскале, хранящее текст, который был удален или скопирован, так что можно вставить его в любом месте. Карман работает в тесном взаимодействии с командами редактора. Команда Undo (отменить) — Alt+BackSpace отменяет последнюю команду редактирования, выполненную над строкой (включая набор текста в пустой строке или Ctrl+Y). Эта команда работает только с последней модифицированной или уда- ленной строкой. Команда Redo отменяет последнюю команду Undo в строке. Команда Cut (вырезать) — Shift+Del удаляет выбранный текст из документа и помещает этот текст в карман. Можно вставить этот текст в любой другой доку- мент (или в другое место в этом же самом документе) посредством выбора коман- ды Paste (вставить). Текст остается в кармане, так что можно вставлять один и тот же текст много раз. Команда Сору (копировать) — Ctrl+Ins оставляет выбранный текст нетрону- тым, но помещает его точную копию в карман. Можно вставить этот текст в лю- бой другой документ посредством выбора команды Paste (вставить). Можно также скопировать текст из окна справочной информации: с клавиатуры. Команда Paste (вставить) — Shift+Ins вставляет текст из кармана в текущем окне в позицию, указанную курсором. Текст, .который вставляется в настоящий мо- мент, в окне кармана помечен как текущий блок. Команда Clear (очистить) — Ctrl+Del удаляет выбранный текст, не помещая его в карман. Это означает, что нельзя вставить этот текст, как в случае выбора ко- манд Cut (вырезать) или Сору (копировать). Текст, удаленный с помощью этой ко- манды, невосстановим. Можно очистить сам карман посредством выбора всего тек- ста в кармане и последующим выбором команды Edit/Clear. Команда Show Clipboard (открыть карман) открывает окно кармана, который хранит текст, вырезанный и скопированный из других окон. Текущий выбранный
492 Приложение С (высвеченный) текст — это текст, который Турбо Паскаль использует при выборе команды Paste (вставить). Можно редактировать карман так, чтобы вставляемый текст точно соответствовал требуемому. Окно кармана похоже на любое другое окно редактора. Единственное отличие окна кармана возникает при вырезании или копировании текста. Когда вы выбрали текст в окне кармана и затем выбрали команду Cut (вырезать) или Сору (копировать), выбранный текст немедленно появ- ляется внизу окна. (Помните, что любой текст, который был вырезан или скопиро- ван, добавляется в конец кармана', поэтому позднее его можно вставить.) Меню Search (поиск) выбирается нажатием Alt+S. Оно позволяет осуществ- лять поиск текста, объявления процедур и месторасположение ошибок в ваших файлах. После выбора этого пункта меню на экран выводится выпадающее меню. Команда Find (найти) — Alt+S+F (или Ctrl+Q+F) показывает диалоговое окно Find, по- зволяющее набрать текст, который вы хотите найти, и установить опции, влияющие на поиск. Диалоговое окно поиска содержит несколько кнопок: • Options (опций); • Case sensitive — различение прописных и строчных букв; • Whole words only — только целые слова; . • Regular expression — регулярное выражение. Включайте эту кнопку в том случае, лсли хотите, чтобы Турбо Паскаль распознавал спецификаторы л, $,., * *, +, [], \ в строке поиска. Они означают следующее: Л — в начале строки — начало строки; $ — в конце выражения—конец строки; . — любой символ. * — символ, сопровождаемый звездочкой, означает любое число повторений (включая нуль) этого символа. Например, Ьо* означает bot, b, boo, и даже be; ч---символ, сопровождаемый знаком плюс, означает любое число повторений этого символа (но не нуль). Например, Ьо+ означает bot или boo, но не be или Ь; [] — символы в квадратных скобках означают какой-то один символ, стоящий в этих скобках. Например, [bot] означает или Ь, или о, или t; [Л] — этот знак в квадратных скобках в начале строки означает "не". Таким образом, [Abot] означает любой символ, кроме Ь, о или t; [ — ] — внутри квадратных скобок означает диапазон символов. Например, [Ь — о] означает любой символ от b до о; Find- Replace... Search again Go to line number- Show last compiler error Find error— Find procedure—
Меню интегрированной системы программирования 493 \ — этот знак перед символом спецификатора указывает Турбо Паскалю на то, что нужно рассматривать этот символ как литеру, а не как спецификатор. Напри- мер, \Л означает л, а не начало строки. . Введите строку в окне ввода и выберите ОК для начала поиска, а для отказа от поиска выберите Cancel. Если вы хотите ввести строку, которую уже искали, выбе- рите стрелку вниз, чтобы посмотреть архивный список и осуществить из него вы- бор. Можно также выбрать слово, на котором стоит курсор в окне редактора, и использовать его в окне поиска посредством просто вызова Find из Search меню. • Direction — выбор направления поиска (вперед, назад). • Scope — область поиска (весь файл или выделенный текст). • Origin — начало поиска (во всей области или от позиции курсора). Команда Replace (заменить) — Alt+S+R (или Ctrl+Q+A) выводит диалоговое окно, позволяющее набирать образец текста для поиска и образец текста, на кото- рый его надо заменить. Диалоговое окно замены содержит несколько зависимых и независимых кнопок; многие из них идентичны кнопкам диалогового окна поиска, описанного ранее. Дополнительная кнопка Prompt on replace (подсказка для заме- ны) управляет подсказкой для каждой замены. Наберите строку поиска и строку замены в окнах ввода и выберите ОК или Change АП (заменить все) для начала поиска или нажмите Cancel для отказа. Если вы хотите ввести строку, использованную ранее, нажмите стрелку вниз для про- смотра архивного списка и выбора в нем. Если Турбо Паскаль находит заданный текст, он спрашивает, хотите ли вы произвести замену. При выборе ОК он находит и заменяет только первый образец элемента поиска. При выборе Change АП (заменить все) он заменяет все вхожде- ния, как это определено с помощью кнопок Direction (направление), Scope (об- ласть) и Origin (начало). Как и в диалоговом окне поиска, можно выбрать слово, на котором стоит курсор в окне редактора и использовать его в окне ввода Text to find (текст для поиска) простым вызовом Find или Replace (найти или заме- нить) из Search menu (меню поиска). Вы можете добавить текст из окна редакто- ра с помощью стрелки вправо. Команда Search Again (поиск вновь) — Ctrl+L повторяет последнюю команду Find или Replace. Все установки, которые были сделаны в последнем диалоговом окне (Find или Replace), остаются действительными при выборе Search Again. Команда Go to line number (идти к строке номер) выдает подсказку номера строки, которую вы хотите найти. . Примечание. Турбо Паскаль показывает текущий номер строки и номер столбца в ниж- нем левом углу каждого окна редактора. Команда Show last compiler error показывает последнюю ошибку компилято- ра в верхней части экрана и позиционирует курсор возле ошибки. Если последняя компиляция удачна, то сообщение не высвечивается.
494 Приложение С Команда Find error (поиск ошибки) — Alt+F8 находит местоположение ошибки времени выполнения. Когда происходит ошибка времени выполнения, ад- рес в памяти, где она произошла, дается в формате seg:ofs (сегмент:смещение). При возврате в IDE Турбо Паскаль автоматически определяет местонахождение ошибки. Эта команда позволяет вам найти ошибку снова, задавая значения seg и ofs. Чтобы Find error работала, нужно установить независимую кнопку Debugging в Integrated (в диалоговом окне Options/Debugger). Если ошибки времени выполне- ния произошли в программе, запущенной внутри IDE, то значения по умолча- нию для адреса ошибки устанавливаются автоматически. Это позволяет вам пере- местить ошибку после изменения файлов. (Если вы просто переместились в том же самом файле, можете вернуться назад к местоположению ошибки с помощью ко- манды Ctrl+Q+W.) Команда Find procedure (поиск процедуры) выводит диалоговое окно, позво- ляющее ввести имя процедуры для поиска. Эта команда доступна только во время сеанса отладки. Введите имя процедуры или выберите стрелку вниз для выбора имени из архивного списка. В противоположность команде Find, эта команда нахо- дит объявление процедуры, а не пример ее использования. Меню Run (выполнение) выбирается нажатием Alt+R. Команды меню за- пуска запускают вашу программу, а также начинают и заканчивают сеансы отлад- ки; После выбора этого пункта меню на экран выводится выпадающее меню. Run Ctrl+F9 Step over F8 Trace into F7 Go to cursor F4 Program reset Ctrl+F2 Parameters... Команда Run (выполнение) — Ctrl+F9 запускает вашу программу, используя параметры, которые вы передали в нее с помощью команды Run/Parameters. Если со времени последней компиляции исходный код был модифицирован, то встроенный менеджер проекта автоматически перекомпилирует и отредак- тирует вашу программу. • Если вы не хотите отлаживать свою програм- му, Можете компилировать и редактировать ее с от- ключенными независимыми кнопками Debugging (что даст вашей программе больше памяти для выполнения) в диалоговом окне Option/Debugger. Если вы ком- пилируете программу с этой независимой кнопкой, установленной „в Integrated, то результирующий выполнимый код • будет содержать отладочную информацию, которая повлияет на поведение команды Run/Run следующим образом: • Если вы не модифицировали исходный код со времени последней компиля- ции, команда Run/Run приведет к выполнению вашей программы до следующей точки прерывания или к выполнению до конца, если точки прерывания не были ус- тановлены. • Если со времени последней компиляции исходный код был модифицирован, а вы уже сделали несколько шагов по программе, используя команды Run/Step Over или Run/Trace into, команда Run/Run выдаст подсказку, хотите ли вы сделать
Меню интегрированной системы программирования 495 перекомпиляцию в своей программе. Если вы ответите ДА, менеджер проекта пе- рекомпилирует и отредактирует вашу программу и установит ее выполнение с на- чала. Если вы ответите НЕТ, то программа выполнится до следующей точки преры- вания или до конца, если точки прерывания не были установлены. • Если вы не находитесь в активном сеансе отладки, то встроенный менеджер проекта перекомпилирует программу и установит ее запуск с начала. Примечание. Если вы хотите использовать все возможности Турбо Паскаль, сделайте установку Debugging в Integrated. Нажатие Ctrl+Break приводит к прекращению выполнения и возвращению в интегрированную среду. Команда Program reset (сброс программы) — Ctrl+F2 прекращает текущий сеанс отладки, освобождает память, размещенную под вашу программу и закрыва- ет все открытые файлы, используемые программой. Команда Go to cursor (перейти на курсор) — F4 выполняет программу до строки, на которой стоит курсор в текущем окне редактора. Если курсор стоит на строке, которая не содержит выполнимое утверждение, команда выдаст предупре- ждение. Команда Run/Go to cursor может также начать сеанс отладки, команда Go to cursor не устанавливает постоянную точку прерывания, но позволяет программе останавливаться на постоянной точке прерывания, если она встретила эту точку прерывания перед строкой, на которой стоит курсор. Если это произошло, вы должны выбрать команду Goto cursor опять. Рекомендация. Используйте команду Go to cursor для того, чтобы перейти'на ту часть программы, которую вы хотите отлаживать. Если вы хотите остановить программу на опре- деленном утверждении каждый раз, когда она достигает этой точки, установите на этой строке точку прерывания. Команда Trace into (пошаговая трассировка) — F7 выполняет вашу програм- му утверждение за утверждением. Когда она достигает вызова процедуры, то вы- полняет каждое утверждение внутри процедуры, вместо выполнения процедуры как одного шага (см. Run/Step over). Если утверждение не содержит вызова проце- дур, доступных отладчику, .Trace into остановится на следующем выполнимом ут- верждении. Используйте команду Trace into для перемещения позиции выполне- ния к процедуре, вызываемой процедурой, которую вы сейчас отлаживаете. Если утверждение содержит вызов процедуры, доступной отладчику, Trace into останав- ливается в начале определения процедуры. Последующая команда Trace into или Step over выполняет утверждения в определении процедуры. Примечание. Trace Into команда распознает только процедуры, определенные в исход- ном файле, откомпилированном с двумя опциями, установленными в On: • в диалоговом окне Compiler Options (Options/Compiler), Debug Information независи- мая кнопка должна быть включена; • в диалоговом окне Debugger (Options/Debugger) независимую кнопку переключите в Integrated.
496 Приложение С Compile AIt+F9 Make F9 Build Команда Step over (шаг через) — F8 выполняет следующие утверждения в те- кущей процедуре. Она не выполняет трассировку внутрь вызовов процедур нижне- го уровня, даже если они доступны отладчику. Рекомендация. Используйте Step over для выполнения процедуры, которую вы сей- час отлаживаете, как одно утверждение за один момецт времени без входа внутрь других процедур. 1 Команда Parameters (параметры) позволяет вам передать выполняющейся программе параметры командной строки точно так же, как вы их набрали в ко- мандной строке DOS. Команды переназначения DOS будут игнорироваться. При выборе этой команды появится диалоговое окно с одним окном ввода. Примечание. Здесь нужно ввести только параметры, а не имя программы. Если вы уже завершили отладку и хотите изменить параметры, то можете выбрать Program reset для вы- полнения программы с новыми параметрами. Меню Compile (компиляция) выбирается нажатием Alt+C. Используется для того, чтобы сделать compile, make или build программы в активном окне. По- сле выбора этого пункта меню на экран выводится выпадающее меню. Команда Compile (компиляция) — Alt+F9 компи- лирует активный файл редактора. При этом на экран вы- водится окно статуса, показывающее результаты компи- ляции. Когда компиляция завершается, нажмите любую клавишу, чтобы удалить это окно. Если происходит ка- кая-нибудь ошибка или предупреждение, окно редакто- ра, содержащее исходный код с ошибкой, становится ак- тивным, появляется сообщение об ошибке, а курсор ус- танавливается на местоположении первой ошибки. Команда Маке (сборка) — F9 вызывает встроен- ный менеджер проекта для создания .ЕХЕ файла. При этом выполняется следующее. Если для Primary File было задано ийя, то компилируется этот файл; в против- ном случае компилируется файл в активном окне редактора. Турбо Паскаль проверяет все файлы, от которых зависит компилируемый файл. Если исходный файл для данного модуля был модифицирован со времени соз- дания файла .TPU (объектный код), то модуль перекомпилируется. Если для данного модуля был изменен интерфейс, то все другие модули, кото- рые зависят от него, перекомпилируются. Если модуль, отредактированный в файл .OBJ (внешние программы), и файл .OBJ новее, чем файл .TPU этого модуля, то модуль перекомпилируется. Если модуль включает файл Include, а файл Include новее, чем файл .ТРО это- го модуля, то этот модуль перекомпилируется. Destination Memory Primary file... Clear primary file Information...
Меню интегрированной системы программирования 497 Если исходный файл к модулю (файлу .TPU) не может быть установлен, то этот модуль не компилируется, но используется. Примечание. Compile/Make перестраивает только те файлы, которые не являются те- кущими и не находятся в активном окне редактора (или если задана Primary File). Compile/Make перекомпилирует только те файлы, которые не являются текущими (или ес- ли задана опция Primary File). Команда Build (полная сборка) перекомпилирует все файлы независимо от их даты. Эта команда подобна команде Compile/ Make за исключением того, что она не имеет условий. Команда Destination (назначение) дозволяет определить, будет ли выполняе- мый код храниться на диске (как файл .EXE) или он будет храниться в памяти (и, таким образом, теряться при выходе из Турбо Паскаль). Заметим, что, даже если Destination установлена в MeYnory (память), все модули, перекомпилированные во время Маке и Build, имеют свои обновленные файлы .TPU на диске. Если Destination установлена в Disk (диск), то создается файл .EXE, а его имя извлекается из одного или двух имен по следующему правилу: имя Primary File или, если оно не задано, имя файла в активном окне редактора. Примечание. Установка Destination в Disk увеличивает память, доступную в IDE для компиляции и отладки программ. Файлы .EXE и .TPU (если они есть) хранятся в том же справочнике, что и соответствующие исходные файлы, или в справочнике EXE & TPU Directory (Options/Directories), если он задан. Команда Primary file (основной файл) выбирается для того, чтобы задать файл .PAS, который будет компилироваться при использовании команд Compile/Make (F9) или Build (Alt+C+B). Можно применять эту опцию при работе над программой, которая включает несколько модулей и файлов Include. Не имеет значения, какой файл вы только что редактировали; Маке или Build всегда опери- руют с приоритетным файлом. Примечание. Если вы задаете другой файл приоритетным, а хотите откомпилировать файл в выбранном окне редактора, выберите Compile (Alt+F9). Команда Clear primary file отменяет выбор файла Primary, после чего коман- ды Compile и Build будут использовать файл в активном окне редактирования. Команда Information открывает окно, в котором выдается информация о по- следней скомпилированной программе, текущем состоянии памяти и окружения. Меню Debug (отладка) выбирается нажатием Alt+D. Команды меню отлад- ки управляют всеми свойствами интегрированного отладчика. После выбора этого пункта меню на экран выводится выпадающее меню. Команда Breakpoints (точки прерывания) открывает диалоговое окно, позво- ляющее управлять использованием безусловных точек прерывания. Оно показыва- ет все установленные точки прерывания, номера их строк и условия. Условие имеет архивный список, позволяющий выбрать условие точки прерывания, ис- пользованное ранее. 17—116
498 Приложение С Breakpoints... Callstack Ctrl+F3 Register Watch Output U ser screen Alt+F 5 Evaluate/modify... Ctrl+F4 Add watch... Ctrl+F7 Add breakpoint... Когда бы выполняющаяся программа ни встретила точку прерывания, она остановится на строке с точкой прерывания. Перед компиляцией исходного файла можно установить точку преры- вания на любой строке, даже на пустой строке или комментарии. При компиляции и выполнении файла Турбо Паскаль проверяет все установлен- ные точки прерывания и дает возможность уда- лить, игнорировать или изменить неправильные точки прерывания. При отладке файла Турбо Пас- каль знает, какие строки содержат выполнимые утверждения, и выдаст предупреждение в случае попытки установить неправильные точки преры- вания. Можно удалить точки прерывания из своей программы посредством выбора кнопки Delete (удалить). Можно также просмотреть исходный текст, где есть уста- новленные точки прерывания, посредством выбора кнопки View (просмотр). View передвигает курсор к выбранной точке прерывания в окне Edit (эта команда не вы- полняет Ваш код). Примечание. Это диалоговое окно не имеет кнопки Cancel, поэтому будьте особенно внимательны при редактировании и удалении. Выберите кнопку Edit для добавления новой точки прерывания. Команда Call stack — Ctrl+F3 открывает окно, в котором показана последова- тельность процедур, вызываемых исполняемой программой. В окне содержатся имена процедур и значения передаваемых им параметров. Процедура, выполняемая в текущий момент времени, находится в вершине сте- ка. Имя выполняемой программы находится в конце стека. Для показа текущей строки любой процедуры в окне подсветите имя процеду- ры и нажмите Enter. Окно остается на экране.в течение всего сеанса работы, если вы не закроете его специально. * Команда Register открывает окно, показывающее регистры CPU (центрально- го процессора), используемые обычно при отладке модулей на ассемблере. Верх- няя половина окна показывает содержимое регистров, а нижняя — содержимое восьми флагов. Интерпретация флагов следующая: Флаг Содержимое Флаг Содержимое C текущий флаг (сапу) Р паритета (parity) Z нулевой (zero) А вспомогательный (auxilary) S знаковый (sign) I прерывания (interrupt) 0 переполнения (overflow) D направления (direction)
Меню интегрированной системы программирования 499 Команда Watch открывает окно, в котором содержатся выражения и их изме- няющиеся значения. Элементы окна добавляются или убираются командой Add Watch. Команда Output открывает окно, в котором демонстрирует вывод на экран только в текстовом режиме. Команда User Screen — Alt+F5 обеспечивает полноэкранный выход на экран пользователя с возможностью наблюдения и текста и графики. Команда Evaluate/Modify (вычисление/модификация) — Ctrl+F4 вычисляет переменную или выражение, показывает ее значение, и, если это возможно, позво- ляет его изменить. Команда открывает диалоговое окно, содержащее три поля: Expression (выражение), Result (результат) и New Value (новое значение). Примечание. Кнопка Evaluate задана по умолчанию; если с помощью табуляции вы попадете в поле New Value, кнопка Modify будет задана по умолчанию. Поле Expression по- казывает выражение, по умолчанию состоящее из слова, на котором стоит курсор в окне ре- дактора. Можно вычислить выражение по умолчанию, нажав Enter, или отредактировать, или заменить его. Можно также нажимать стрелку вправо, чтобы расширить выражение по умолчанию путем копирования дополнительных символов из окна редактора. Если отладчик может вычислить выражение, он показывает его значение в поле Result. Если выражение относится к переменной или простому элементу данных, то можно подвес- ти курсор к полю New Value и ввести выражение как новое значение. Для закрытия диало- гового окна нажмите Esc. Используйте выражение повтора для просмотра значений последовательных элементов данных. Например, для массива целочисленного типа, названного Listlnt, ListInt[O],5 пока- жет пять последовательных целых в десятичном представлении, a ListInt[0],5x покажет пять последовательных целых чисел в шестнадцатеричном представлении. Команда Add watch (добавить выражение для просмотра) — Ctrl+F7 вставля- ет выражение просмотра в окно Watch. При выборе этой команды отладчик откры- вает диалоговое окно и выдает подсказку для ввода выражения просмотра. Выра- жением по умолчанию является слово, на Котором стоит курсор в текущем окне редактора. Имеется также архивный список, который можно применить для быст- рого ввода выражения, использованного ранее. Если окно Watch является активным, можно вставить новое выражение для просмотра посредством нажатия Ins. Команда Add breakpoint открывает диалоговое окно, в котором задаются па- раметры новой точки прерывания. В поле Condition вводится условие, по выполне- нии которого происходит прерывание. В поле Pass Count устанавливается число проходов контрольной точки, после выполнения которых произойдет останов. В поле File Name записывается полное путевое имя исходного файла, содержащего текущую контрольную точку. В поле Line Number показывается номер строки, со- держащей текущую точку прерывания. Можно ввести новое значение номера.
500 Приложение С Messages Go to next Go to previous Alt+F 8 Alt+F 7 Grep Shift+F2 Кнопка Modify предназначена для записи изменений, сделанных с помощью диалогового окна. Кнопка New служит для ввода информации для новой точки (ус- ловия, счетчик проходов, имя файла, номер строки). Меню Tools (сервисные средства) выбирается нажатием Alt+T. Данное ме- ню обеспечивает различные отладочные команды сообщения, следуемые за спи- ском по умолчанию программ и любых программ, установленных пользователем с помощью команды Options/Tools/Transfer. Команда Messages открывает окно, в котором отображается информация из программы, выдавае- мая посредством фильтра DOS (типа GREP). Для отслеживания строк программы необходимо вы- брать нужное сообщение и нажать клавишу Space. Чтобы отредактировать строку, на которую ссыла- ется сообщение укажите курсором нужное сообще- ние и нажмите Enter или дважды щелкните мышью. Команда Go to next — Alt+F8 позволяет перейти к следующему элементу спи- ска. Список содержит имена программ, которые можно запускать, не выходя из Турбо Паскаля. Такие программы называются трансферными. По окончании такой программы выполняется возврат в среду программирования. Команда Go to previous — Alt+F7 осуществляет переход к предыдущему эле- менту списка. Команда Grep — Shift+F2 запускает поисковую утилиту, которая может осу- ществлять просмотр текста в нескольких файлах одновременно. Например, если вы забыли, в, какой программе определена процедура SetUpModem, то можете ис- пользовать программу GREP для просмотра содержимого всех .PAS-файлов в ва- шем справочнике на наличие строки SetUPMyModem. Эта программа установлена в меню Option/Tools. Меню Options (опции) выбирается нажатием Alt+O. Оно содержит коман- ды, позволяющие посмотреть и изменить различные установки по умолчанию в Турбо Паскале. Большинство команд меню приводит к появлению диалогового ок- на. После выбора этого пункта меню на экран выводится выпадающее меню. Команда Compiler... (компилятор) выводит меню, которое предоставляет не- сколько опций для установки, влияющих на компиляцию кода. Назначение этих опций следующее: • Code Generation (генерация кода). Установки в этом окне определяют для компилятора методы создания объектного кода: • Force Far Calls позволяет всем процедурам и функциям использовать дальние модели вызова. Если эта опция отключена, то компилятор использует ближние мо-
Меню интегрированной системы программирования 501 Compiler... Memory sizes... Linker... Debugger... Directories... Tools... Environment > Open... Save TURBO.TP Sa ve as... дели вызова для всех процедур и функций внутри компи- лируемого файла. (Эта опция эквивалентна директиве ком- пилятора $F.) • Overlays Allowed делает возможной или невозмож- ной генерацию оверлейного кода. Турбо Паскаль разреша- ет делать оверлейным модуль только в том случае, если он был откомпилирован с включенной опцией Overlays Allowed. В этом состоянии генератор кода принимает осо- бые предосторожности при передаче строк и установлен- ных константных параметров из одной оверлейной проце- дуры или функции в другую. Включение независимой кнопки Overlays Allowed не заставляет вас делать модуль оверлейным. Она дает инст- рукции Турбо Паскалю сделать этот модуль оверлейным, если это требуется. Если вы работаете над модулем, который планируете ис- пользовать и как оверлейный, и как неоверлейный, то переключение Overlays Allowed обеспечит вам и то, и другое при использовании одного и того же модуля. (Эта опция эквивалентна директиве компилятора $О+). • Word Align Data (когда опция включена) устанавливает в Турбо Паскаль вы- равнивание несимвольных данных по четным адресам. Выравнивание по словам увеличивает скорость, с которой процессоры 8086 и 80286 вызывают и хранят дан- ные. Когда эта опция отключена, Турбо Паскаль использует побайтное выравнива- ние, где данные могут выравниваться по четным или нечетным адресам, в зависи- мости от того, какой следующий адрес является доступным. (Эта опция эквива- лентна директиве компилятора $А.) • 286 Instructions приводит к тому, что Турбо Паскаль генерирует код для на- бора инструкций 80286. Заметим, что программа, откомпилированная с включен- ной генерацией кода 80286, не проверяет присутствие 80286 во время выполне- ния. (Эта опция эквивалентна директиве компилятора $G.) • Группа кнопок Run-time Errors позволяет выбрать, какие ошибки времени вы- полнения будут генерироваться. Когда включена Range Checking, компилятор ге- нерирует код, который проверяет, чтобы массив и строка не выходили за границы, а присвоения переменным скалярного типа не превышали заданные диапазоны. Ес- ли проверка была неудачна, программа прерывается с ошибкой времени выполне- ния. (Эта опция эквивалентна директиве компилятора $R.) • При включении опции Stack Checking компилятор генерирует код, который проверяет перед каждым вызовом процедуры или функции, имеется ли доступное пространство для локальных переменных в стеке. Если проверка была неудачна, программа прерывается с ошибкой времени выполнения. (Эта опция эквивалентна директиве компилятора $S.)
502 Приложение С • При включении опции I/O Checking компилятор генерирует код, который проверяет наличие ошибок ввода-вывода после каждого вызова ввода-вывода. Если проверка была неудачна, то программа прерывается с ошибкой времени выполне- ния. Когда эта опция отключена, проверка ввода-вывода невозможна. Однако пользователь может тестировать ошибки ввода-вывода через системную функцию lOResult. (Эта опция эквивалентна директиве компилятора $1.) • Группа Syntax Options (синтаксические опции) позволяет выбрать тип син- таксических опций, по которым будет осуществляться поиск. • С включенной опцией Strict Var-String компилятор сравнивает объявленный тип строкового параметра типа var с типом действительно передаваемого парамет- ра. Если они не идентичны, то происходит ошибка компиляции. Если эта опция от- ключена, то такая проверка не производится. (Эта дЬция эквивалентна директиве компилятора $V.) • С включенной опцией Complete Boolean Evaluation всегда вычисляются все термы в булевских выражениях. Если эта опция отключена, компилятор генерирует код, прекращающий вычисление булевского выражения, как только это становится возможным. (Эта опция эквивалентна директиве компилятора $В.) • При включенной опции Extended Syntax синтаксис Турбо Паскаль расширя- ется так, что позволяет использовать вызовы функций, определенных пользовате- лем как утверждения, как если бы они были процедурами. Если эта опция от- ключена, расширенный синтаксис невозможен. (Эта опция эквивалентна директи- ве компилятора $Х.) • Опции Numeric Processing позволяют решить, как Турбо Паскаль будет обра- батывать числа с плавающей точкой. • Выберите 8087/80287 для генерации прямого inline-кода 8087 или 80287. (Эта опция эквивалентна директиве компилятора $N.) Выберите Emulation, если вы хотите, чтобы Турбо Паскаль проверил, имеет ли Ваш компьютер сопроцессор 80x87 (и использовал его, если это нужно). Если его нет, Турбо Паскаль эмулирует 80x87. (Эта опция эквивалентна директиве компилятора $Е.) Примечание. Эта опция игнорируется, если 8087/80287 недоступен. Опции в группе Debugging (отладка) можно установить так, чтобы включить или отключить отладочную информацию или генерацию локальных символов. • Включение Debug Information позволяет генерировать отладочную информа- цию, которая состоит из построчной таблицы для каждого утверждения Паскаля, отображающей адреса объектного кода в исходные текстовые числа. (Эта опция эквивалентна директиве компилятора $D.) Когда вы включили Debug Information для данной программы или модуля, IDE позволит вам устанавливать точки пре- рывания в этом модуле. Когда происходит ошибка времени выполнения в програм- ме или модуле, откомпилированном с включенной опцией Debug Information, Тур-
Меню интегрированной системы программирования 503 бо Паскаль автоматически установит курсор на утверждении, вызвавшем ошибку, с помощью Search/Find error. Примечание 1. Debug Information обычно используется вместе с командой Local Symbols. Для модулей отладочная информация записывается в файл .TPU вместе с объектным кодом модуля. Отладочная информация увеличивает размер файла .TPU и берет дополни- тельную память при компиляции программы, использующей этот модуль, но не влияет на размер или скорость выполняемой программы. Части исходного кода, откомпилированные и отредактированные с выключенной опци- ей Debug Information, недоступны отладчику. Если дискового пространства недостаточно, отключите опцию Debug Information, чтобы создавать меньшие файлы .TPU и использо- вать меньше памяти при компиляции и выполнении. Примечание 2. Чтобы использовать Турбо Debugger, SD должна быть включена. • Включенная опция Local Symbols делает возможной генерацию информа- ции о локальных символах, которая состоит из имен и типов всех локальных пере- менных и констант в модуле. (Эта опция эквивалентна директиве компилятора $L.) Когда вы включили Local Symbols для данной программы или модуля, IDE позво- ляет проверять и изменять локальные переменные модуля. Также вызовы проце- дур и функций модуля можно исследовать с помощью команды Window/Call Stack. Для модулей информация о локальных символах записывается в файл .TPU вме- сте с объектным кодом модуля. Информация о локальных символах увеличивает размер файла .TPU и берет дополнительную память при компиляции программы, использующей этот модуль, но не влияет на размер или скорость выполняемой про- граммы. Примечание. Опция Local Symbols игнорируется, если опция Debug Information отклю- чена. • Окно ввода Conditional Defines (условные определения) используется для то- го, чтобы ввести символы, на которые будут ссылаться директивы условной ком- пиляции. При этом можно разделять многочисленные условия точкой с запятой (;), например: TestCode; DebugCode. Команда Memory sizes (размеры памяти) позволяет определить потребности памяти по умолчанию для программы. Все три установки можно задать в своем ис- ходном коде, используя директиву компилятора $М. Если вы пытаетесь выпол- нить программу, но не хватает кучи для удовлетворения заданной потребности, программа прервется с ошибкой времени выполнения. (Эта опция эквивалентна ди- рективе компилятора $М.) В поле Stack Size задается размер (в байтах) сегмента стека. Размер по умолча- нию 16,384, максимальный размер — 65,520. В окне Low Heap Limit задается минимальный требуемый размер кучи (в бай- тах). По умолчанию минимальный размер равен 0 Кбайт.
504 Приложение С В окне High Heap Limit задается максимальный требуемый размер кучи (в бай- тах). По умолчанию максимальный размер равен 655,360, который (в большинстве систем) распределит всю доступную память в кучу. Это значение должно быть больше или равно наименьшему размеру кучи. Команда Linker (редактор связей) позволяет сделать несколько установок, влияющих на редактирование. Команда Linker открывает диалоговое окно, которое имеет несколько зависимых кнопок. Кнопка Map File (файл карты) используется для выбора типа создаваемого файла .МАР. Для установок, отличных от Off, файл .МАР помещается в справоч- ник EXE и TPU, заданный в диалоговом окне Options/Directories. Установкой по умолчанию для файла .МАР является Off. Опция Link Buffer (буфер редактора связей) приводит к тому, что Турбо Пас- каль использует Memory (память) или Disk (диск) для буфера редактора связей. При выборе зависимой кнопки Memory увеличивается скорость компиляции, но может не хватить памяти для больших программ. Выбор зависимой кнопки Disk ос- вобождает память, но замедляет компиляцию. Команда Debugger (отладчик) открывает диалоговое окно, чтобы сделать не- сколько установок, влияющих на интегрированный отладчик. Зависимые кнопки Debugging определяют, будет ли отладочная информация включена в выполнимый файл и как будет выполняться .ЕХЕ-файл под управлени- ем Турбо Паскаля: • Выбор Integrated (по умолчанию) позволяет отлаживать программы как в ин- тегрированном отладчике, так и в автономном отладчике Турбо Debugger. • Выбор Standalone дает возможность отлаживать программы только в Турбо Debugger. Опции Integrated, Standalone (Options/Debugger) и Map File (Options/Linker) соз- дают полную информацию и информацию о локальных символах для модуля толь- ко в том случае, если этот модуль был откомпилирован с включенными опциями Debug Information и Local ^Symbols соответственно. Зависимые кнопки Display Swapping позволяют установить, когда интегриро- ванному отладчику изменять окна просмотра во время выполнения программы. При отладке в режиме двойного монитора (т. е. с использованием опции /d в ко- мандной строке) вывод своей программы можно видеть на одном мониторе, а экран Турбо Паскаля — на другом. В этом случае Турбо Паскаль никогда не переключа- ет экран, и установка Display Swapping ни на что не влияет. Если установить Display Swapping в None, то отладчик не будет переключать экран вообще. Нужно использовать эту установку для части отладки кода, где со- вершенно определенно нет вывода на экран. При выполнении программы в режиме отладки с установкой по умолчанию от- ладчик Smart ищет в коде, который выполняется, места, где будет генерироваться
Меню интегрированной системы программирования 505 вывод на экран. Если код делает вывод на экран (или вызов процедуры), то проис- ходит переключение с экрана интегрированной усовершенствованной среды на эк- ран пользователя на время, достаточное для вывода, а затем экран переключается обратно. В противном случае переключения экрана не происходит. Примечание. Знайте, что при быстрой смене экрана: • он переключается при любом вызове процедуры, даже если эта процедура не делает вывода на экран; • в некоторых ситуациях экран IDE может быть модифицирован без переключения, на- пример если на экран пишет подпрограмма, вызывающая прерывание таймера; • при установлении кнопки Display Swapping в Always отладчик будет переключать эк- ран всякий раз при выполнении утверждения. Нужно выбрать эту опцию, если поверх экра- на интегрированной усовершенствованной среды будет писать ваша выполняющаяся про- грамма. Команда Directories (справочники) определяет в Турбо Паскале, где искать файлы, необходимые для компиляции, редактирования связей и файлы вывода. Она открывает диалоговое окно, содержащее четыре окна ввода. Используйте следую- щие директивы при введении имен справочников в этих окнах ввода: • нужно разделять многочисленные имена путей справочников (если это разре- шено) точкой с запятой (;). Можно использовать максимум 127 символов (включая пробелы). • пробелы перед точкой с запятой и после разрешаются, но не требуются; • разрешаются относительные и абсолютные имена путей, включая имена пу- тей, относящихся к фиксированной позиции на устройстве, отличном от текуще- го. Например: C:\PASCAL;C:\PASCAL\MYPROGS;A:\TURBO\EXAMPLES; Назначение каждого окна ввода: • ЕХЕ and TPU Directory задает каталог вывода для файлов .ЕХЕ или .TPU. Ес- ли ввода в этом окне не было, файлы будут храниться в справочнике, где находят- ся исходные файлы. Файлы .МАР также будут храниться здесь, если Map File (Options/Linker) установлена в значение, отличное от Off. • Include Directories задает каталог, содержащий стандартные включаемые фай- лы. Стандартные включаемые файлы — это файлы, заданные директивой компи- лятора {$1 filename}. Разрешается несколько имен справочников разделять точ- кой с запятой, как в команде DOS PATH. • • Unit Directories задает справочники, содержащие ваши файлы модулей Турбо Паскаля. При этом многочисленные справочники разделяются точкой с запятой (;), как в команде DOS PATH. • Object Directory используется для задания справочников, содержащих файлы .OBJ (подпрограммы ассемблерного языка). Когда Турбо Паскаль встречает ди-
506 Приложение С рекгиву {$L filename}, он ищет сначала в текущем справочнике, затем в справоч- никах, заданных здесь. Команда Tools открывает диалоговое окно, с помощью которого можно добав- лять новые или убирать программы из меню Tools. Поле Program titles содержит список для обзора, добавления или изъятия трансферных программ. Клавиша Edit позволяет изменить программу в списке и меню Tools. Клавиша New позволяет до- бавить новую программу. Клавиша Delete убирает программу из списка и из меню. Команда Environment (среда) дает возможность сделать установки для среды. Эта команда открывает меню; позволяющее выбрать установки из опций Preferences, Editor и Mouse. С помощью кнопок Screen Size можно установить, будет ли экран интегриро- ванной усовершенствованной среды иметь 25 строк или 43/50 строк. В зависимо- сти от типа видеоадаптера вашего PC будут доступны одна или две кнопки. При ус- тановке 25 строк (по. умолчанию) Турбо Паскаль использует 25 строк и 80 столб- цов. Для систем с монохромным дисплеем или CGA доступен только этот размер экрана. Если ваш персональный компьютер имеет EGA- или VGA-адаптер, можно использовать опцию 43/50 строк. Интегрированная усовершенствованная среда показывает 43 строки и 80 столбцов, если имеется EGA-адаптер, и 50 строк на 80 столбцов, если имеется VGA-адаптер. При пошаговом прохождении или установлении местоположения ошибки в ис- ходном коде интегрированная среда открывает новое окно, как только она встре- тит файл, который не был еще загружен. Выбор Current Window приводит к тому, что интегрированная среда заменяет содержание самого верхнего окна редактора новым файлом вместо открытия нового окна редактора. • Если Editor Files включена в опции Auto Save, и если файл был изменен со времени последнего сохранения, Турбо Паскаль автоматически сохраняет исход- ный файл, как только вы выбрали команду Run/Run (или любую другую команду отладки/выполнения) или File/DOS shell. Когда включена опция Environment, все установки, сделанные во время этого сеанса работы, автоматически сохранятся в файле конфигурации TURBO.TP при выходе из Турбо Паскаля. Когда включена опция Desktop, Турбо Паскаль определяет, будет ли ваша кон- фигурация (в файле TURBO.DSK) сохраняться при выходе и будет ли она восста- навливаться при возврате в Турбо Паскаль. Ваша информация о конфигурации не будет сохраняться, если файл .ТР не создан (автоматически или с помощью выбо- ра команды Options/Save Options), а зависимая кнопка Desktop Files не установлена в значение, отличное от None. Если вы хотите, чтобы IDE сохранила файл конфигурации окон и восстанавли- вала конфигурацию окон последнего сеанса программирования, выберите зависи- мую кнопку Current Directoiy или Config File Directory. Когда интегрированная среда сохраняет файл конфигурации TURBO.TP, она также создает TURBO.DSK,
Меню интегрированной системы программирования 507 содержащий информацию об окнах редактора, позицию всех окон, архивные спи- ски, положение точек прерывания и другую информацию. Все это можно сохра- нить и восстановить автоматически включением обеих опций Environment и Desktop в группе Auto Save. Альтернативно можно создать TURBO.TP, используя диалоговое окно Options/Save Options. Когда в следующий раз вы загрузите TURBO.EXE, он будет искать TURBO.TP и TURBO.DSK в текущем справочнике. Если они будут найдены, то они загрузятся, а конфигурация предыдущего сеанса работы и конфигурация окон восстановятся. Если TURBO.TP не будет найден в текущем справочнике, IDE будет искать его в справочнике, содержащем сам TURBO.EXE. При выборе Editor в меню Environment появляются опции, из которых можно осуществить выбрр. Диалоговое окно Editor Options имеет несколько независимых кнопок, управляющих обработкой текста в окнах редактора. При включенной кнопке Create Backup Files (по умолчанию) Турбо Паскаль ав- томатически создает копию исходного файла в окне редактора, если вы выбе- рете File/Save, и дает этой копии файла расширение .ВАК. При выключенной опции Insert Mode любой текст, набйраемый в окнах редак- тора, будет перекрывать существующий текст. Когда эта опция включена, наби- раемый текст вставляется со сдвигом вправо. Нажатие Ins включает Insert-режим при работе в окне редактора. Когда включена опция Autoindent Mode, нажатие Enter в окне редактора уста- навливает курсор под первый непустой символ в первой непустой строке. Это поможет сделать код вашей программы более читаемым. Когда включена опция Use Tab Character, Турбо Паскаль вставляет символ та- буляции (ASCII 9) при нажатии клавиши Tab. Если эта опция отключена, Турбо Паскаль заменяет табуляцию пробелами, число которых определено установкой Tab Size, описанной ниже. Чтобы поменять способ, которым табуляция изобража- ется в файле, просто измените значение размера табуляции на требуемый размер. Турбо Паскаль пересчитает все табуляции в этом файле с учетом того размера, ко- торый был выбран. Можно сохранить этот новый размер табуляции в файле кон- фигурации посредством выбора команды Options/Save Options. При включении опции Optimal Fill Турбо Паскаль начинает каждую строку аб- заца с минимально возможным количеством символов, используя табуляцию и пробелы по мере необходимости. Это приводит к созданию строк с меньшим коли- чеством символов, чем при отключенной опции Optimal Fill. Когда включена опция Backspace Unindents (по умолчанию), а курсор стоит на непустой строке или первом непустом символе строки, клавиша пробел выравнива- ет строку до уровня без отступов. При включенной опции Cursor Through Tabs, клавиши со стрелками передви- нут курсор к середине табуляции, в противном случае курсор перепрыгнет не- сколько столбцов, пока не установится под табуляцией.
508 Приложение С При включении опции Use Tab Character в этом диалоговом окне и нажатии Tab Турбо Паскаль вставляет символ табуляции в файл и передвигает курсор к сле- дующему знаку табуляции. Окно ввода Tab Size позволяет установить, сколько символов до следующего знака табуляции будет пропущено. Допустимыми явля- ются значения от 2 до 16; по умолчанию задано 8. При выборе Mouse из меню Environment показывается диалоговое окно Mouse Options, содержащее все установки для мыши. Зависимые кнопки Right Mouse Button определяют эффект нажатия правой кла- виши мыши (или левой клавиши, если включена опция Reverse Mouse Buttons). По умолчанию задается Topic Search. В окне Mouse Double Click можно изменить передвигающуюся отметку на по- лосе управления для настройки скорости двойной отметки мышью, используя кла- виши со стрелками. Перемещение отметки ближе к Fast означает, что Турбо Пас- каль требует более короткого времени между отметками для распознавания двой- ной отметки. Перемещение отметки ближе к Slow показывает, что Турбо Паскаль будет распознавать двойную отметку, даже если между отметками прошло доста- точно много времени. При включении кнопки Reverse Mouse Buttons активной кнопкой мыши стано- вится самая правая вместо самой левой (установка для левши). Заметим, однако, что кнопки на самом деле не переключатся до тех пор, пока не будет выбрана кноп- ка ОК. Выбор Startup из меню Environment позволяет выбрать установки для интегри- рованной среды. Все эти опции соответствуют вышеупомянутым опциям команд- ной строки. Изменения, которые вы делаете здесь, записываются прямо в TURBO.EXE и не действуют до тех пор, пока вы не загрузите в следующий раз IDE. Диалоговое окно Colors используется для настройки цветов IDE по своему вку- су. Список Group содержит имена различных областей IDE, которые можно на- строить. При выборе этой фуппы окно списка Item будет содержать имена различ- ных видов этой области. В цветной и черно-белой системах можно менять основ- ной и фоновый цвета, используя мышь или курсор для изменения палитры. Во всех системах текст в нижнем правом углу диалогового окна отражает текущие установ- ки. Изменение не действует на конфигурацию окон до тех пор, пока вы не закроете диалоговое окно (путем выбора ОК). Команда Open открывает диалоговое окно, с помощью которого можно оты- скать установки, сохраненные ранее командой Save в файле с расширением .ТР. В поле Option file name задается имя файла, в котором будут сохраняться уста- новки. По умолчанию это TURBO.TP. Список Files содержит набор файлов текущего каталога, соответствующих мас- ке, заданной в поле Option file name. На информационной панели высвечивается информация о полном имени, дате, времени и размерах выбранного файла.
Меню интегрированной системы программирования 509 Команда Save TURBO.TP.сохраняет установки, сделанные в диалоговых ок- нах Find и Replace (из меню Search), Destination и Primary File (из меню Compile) и все установки в меню Options. В данном случае они хранятся в файле TURBO.TP. Команда Save as открывает диалоговое окно, с помощью которого Турбо Пас- каль запоминает установки, сделанные в диалоговых окнах Find и Replace (из ме- ню Search), Destination и Primary File (из меню Compile) и все установки в меню Options. В поле Option file name задается имя файла, в котором будут сохраняться установки. Список Files содержит набор файлов текущего каталога, соответству- ющих маске, заданной в поле Option file name. Меню Window(oKHa) выбирается нажатием Alt+W. Оно содержит команды управления окном. Большинство из окон, которые вы откроете из этого меню, имеют все стандартные элементы окна скролинг» закрывающую кнопку и кнопки масштабирования, позволяющие посмотреть и изменить различные установки по умолчанию в Турбо Паскале. Первые девять окон пронумерованы. Для выбора ок- на по номеру задать Alt+N окна. После выбора этого пункта меню на экран выво- дится меню. _____________________ Расположение открытых окон определяют команды Tile Tile (черепица — неперекрывающееся расположение Cascade окон) и Cascade (каскад — расположение окон одно за Close all другим с просмотром только активного окна, а для дру- Refresh display гих окон видны только имена файлов и номера окон). Команда Close all закрывает все окна. - Size/Move Ctrl+F5 Команда Refresh display восстанавливает экран, Zoom F5 если программа его случайно испортила. Next F6 Команда Size/Move — Ctrl+F5 позволяет задать Previous Shift+F6 размер и позицию окна на экране. Close Alt+F3 Команда Zoom — F5 раскрывает активное окно во весь экран. Если окно уже расширено, то команда вое- List... Alt+0 станавливает его текущий размер. Команда Next — F6 активизирует следующее окно. Команда Previous — Shift+F6 активизирует предыдущее окно, т. е. окно, быв- шее активным перед текущим. Команда Close — Alt+F3 закрывает текущее окно. Команда List — Alt+О используется для получения списка всех открытых окон. Меню Help (помощь) выбирается нажатием Alt+H. Оно дает доступ к встро- енной справочной информации в специальном окне. Справочная информация име- ется по всем аспектам интегрированной среды Турбо Паскаль. (Также в строке статуса появляются подсказки для меню в одну строку и диалоговых окон, когда
510 Приложение С бы ни была выбрана команда.) После выбора этого пункта меню на экран выводит- ся выпадающее меню. Contents Index Shift+F 1 Topic search Ctrl+Fl Previous topic Alt+F 1 Using help Files... Compiler directives Reserved words Standard units Turbo Pascal Language Error messages About... Открыть окно помощи можно не только из ме- ню. • Для открытия окна Help выполните одно из следующих действий: • нажмите F1 в любой момент времени (нахо- дясь в любом диалоговом окне или при выборе любой команды меню); • когда окно редактора активно, а курсор стоит под словом, нажмите Ctrl+Fl для получе- ния справочной информации по языку; • отметьте кнопку Help, когда она появится в строке статуса или в диалоговом окне. Для закрытия окна Help нажмите Esc или за- крывающую кнопку либо выберите команду Window/Close. Можно держать окно Help в другом окне во время работы, если окно Help не было от- крыто из диалогового окна или не была нажата клавиша F1 при выборе команды меню. (Если вы нажмете клавишу F6 или кнопку мыши для другого окна, когда находитесь в Help, окно Help останется на экране.) Экраны Help часто содержат ключевые слова (высвеченный текст), которые можно выбрать для получения большей информации. Нажмите Tab для перехода к ключевому слову, нажмите Enter для получения более подробной справочной ин- формации. (Альтернативным способом является подведение курсора к высвечен- ному ключевому слову и нажатие Enter.) С помощью мыши можно дважды отметить любое ключевое слово для откры- тия справочного текста по этому элементу. Примечание. При получении справочной информации в диалоговом окне или меню нельзя изменять размер окна или копировать в карман. В этом случае Tab используется для управления диалоговым окном, а не для перехода к ключевому слову. Вы можете нажать Ctrl+Fl на любом слове для получения справочной инфор- мации. Если слово не найдено, выполняется поиск вперед по оглавлению и пока- зывается ближайший соответствующий текст. Когда окно Help активно, можно копировать из окна и вставлять этот текст в окно редактора. Выберите сначала текст (используя Shift плюс стрелку вправо, стрелку влево, стрелку вверх, стрелку вниз), выберите Edit/Сору, перейдите в ок- но Edit и выберите команду Edit/Paste. ' Можно также копировать заранее выбранные примеры программ из экрана подсказки посредством выбора команды Edit/Сору Example.
Меню интегрированной системы программирования 511 Команда Contents (содержание) открывает окно Help с основной таблицей со- держания. Из этого окна можно перейти к любой другой части системы справоч- ной информации. Можно получить подсказку по справочной информации посредством нажатия F1, когда окно Help активно. Можно также получить этот экран, отметив строку статуса. Команда Index (оглавление) — Shift+Fl открывает диалоговое окно, показы- вающее полный список ключевых слов справочной информации (специально вы- свеченный текст на экране справочной информации, позволяющий быстро пе- редвигаться к соответствующему экрану); Можно сделать скролинг списка или поиск вперед по этому списку посредст- вом нажатия букв на клавиатуре. Например, для того чтобы посмотреть наличие информации по "printing", можно набрать р г i. Когда вы наберете р, курсор прыг- нет на первое ключевое слово, начинающееся на р. Когда наберете г, курсор прыг- нет на первое ключевое слово, начинающееся на рг. Когда наберете i, курсор прыг- нет на первое ключевое слово, начинающееся на pri, и т. д.. Когда вы найдете интересующее вас ключевое слово, выберите его, установив под ним курсор и нажав Enter. (Можно также отметить дважды клавишу мыши.) Команда Topic search (поиск раздела) — Ctrl+Fl показывает справочную ин- формацию по языку и по текущему выбранному элементу. Чтобы получить справочную информацию по языку, установите курсор под элементом в окне редактора и выберите Topic Search. Можно получить справочную информацию по таким элементам, как имена процедур (Writeln, например), зарезервированные слова и т. д. Если справочной ин- формации по этому элементу нет, то оглавление справочной информации покажет ближайший соответствующий элемент. Команда Previous topic (предыдущий раздел) — Alt+Fl открывает окно Help и. вновь показывает текст, который вы просматривали последний раз. Турбо Паскаль позволяет просмотреть 20 предыдущих экранов подсказки. Можно также отме- тить строку статуса для просмотра последнего экрана справочной информации: Команда Using help (подсказка по справочной информации) открывает тексто- вый экран, поясняющий, как пользоваться системой справочной информации. Ес- ли вы уже находитесь в системе справочной информации, можно вызвать этот эк- ран нажатием F1. Команда Files открывает диалоговое окно Install Help Files. Команда Compiler directives открывает окно Help и выдает список директив компилятора, через которые можно выйти на соответствующий текст подсказки, относящийся к слову, помеченному курсором в списке.
512 Приложение С Команда Reserved words открывает окно Help и выдает список слов, заре- зервированных для внутреннего использования в языке. Пометив курсором соот- ветствующее слово, можно вызвать поясняющий текст. Команда Standard units открывает окно Help и выдает список стандартных модулей Турбо Паскаль. Выделив соответствующий модуль, можно вызвать под- сказку по размещенным в нем процедурам и функциям. Команда Turbo Pascal Language открывает окно Help и выдает список эле- ментов языка, через который можно выйти на соответствующий текст подсказки, относящийся к слову, помеченному курсором в списке. Команда Error messages открывает окно Help и выдает информацию об ошиб- ках, обнаруженных системой. Команда About (о) выводит диалоговое окно, которое покажет информацию о версии Турбо Паскаль. Для закрытия этого окна нажмите Esc или пробел либо от- метьте кнопку ОК (или нажмите Enter). Локальные меню. Находясь в любом из окон среды программирования, мож- но получить дополнительный сервис через локальное меню, вход в которое реали- зуется нажатием Alt+Fl0 либо правым щелчком мыши в окне или титульной рам- ки. Интегрированная среда программирования Турбо Паскаль 7.0 предоставляет следующий набор локальных меню: Browse, Edit, Help, Message, Wath. Меню Browse открывается в окне просмотра и содержит следующие опции: Browse — позволяет усмотреть символ, отмеченный курсором. Previous — высвечивает предыдущий просмотренный объект. Goto sourse — помещает курсор в выбранную строку исходного текста. Track sourse — воздействует на ObjectBrowser, окно Message и отладчик. Если строка, на которую указано при просмотре, отсутствует в редакционном окне, то IDE открывает его и подсвечивает соответствующее место. Options — открывает диалоговое окно, установки которого действуют только на то локальное окно просмотра, из Которого открыто локальное меню. Edit — открывается в окне просмотра или на титульной рамке и предоставляет набор команд для редактирования, оперативной подсказки и отладки. Help — открывается в окне Help и предоставляет услуги в виде опций меню, определяющих способ выдачи подсказки. Message — открывается в окне Message и выдает набор команд для управления точками наблюдения. Wath — открывается в окне Wath и предоставляет набор команд для редакти- рования выражений в окне просмотра, их блокирования и разблокирования.
Г 1 Приложение D. СТРУКТУРА ПАМЯТИ j \ ТУРБО ПАСКАЛЬ-ПРОГРАММЫ г ' В данном приложении рассматривается внутренняя структура программы на ^Т)1рбо Паскале, назначение некоторых регистров центрального процессора и сис- 5темных переменных, поддерживающих эту структуру. Знание порядка распределения памяти в программе поможет вам лучше понять принципы ее выполнения и осмысленно использовать соответствующие значения для своих целей. Ниже, на рисунке D. 1, представлена общая схема ехе-файла, полученного по- меле компиляции некоторой программы на Паскале. Как видно из рисунка, в начале любой программы, выполняемой в среде MS ' DOS, операционная система формирует префикс сегмента программы (PSP —Pro- gram, Segment Prefix) — область размером 256 байт, содержащую ряд характери- стик программы, используемых MS DOS при загрузке программы в оперативную память и выполнении, в частности, информацию о размере, адрес копии окружения и параметры вызова. Сегментный адрес PSP а, значит, адрес начала программы содержится в сис- t темной переменной PrefixSeg модуля System. Адрес программы при размещении в памяти всегда начинается с границы параграфа и занимает целое число параграфов. Примечание. Параграфом называют непрерывный участок памяти размером 16 байт. Далее располагаются компоненты кода, загруженные непосредственно из ехе- файла программы. Структура этой части соответствует структуре исходного текста 'Турбо Паскаль-программы: каждый модуль образует свой кодовый сегмент. Глав- ная программа занимает первый от младших адресов кодовый сегмент; далее сле- дуют кодовые сегменты модулей в порядке, обратном тому, как они следовали в разделе описания uses. Последний кодовый сегмент занимает образ модуля System, который подключается к каждой программе. Размер одного кодового сегмента не может превышать 64 Кбайт, но общий размер кода ограничен только имеющейся оперативной памятью. ‘ После сегмента модуля System располагаются значения типизированных кон- стант, собранные компилятором из всех описаний программы независимо от их уровня вложенности. Область памяти выше типизированных констант отводится для хранения глобальных переменных (описанных во внешнем блоке). Таким обра- зом, видно, что память под эти объекты отводится статически, на основе подсчета компилятором суммарного объема, необходимого для хранения типизированных констант и глобальных переменных. Размер сегмента данных программы не может превышать 64 Кбайт. Он адресуется посредством регистра DS (Data Segment); зна- чение его во время выполнения программы не изменяется. Содержимое этого реги- стра может быть получено с помощью стандартной функции Dseg (без параметров).
514 Приложение^ PrefixSeg Верхняя граница памяти DOS HeapEnd HeapPtr HeapOrg SsegrSPtr Sseg.0000 Dseg:0000 ► Неиспользуемая (свободная часть кучи) — — Куча (растет вверх 4) Оверлейный буфер vjvmcapcnu Стек (растет вниз +) * OvrHeapEnd Незанятая часть стека Глобальные переменные Типизированные константы ' Кодовый сегмент модуля System Кодовый сегмент первого модуля Образ ЕХЕ-файла в оперативной Кодовые сегменты других модулей - nai пяти Кодовый сегмент последнего модуля Кодовый сегмент главной программы ► Префикс сегмента программы (PSP) "Ж Нижняя граница памяти DOS Рис. D.l. Общая схема распределения памяти после компиляции некоторой программы на Турбо Паскале Вслед за сегментом данных в памяти располагается сегмент стека, используе- мый для управления локальными переменными подпрограмм. Стековый механизм поддерживается аппаратурой центрального процессора: сегмент стека адресуется специальным регистром SS (Stack Segment), значение которого также не изменяет- ся во время выполнения программы и может быть получено обращением к стан- дартной функции Sseg. -Другой регистр процессора SP (Stack Pointer — указатель стека) всегда показывает адрес начала свободной части сегмента стека (вершину стека). В процессе вызова подпрограмм и выходов из них значение регистра SP ме- няется (вершина "передвигается в пределах сегмента стека"). Текущее значение ре- гистра SP можно получить обращением к стандартной функции Sptr (смещение SP относительно SS). Примечание. Стек всегда начинается ("растет") с конца области памяти, выделенной под сегмент стека, к его началу, адресуемому SP.
Структура памяти Турбо Паскаль-программы Размером памяти, выделяемым для стека, можно управлять директивой компи- л 1тора $М. Этот размер не может превышать 64 Кбайт; размер по умолчанию — 16 Е^байт, минимальный размер — 1 Кбайт. Для просмотра содержимого регистров процессора можно воспользоваться ко- мандой Register пункта Debug главного меню интегрированной среды программи- рования Турбо Паскаль, как было описано в приложении С. Для просмотра содержимого стека примените команду Call stack пункта Debug главного меню интегрированной среды программирования Турбо Паскаль. В рас- крывающемся при этом окне будет показана последовательность процедур, вызы- ваемых исполняемой программой, а также имена процедур и значения передавае- мых им параметров. Выше стека может находиться буфер оверлеев, который предназначен для обеспечения выполнения оверлейных структур. Он используется для временного размещения модулей из оверлейного файла программы; его размер по умолчанию соответствует размеру наибольшего оверлейного модуля в программе. Если в про- грамме нет оверлеев, размер буфера оверлеев равен 0 (т. е. буфер отсутствует). Ор- ганизация буфера оверлеев и работа с ним обеспечивается средствами системного модуля Overlay. Размер буфера определяется динамически, в процессе выполнения программы, с помощью собственяых средств модуля Overlay (процедуры OvrSet- Buf)- Выделение памяти под оверлейный буфер производится за счет динамиче- ской памяти, при этом размер кучи соответственно уменьшается. Сегментные адре- са начала и конца буфера оверлеев определяются типизированными константами модуля System: OvrHeapOrg и OvrHeapEnd. Оставшаяся часть памяти, выделяемой программе, используется для динамиче- ской памяти, которая хранит переменные, образуемые вызовами стандартных про- цедур New и GetMem. Размер кучи определяется минимально допустимыми и мак- симальными значениями, устанавливаемыми директивой компилятора $М. Факти- ческий размер динамической памяти зависит также от реального-объема памяти перед запуском программы. Размер кучи может быть от 0 байт до 640 Кбайт. Куча организована по стековому принципу и используется, начиная от нижних адресов. Сегментный адрес нижней границы — "начала" кучи хранится в систем- ной переменной HeapOrg, а ее "вершина" — нижняя граница свободной памяти со- держится в переменной HeapPtr. При каждом выделении памяти для динамической переменной указатель HeapPtr передвигается вверх на размер этой переменной. Верхняя граница кучи (и всей программы) всегда находится по адресу, храня- щемуся в системной переменной HeapEnd. Наглядное представление о распределении памяти Турбо Паскаль-программы можно получить с помощью записанной ниже программы. program Dem_Sxze;{Программа, демонстрирующая распределение памяти} uses Crt; var
516 Приложение D Р : pointer; I : word; procedure Prog_Size; var SysemTotalSize : word; {Общий размер кучи} PrefixSize : word; {Размер PSP} CodeSize : word; {Размер сегмента кода} DataSize : word; {Размер сегмента данных} HeapSize : word; {Размер динамической памяти} AllocHeapSize : word; {Размер занятой части динамической памяти} Factor : real; S : string[80]; L : byte absolute S; Ы : byte; function Lin_Adr (P : pointer) : longint; {Вычисление абсолют- ного (линейного) адреса объекта по обычному сегментному адресу} begin Lin_Adr:=longint(Seg(РЛ))*16+Ofs(Рл) end; begin {Начало процедуры} SystemTotalSize := 640*1024 div 16; PrefixSize := 256 div 16; CodeSize := Dseg — PrefixSeg — PrefixSize; DataSize := Seg(HeapOrg*) — SSeg; HeapSize := MemW[PrefixSeg:2] - Seg (HeapOrg*); AllocHeapSize := (Lin_Adr(HeapPtr)-Lin_Adr(HeapOrg)+1) div 16; Writein (’ Распределение памяти: ') ; Factor := 67 / SystemTotalSize; L:= Round(Factor*PrefixSize) ; FillChar(S[1],L,#176) ;* Writein (’PSP PrefixSize:5,' ', S); L := Round(Factor*CodeSize); FillChar(S[l],L,#176); Writeln(’Код ',CodeSize:5,' ' ,S) ; L := Round(Factor*Data$ize); FillChar(S[1],L,#176); Writein ('Данные ',DataSize:5,' ',S); L := Round(Factor*HeapSize); Ll := Round(Factor*AllocHeapSize) ; FillChar(S[l],LI,#176); FillChar(S[Ll+1],L-L1,#219) ; Writeln('Куча: ',HeapSize:5,' ',S) ;
Структура памяти Турбо Паскаль-программы 517 Writein end; begin {Основная программа} Randomize; for I:=l to 100 do {Попробуйте поменять конечное значение пара- метра I (от 1 до 1000)} GetMem(P,Random(1000)); {Занимаем часть динамической памяти бло- ками случайных размеров} Prog_Size; {Выводим на экран карту памяти} end. ь
ОГЛАВЛЕНИЕ Предисловие........................................................................ 3 Часть I. КОМПЬЮТЕР И ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ Глава 1. Компьютер - алгоритм - программа.............................,............ 5 1.1. Структура персонального компьютера......................................... 5 1.1.1. Процессор............................................................ 6 1.1.2. Память............................................................... 7 1.1.3. Клавиатура........................................................... 8 1.1.4. Видеомонитор ...................................................... 12 1.1.5. Дополнительные устройства компьютера.....4.......................... 13 1.2. Программное обеспечение персонального компьютера.......................... 14 1.2.1. Системное программное обеспечение................................... 15 1.2.2. Прикладное программное обеспечение.................................. 15 1.2.3. Инструментальные средства........................................... 15 1.3. Основные этапы решения задач на компьютере................................ 15 1.4. Как вызвать программу?.................................................... 18 Контрольные вопросы и задания.................................................. 18 Глава 2. Интегрированная среда программирования Турбо Паскаль7.0.................. 21 2.1. Языки программирования.................................................. 21 2.1.1. Язык программирования Паскаль....................................... 22 2.1.2. Трансляторы.......................................................... 23 2.2. Использование среды программирования Турбо Паскаль ....................... 24 2.2.1. Основные файлы пакета Турбо Паскаль ................................ 25 2.2.2. Запуск интегрированной среды программирования ...................... 26 2.2.3. Использование клавиатуры для выбора команды меню.................... 27 2.2.4. Использование “мыши” для выбора команд меню......................... 27 2.2.5. Быстрые способы выбора команд меню.................................. 27 2.2.6. Окна Турбо Паскаля.................................................. 28 2.2.7. Справочная система Турбо Паскаля.................................... 32 2.2.8. Редактор интегрированной среды...................................... 32 2.2.9. Ввод текста программы в окне редактора.............................. 35 2.2.10. Компиляция программы............................................... 36 2.2.11. Создание .ехе-файла................................................ 36 2.2.12. Исполнение программы............................................... 37 2.2.13. Просмотр выполнения программы на экране пользователя............... 37 2.2.14. Сохранение программы на диске...................................... 38 2.2.15. Завершение работы в среде программирования......................... 40 2.2.16. Открытие файла текста программы.,.................................. 40 2.2.17. Получение справочной информации по редактору....................... 41 2.2.18. Ошибки, обнаруженные при компиляции................................ 42 Контрольные вопросы и задания.................................................. 44
Оглавление 521 Часть II. НАЧАЛА ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ ТУРБО ПАСКАЛЬ Глава 3. Основные элементы языка Паскаль ........................................ 45 3.1. Основные элементы программирования........................................ 45 3.2. Алфавит и словарь языка Паскаль........................................... 46 3.2.1. Слова в Паскале ................................................... 47 3.2.2. Формальные методы описания синтаксических конструкций языка программирования .... 49 3.2.3. Идентификаторы..................................................... 49 3.2.4. Константы и переменные............................................. 51 3.3. Структура программы..................................................... 54 3.3.1. Раздел uses........................................................ 57 3.3.2. Раздел описания меток.............................................. 58 3.3.3. Раздел описания констант........................................... 58 3.3.4. Раздел описания типов данных....................................... 59 3.3.5. Раздел описания переменных ...................................... 59 3.3.6. Раздел описания процедур и функций ................................ 60 3.3.7. Раздел операторов.................................................. 60 3.3.8. Комментарии.................................................... 61 3.3.9. Директивы компилятора и управляющие символы ....................... 62 3.3.10. Библиотечные модули пользователя.................................. 63 3.4. Рекомендации по стилю программирования.....................................64 Контрольные вопросы и задания.................................................. 65 Глава 4. Типы данных ............................................................ 69 4.1. Общие сведения............................................................ 69 4.2. Перечень типов данных в Турбо Паскале..................................... 70 4.3. Скалярные типы данных..................................................... 71 4.3.1. Целочисленные типы данных.......................................... 72 4.3.2. Вещественные типы данных.......................................... 73 4.3.3. Литерный (символьный) тип......................................,... 75 4.3.4. Булевский тип...................................................... 76 4.3.5. Пользовательские типы .?........................................... 76 4.3.6. Перечисляемый тип.................................................. 76 4.3.7. Интервальный тип (диапазон)........................................ 78 4.4. Структурированные типы данных............................................. 79 4.5. Тождественность и совмесЛмость типов ..................................... 79 4.6. Выражения, операции, операнды............................................. 81 4.6.1 .Арифметические выражения и операции................................ 81 4.6.2. Выражения и операции отношения..................................... 85 4.6.3. Логические выражения и операции.................................... 85 4.6.4. Приоритет операций............................................... 87 Контрольные вопросы и задания................................................. 88 Глава 5. Ввод - вывод данных..................................................... 91 ' 5.1. Общие сведения............................................................ 91 5.2. Процедуры ввода-вывода..:............................................... 91 5.2.1. Процедура чтения Read............................................. 91 5.2.2. Процедура записи Write ............................................ 93 5.3. Форматы вывода............................................................ 93 Конт рольные вопросы и задания..........-.................................... 97
522 Оглавление Глава 6. Операторы ............................................................ 99 6.1. Общие сведения ........................................................... 99 6.2. Простые операторы ................................................... :.. 99 6.2.1. Оператор присваивания.............................................. 99 6.2.2. Оператор безусловного перехода (go to)............................. 100 6.2.3. Оператор вызова процедуры ........................:.................. 100 6.2.4. Пустой оператор.................................................... 101 6.3. Структурные операторы.................................................. 101 6.3.1. Составной оператор................................................ 101 6.3.2. Условные операторы................................................ 101 6.3.3. Получение подсказки по языку программирования...................... 103 6.3.4. Тестирование и отладка программ.................................... 105 6.3.5. Операторы повтора.................................................. 113 ’6.3.6. Вложенные операторы цикла...........;............................ 122 6.4. Правила Пунктуации....................................................... 124 Контрольные вопросы и задания................................................. 125 Глава 7. Процедуры и функции.................................................... 130 7.1. Необходимость структуризации в программировании.......................... 130 7.2. Подпрограммы в языке Паскаль............................................. 132 7.2.1. Стандартные библиотечные модули.................................. 133 7.2.2. Встроенные функции и процедуры..................................... 134 7.3. Процедуры и функции пользователя......................................... 136 7.3.1. Процедуры.......................................................... 137 7.3.2. Функции............................................................ 141 7.3.3. Механизм передачи параметров ...................................... 143 7.3.4. Область действия параметров....................................... 149 7.3.5. Рекурсии........................................................... 152 7.3.6. Нетрадиционное использование подпрограмм.......................... 154 Контрольные вопросы и задания............................................ /... 157 Часть IIL СТРУКТУРИРОВАННЫЕ ТИПЫ ДАННЫХ......................................... 162 Глава 8. Строки ................................................................ 163 8.1. Описание строкового типа................................................ 163 8.2. Строковые выражения..................................................... 164 8.3. Строковые процедуры и функции .......................................... 166 8.3.1. Упражнения........................................................ 168 Контрольные вопросы и задания................................................. 178 Глава 9. Массивы...............................................:................ 181 9.1. Описание типа массив.......................>............................ 181 9.1.1. Действия над массивами........................................... Г84 9.1.2. Действия над элементами массива.................................... 184 9.2. Сортировка массивов.................................................... 195. 9.2.1. Линейная сортировка (сортировка отбором)......................... 195 9.2.2. Сортировка методом пузырька..................................‘..... 197 9.2.3. Метод быстрой сортировки с разделением............................. 199 9.3. Бинарный поиск в упорядоченных массивах................................. 202 9.4. Транспонирование матрицы .............................................. 210 Контрольные вопросы и задания................................................ 214
524 Оглавление Глава 14. Управление экраном и звуком компьютера 14.2. Управление звуком.........................................................358 14.2.1. Общие сведения......................................................358 14.2.2. Генерация мелодий ..................................................359 14.2.3. Звуковое сопровождение процесса вывода..............................360 Контрольные вопросы и задания.........................л.........................364 Глава 15. Графика в Турбо Паскале .}...............................................367 15.1. Аппаратная и программная поддержка графики................................367 15.1.1. Адаптер и монитор...................................................367 15.1.2. Видеобуфер......................................................... 368 15.1.3. Видеостраницы...................................................... 369 15.1.4. Драйверы............................................................370 15.1.5. Состав графических средств......................................... 371 15.2. Модуль Graph..............................................•.............. 371 15.3. Инициализация графики.....................................................374 15.3.1. Инициализация видеорежима ..........................................374 15.3.2. Закрытие видеорежима............................................ 376 15.3.3. Переключение текст - графика - текст................................376 15.3.4. Обработка ошибок.................................................. 377 15.4. Базовые процедуры и функции...............................................379 15.4.1. Система координат'..................................................379 15.4.2. Экран и окно в графическом режиме................................. 380 15.4.3. Вывод точки.........................................................382 15.4.4. Вывод линии.........................................................383 15.5. Работа с текстом..........................................................386 15.5.1. Вывод текста........................................................386 15.5.2. Вывод численных значений ...........................................386 15.5.3. Шрифты .............................................................387 15.5.4. Выравнивание текста.................................................390 15.6. Установка цвета и палитры................................................ 391 15.7. Построение графических фигур..............................................396 15.7.1. Построение прямоугольников....................................... 396 15.7.2. Построение многоугольников..........................................397 15.7.3. Построение дуг и окружностей........................................399 15.7.4. Атрибуты графических фигур..........................................401 Упражнения......................................................................408 Контрольные вопросы и задания...................................................418 Часть V. ВВЕДЕНИЕ В ПРОГРАММИРОВАНИЕ НА ТУРБО ПАСКАЛЕ В СРЕДЕ WINDOWS Глава 16. Введение в объектно-ориентированное программирование.....................423 16.1. Идея объектно-ориентированного программирования...........................423 16.1.1 .Что такое объекты?..................................................424 16.1.2. Наследование записей................................................426 16.2. Операции и методы.........................................................428 16.2.1 Методы. Инициализация полей объектов.................................429
Оглавление 525 16.2.2. Поля данных объектов и формальные параметры методов.................432 16.2.3. Сокрытие данных в объектах..........................................433 16.2.4. Инкапсуляция.................................................... 434 16.2.5. Оптимизация представления методов...................................435 16.2.6. Совместимость объектных типов.......................................441 16.2.7. Виртуальные методы. Конструктор.....................................442 16.2.8. Динамические объекты.i..............................................446 16.2.9. Деструкторы.........................................................450 16.2.10. Динамические методы................................................453 16.3. Внутреннее представление объектов в Турбо Паскале.........................453 Контрольные вопросы и задания...................................................459 Глава 17. Простое приложение Windows.............................................. 461 17.1. Что представляет Ообой приложение Windows.................................461 17.1.1. Особенности и преимущества Windows..................................462 17.1.2. Ресурсы.............................................................464 17.1.3. Типы данных в Windows........................................... 465 17.1.4. Обьектно-ориёнтированное использование окон....................... 465 17.1.5. Лучший интерфейс для Windows...................................... 466 17.1.6. Абстрагирование функций Windows.....................................466 17.1.7. Регулятор окна.................................................... 467 17.1.8. Автоматизация реакции системы в виде сообщений......................467 17.1.9. Элементы интерфейса пользователя .................................. 468 17.2. Архитектура Windows-программы.............................................472 17.3. Венгерская нотация......................................:.................473 17.4. Ссылки.................................................................. 475 17.5. Простое приложение Windows................................................476 17.6. Простейшая программа, использующая ObjectWindows..........................479 Контрольные вопросы и задания...................................................481 Приложения ....................................................................... 482 Приложение А. Таблица ASCII ................................................... 482 Приложение В. Управление клавиатурой........................................... 483 Приложение С. Меню интегрированной системы программирования Турбо Паскаль 7.0...488 Приложение D. Структура памяти Турбо Паскаль-программы......................... 513 ' Литература ..................................................................... 518