Text
                    0. Л. Голицына,
И. И.Попов
ОСНОВЫ АЛГОРИТМИЗАЦИИ
И ПРОГРАММИРОВАНИЯ
3-е издание, исправленное и дополненное
Допущено Министерством образования Российской Федерации
в качестве учебного пособия для студентов учреждений
среднего профессионального образования, обучающихся
по специальности «Информатика и вычислительная техника»
ддрум 2008

УДК 004.9(075) ББК 32.973я723 Г 60 Рецензенты: кандидат технических наук, профессор, зав. кафедрой проектирования автоматизированных информационных систем РЭА им. Г.В. Плеханова В.П. Романов', кандидат технических наук, доцент кафедры автоматизированных информационных систем РГГУ Г.Ю. Максимович Голицына О.Л., Попов И.И. Г 60 Основы алгоритмизации и программирования: учеб, пособие. — 3-е изд., испр. и доп. — М: ФОРУМ, 2008. — 432 с. — (Про- фессиональное образование). ISBN 978-5-91134-214-2 Учебное пособие написано в соответствии с государственным обра- зовательным стандартом. В нем рассмотрены основные понятия алго- ритмизации и программирования, представлены все виды задач обработ- ки данных, приводятся таблицы сравнительного анализа форматов, опе- раторов, процедур, описания данных для различных языков программирования. Дается развернутое и практически полное описание языков и систем программирования Pascal, Basic, С; а также сред Visual Basic и Delphi. Все разделы насыщены примерами и задачами. Пособие предназначено для учащихся техникумов, колледжей, мо- жет быть рекомендовано студентам вузов. УДК 004.9(075) ББК 32.973я723 © Голицына О.Л., Попов И.И., 2002, 2007, 2008 © Издательство «Форум», 2002, 2007, 2008 ISBN 978-5-91134-214-2
Предисловие Вплоть до XVII в. деятельность общества в целом и каждого чело- века в отдельности была направлена на овладение веществом, т. е. по- знание свойств вещества и изготовление сначала примитивных, а потом все более сложных орудий труда, вплоть до механизмов и машин, по- зволяющих изготовлять потребительские ценности. Затем в процессе становления индустриального общества на пер- вый план вышла проблема овладения энергией — сначала тепловой, затем электрической, наконец, атомной. Овладение энергией позволило освоить массовое производство потребительских ценностей и, как след- ствие, повысить уровень жизни людей и изменить характер их труда. В то же время человечество стремилось познать тайны мироздания, составляя его модели, выделяя общие закономерности, пытаясь увидеть некоторое единство в разнообразии материальных объектов. Одним из первых обобщений нарождающейся науки стало понятие вещества. Эта идея возникла в философии древней Греции и развилась до современ- ных квантовых и волновых теорий вещества. Казалось, что все в мире можно объяснить, описав совокупность взаимодействующих матери- альных частиц. Следующим принципиальным понятием стала энергия. Его появление было связано с развитием техники, созданием двигате- лей, технических преобразователей энергии. Физические, химические, биологические процессы стали рассматриваться с позиции передачи и преобразования энергии. Однако исследование все более сложных объ- ектов в технике, биологии, обществе поставило науку перед фактом не- возможности детального описания их поведения на языке материально- энергетических моделей. Потребность человека запомнить и выразить информацию об ок- ружающем мире стала толчком к появлению письменности, книгопеча- тания, живописи, фотографии, радио, телевидения. В истории развития цивилизации можно выделить несколько информационных революций — преобразование общественных отношений из-за кардинальных измене- ний в сфере обработки информации, информационных технологий. Следствием подобных преобразований являлось приобретение челове- ческим обществом нового качества. Первая информационная революция связана с изобретением пись- менности. Появилась возможность распространения знаний и сохране- ния их для передачи последующим поколениям. 3
Вторая (конец XVI в.) — вызвана изобретением книгопечатания, которое радикальным образом изменило общество и культуру. Третья (конец XIX в.) — обусловлена открытием принципов элек- тросвязи, благодаря которому появились телеграф, телефон, радио, по- зволяющие оперативно передавать информацию. Четвертая революция (70-е годы XX в.) связана с созданием персо- нальных компьютеров. Пятая революция (90-е годы XX в.) знаменует создание открытого информационного общества на базе глобальной сети Internet, мобиль- ной и спутниковой связи. В конце XX в. человечество вступило в новую стадию развития — стадию построения информационного общества. Информация стала важнейшим фактором экономического роста, а уровень развития инфор- мационной деятельности и степень вовлеченности и влияния ее на гло- бальную информационную инфраструктуру превратились в важнейшее условие конкурентоспособности страны в мировой экономике. Понима- ние неизбежности прихода этого общества наступило значительно раньше. Австралийский экономист К. Кларк еще в 40-е годы говорил о наступлении общества информации и услуг, общества с новыми техно- логическими и экономическими возможностями. Американский эконо- мист Ф. Махлуп выдвинул предположение о наступлении информаци- онной экономики и превращении информации в важнейший товар в конце 50-х годов. В конце 60-х годов Д. Белл фиксировал превращение индустриального общества в информационное. Что касается стран, ра- нее входивших в СССР, то процессы информатизации в них развива- лись замедленными темпами. Информатика меняет всю систему общественного производства и взаимодействия культур. С наступлением информационного общества начинается новый этап не только научно-технической, но и социальной революции. Меняется вся система информационных коммуникаций. Разрушение старых информационных связей между отраслями эконо- мики, направлениями научной деятельности, регионами, странами уси- лило экономический кризис конца века в странах, которые уделяли раз- витию информатизации недостаточное внимание. Важнейшая задача общества — восстановить каналы коммуникаций в новых экономиче- ских и технологических условиях для обеспечения четкого взаимодей- ствия всех направлений экономического, научного и социального раз- вития как отдельных стран, так и в глобальном масштабе. В качестве средства для хранения, переработки и передачи инфор- мации научно-технический прогресс предложил обществу компьютер (электронно-вычислительную машину — ЭВМ). Но вычислительная техника не сразу достигла необходимого уровня. В ее развитии отмеча- ют предысторию и четыре поколения ЭВМ. Предыстория начинается в 4
глубокой древности с различных приспособлений для счета (абак, сче- ты), а первая счетная машина появилась лишь в 1642 г. Ее изобрел французский математик Паскаль. Построенная на основе зубчатых ко- лес, она могла суммировать десятичные числа. Все четыре арифметиче- ских действия выполняла машина, созданная в 1673 г. немецким мате- матиком Лейбницем. Она стала прототипом арифмометров, использо- вавшихся с 1820 г. до 60-х годов XX в. Впервые идея программно- управляемой счетной машины, имеющей арифметическое устройство, устройства управления, ввода и печати (хотя и использующей десятич- ную систему счисления), была выдвинута в 1822 г. английским матема- тиком Бэббиджем. Проект опережал технические возможности своего времени и не был реализован. Лишь в 40-х годах XX в. удалось создать программируемую счетную машину на основе электромеханических реле, которые могли пребывать в одном из устойчивых состояний — «включено» и «выключено». Это сделать технически проще, чем пы- таться реализовать десять различных состояний, необходимых для об- работки информации на основе десятичной системы счисления. С каждым новым поколением ЭВМ увеличивались быстродействие и надежность их работы при уменьшении стоимости и размеров, совершенствовались устройства ввода и вывода информации. В соответствии с трактовкой компьютера как технической модели информационной функции челове- ка — устройства ввода приближаются к естественному для человека восприятию информации (зрительному, звуковому) и, следовательно, операции по вводу в компьютер становятся все более удобными для человека. Современный компьютер — это универсальное многофункцио- нальное электронное автоматическое устройство, которое в современ- ном обществе взяло на себя значительную часть работ, связанных с об- работкой информации. По историческим меркам компьютерные техно- логии обработки информации еще очень молоды и находятся в самом начале своего развития. Компьютерные технологии сегодня преобразу- ют или вытесняют старые технологии обработки информации. Неотъемлемой частью компьютера является программное обеспе- чение (ПО), которое можно разделить на системное и прикладное про- граммное обеспечение. Операционная система является основой системного ПО, обеспе- чивает функционирование и взаимосвязь всех компонентов компьютера и предоставляет пользователю доступ к его аппаратным возможностям. Прикладное программное обеспечение можно в свою очередь раз- делить на две группы программ: средства разработки и приложения. Средства разработки — это инструменты программиста. Тради- ционными средствами разработки являются системы (среды) програм- мирования (СП), использующие алгоритмические языки программиро- 5
вания (ЯП). Основой систем программирования являются трансляторы, т.е. программы, обеспечивающие перевод исходного текста программы на машинный язык (объектный код). Приложения — это программы, решающие задачи в конкретных предметных областях, такие как текстовые и графические редакторы, электронные таблицы, издательские системы, системы управления ба- зами данных (СУБД) и т.д. Настоящее учебное пособие посвящено вопросам разработки алго- ритмов и программ с использованием языков программирования высо- кого уровня и систем программирования. В первой главе рассмотрены основные принципы и понятия, свя- занные с алгоритмизацией и программированием — алгоритмы, струк- туры данных, кодирование символьной, цифровой, графической инфор- мации, интерфейсы и их программирование, языки и системы програм- мирования с общих позиций, внешние устройства и обращение к файлам и базам данных. Дается также необходимое представление о разработке приложений. В последующих разделах последовательно рассмотрены языки про- граммирования Pascal, Basic, С, а также даны краткие характеристики систем программирования Delphi и Visual Basic. В начале каждого раз- дела приводятся примеры текстов простой диалоговой программы и программы сортировки массива на соответствующем ЯП. Каждый из разделов заканчивается примерами и упражнениями. Во второй главе дается описание ЯП Pascal, в том числе при- меры простых программ на языке Pascal, лексика языка, переменные и константы, типы данных. Далее рассматриваются выражения и операции, операторы языка, структурированные типы данных, дина- мические данные, процедуры и функции. Описываются компоненты структуры программы, методы организации ввода-вывода данных и работы с файлами. В третьей главе представлена интегрированная среда разработки приложений Delphi, базирующаяся на объектно-ориентированном рас- ширении языка Pascal. Описывается интерфейс среды Delphi, характе- ристика проекта Delphi, компиляция и выполнение проекта, разработка приложения. Рассматриваются средства управления параметрами ин- тегрированной среды разработки, связь между ЯП Pascal и визуальной средой разработки приложений Delphi. В качестве стандартных приме- ров рассматривается разработка приложений «Калькулятор» и «Редак- тор текстов». В четвертой главе дается описание ЯП Basic, в том числе примеры программ на языке Basic, лексика языка, переменные и типы данных, операции и операторы, процедуры и функции, методы организации вво- да-вывода данных и работы с файлами. 6
В пятой главе дается краткая характеристика интегрированной среды разработки приложений Visual Basic. Рассматривается интерфейс среды, состав проекта, компиляция и выполнение проекта, разработка приложения. В качестве примера приводится разработка программы «Калькулятор» в среде Visual Basic. В шестой главе рассматривается ЯП С (Си), приводятся примеры двух простых программ на Си. Дается описание элементов языка Си, структуры программы. Рассматриваются объявления переменных, вы- ражения и присваивание значений, операторы языка и функции. Описы- ваются средства и процедуры ввода-вывода и доступа к файлам.
Глава 1. Основные принципы алгоритмизации и программирования 1.1. Алгоритмы и программы Понятие алгоритма. Понятие алгоритма является одним из ос- новных понятий современной математики. Еще на самых ранних ступе- нях развития математики (Древний Египет, Вавилон, Греция) в ней ста- ли возникать различные вычислительные процессы чисто механическо- го характера. С их помощью искомые величины ряда задач вычислялись последовательно из исходных величин по определенным правилам и инструкциям. Со временем все такие процессы в математике получили название алгоритмов (алгорифмов). Термин алгоритм происходит от имени средневекового узбекского математика Аль-Хорезми, который еще в IX в. (825) дал правила вы- полнения четырех арифметических действий в десятичной системе счисления. Процесс выполнения арифметических действий был назван алгоризмом. С 1747 г. вместо слова алгоризм стали употреблять алгорисмус, смысл которого состоял в комбинировании четырех операций арифме- тического исчисления — сложения, вычитания, умножения, деления. К 1950 г. алгорисмус стал алгорифмом. Смысл алгорифма чаще всего связывался с алгорифмами Евклида — процессами нахождения наибольшего общего делителя двух натуральных чисел, наибольшей общей меры двух отрезков и т.п. Под алгоритмом понимали конечную последовательность точно сформулированных правил, которые позволяют решать те или иные классы задач. Такое определение алгоритма не является строго матема- тическим, так как в нем не содержится точной характеристики того, что следует понимать под классом задач и под правилами их решения. Первоначально для записи алгоритмов пользовались средствами обычного языка (словесное представление алгоритмов). Уточним понятие словесного представления алгоритма на примере нахождения произведения п натуральных чисел — факториал числа п (с = я!), т.е. вычисления по формуле с = 1-2-3-4-...-И. 8
Этот процесс может быть записан в виде следующей системы по- следовательных указаний (пунктов): 1. Полагаем с равным единице и переходим к следующему пункту. 2. Полагаем i равным единице и переходим к следующему пункту. 3. Полагаем с = i -си переходим к следующему пункту. 4. Проверяем, равно ли i числу п. Если i = п, то вычисления пре- кращаем. Если i < п, то увеличиваем i на единицу и переходим к пункту 3. Рассмотрим еще один пример алгоритма — нахождение наимень- шего числа М в последовательности из п чисел аь а2, , а„ (п / 0). Прежде чем записать словесный алгоритм данного примера, детально рассмотрим сам процесс поиска наименьшего числа. Будем считать, что процесс поиска осуществляется следующим образом. Первоначально в качестве числа М принимается т. е. полагаем М = ah после чего М сравниваем с последующими числами последовательности, начиная с а2. Если Л/75} а2, то Л/сравнивается с а3, если Л/75} а2, то Л/сравнивается с а4, и так до тех пор, пока найдется число а, < М. Тогда полагаем М = at и продолжаем сравнение с М последующих чисел из последовательности, начиная с а,+1> и так до тех пор, пока не будут просмотрены все п чисел. В результате просмотра всех чисел М будет иметь значение, равное наименьшему числу из последовательности (7— текущий номер числа). Этот процесс может быть записан в виде следующей системы последо- вательных указаний: 1. Полагаем i = 1 и переходим к следующему пункту. 2. Полагаем Л/ = а, и переходим к следующему пункту. 3. Сравниваем i с п; если i < п, переходим к 4 пункту, если i = п, процесс поиска останавливаем. 4. Увеличиваем i на 1 и переходим к следующему пункту. 5. Сравниваем а, с М. Если Л/75} а, , то переходим к пункту 3, иначе (Л/> а, ) переходим к пункту 2. В первом алгоритме в качестве элементарных операций использу- ются простейшие арифметические операции умножения, которые могли бы быть разложены на еще более элементарные операции. Мы такого разбиения не делаем в силу простоты и привычности арифметических правил. Алгоритмы, в соответствии с которыми решение поставленных задач сводится к арифметическим действиям, называются численны- ми алгоритмами. Алгоритмы, в соответствии с которыми решение поставленных задач сводится к логическим действиям, называются логическими ал- горитмами. Примерами логических алгоритмов могут служить алго- ритмы поиска минимального числа, поиска пути на графе, поиска пути в лабиринте и др. 9
Алгоритмом, таким образом, называется система четких однознач- ных указаний, которая определяет последовательность действий над некоторыми объектами и после конечного числа шагов приводит к по- лучению требуемого результата. Свойства алгоритмов. Каждое указание алгоритма предписывает исполнителю выполнить одно конкретное законченное действие. Ис- полнитель не может перейти к выполнению следующей операции, не закончив полностью выполнения предыдущей. Предписания алгоритма надо выполнять последовательно одно за другим, в соответствии с ука- занным порядком их записи. Выполнение всех предписаний гарантиру- ет правильное решение задачи. Поочередное выполнение команд алгоритма за конечное число ша- гов приводит к решению задачи, к достижению цели. Разделение вы- полнения решения задачи на отдельные операции (выполняемые испол- нителем по определенным командам) — важное свойство алгоритмов, называемое дискретностью. Анализ примеров различных алгоритмов показывает, что запись ал- горитма распадается на отдельные указания исполнителю выполнить некоторое законченное действие. Каждое такое указание называется командой. Команды алгоритма выполняются одна за другой. После ка- ждого шага исполнения алгоритма точно известно, какая команда должна выполняться следующей. Алгоритм представляет собой после- довательность команд (также инструкций, директив), определяющих действия исполнителя (субъекта или управляемого объекта). Таким образом, выполняя алгоритм, исполнитель может не вникать в смысл того, что он делает, и вместе с тем получать нужный результат. В этом случае говорят, что исполнитель действует формально, т.е. от- влекается от содержания поставленной задачи и только строго выполня- ет некоторые правила, инструкции. Это очень важная особенность алгоритмов. Наличие алгоритма формализовало процесс, исключило рассуждения. Если обратиться к другим примерам алгоритмов, то можно увидеть, что и они позволяют исполнителю действовать формально. Таким образом, создание алго- ритма дает возможность решать задачу формально, механически испол- няя команды алгоритма в указанной последовательности. Построение алгоритма для решения задачи из какой-либо облас- ти требует от человека глубоких знаний в этой области, бывает свя- зано с тщательным анализом поставленной задачи, сложными, ино- гда очень громоздкими рассуждениями. На поиски алгоритма реше- ния некоторых задач ученые затрачивают многие годы. Но когда алгоритм создан, решение задачи по готовому алгоритму уже не тре- бует каких-либо рассуждений и сводится только к строгому выпол- нению команд алгоритма. 10
Всякий алгоритм составляется в расчете на конкретного исполни- теля с учетом его возможностей. Для того чтобы алгоритм мог быть выполнен, нельзя включать в него команды, которые исполнитель не в состоянии выполнить. Нельзя повару поручать работу токаря, какая бы подробная инструкция ему не давалась. У каждого исполнителя имеется свой перечень команд, которые он может исполнить. Совокупность ко- манд, которые могут быть выполнены исполнителем, называется сис- темой команд исполнителя. Каждая команда алгоритма должна опреде- лять однозначно действие исполнителя. Такое свойство алгоритмов на- зывается определенностью (или точностью) алгоритма. Алгоритм, составленный для конкретного исполнителя, должен включать только те команды, которые входят в его систему команд. Это свойство алгоритма называется понятностью. Алгоритм не должен быть рассчитан на принятие каких-либо самостоятельных решений ис- полнителем, не предусмотренных составленным алгоритмом. Еще одно важное требование, предъявляемое к алгоритмам, — ре- зультативность {или конечность) алгоритма. Оно означает, что испол- нение алгоритма должно закончиться за конечное число шагов. Разработка алгоритмов — процесс творческий, требующий умст- венных усилий и затрат времени. Поэтому предпочтительно разрабаты- вать алгоритмы, обеспечивающие решения всего класса задач данного типа. Например, если составляется алгоритм решения кубического уравнения ax' + bxz + cx + d = 0, то он должен быть вариативен, т.е. обеспечивать возможность решения для любых допустимых исходных значений коэффициентов a, b, с, d. Про такой алгоритм говорят, что он удовлетворяет требованию массовости. Свойство массовости не явля- ется необходимым свойством алгоритма. Оно скорее определяет качест- во алгоритма; в то же время свойства дискретности, точности, понятно- сти и конечности являются необходимыми (иначе это не алгоритм). Формы записи алгоритмов. Алгоритмы можно записывать по- разному. Форма записи, состав и количество операций алгоритма зави- сят от того, кто будет исполнителем этого алгоритма. Если задача реша- ется с помощью ЭВМ, алгоритм решения задачи должен быть записан в понятной для машины форме, т. е. в виде программы. Всякий алгоритм может быть: - записан на естественном языке (примеры записи алгоритма на ес- тественном языке приведены при определении понятия алгоритма); - изображен в виде блок-схемы; - записан на алгоритмическом языке. Запись алгоритмов в виде блок-схем. Схема алгоритма — графи- ческое представление алгоритма. Каждый пункт алгоритма отображает- ся на схеме некоторой геометрической фигурой — блоком — и допол- 11
няется элементами словесной записи. Правила выполнения схем алго- ритмов регламентирует ГОСТ 19.002—80 (единая система программной документации, см. табл. 1.1) Блоки на схемах соединяются линиями потоков информации. Ос- новное направление потока информации идет сверху вниз и слева на- право (стрелки могут не указываться), снизу вверх и справа налево — стрелка обязательна. Количество входящих линий для блока не ограни- чено. Выходящая линия должна быть одна (исключение составляет ло- гический блок). Таблица 1.1. Основные элементы блок-схем № п/п Символ Наименование Содержание 1. ст* » 'll ’li о ст* Блок вычислений Вычислительные действия или последовательность дей- ствий 2. Логический блок Выбор направления выпол- нения алгоритма в зависимо- сти от некоторого условия 3. Блоки ввода- вывода данных 1. Общие обозначения ввода (вывода) данных (вне зави- симости от физического но- сителя) 2. Вывод данных, носителем которых является документ 4. Начало (конец) Начало нли конец алгоритма, вход или выход в программу 5. Процесс пользова- теля (подпрограм- ма) Вычисление по стандартной программе или подпрограмме 6. Блок модификации Функция выполняет дейст- вия, изменяющие пункты (например, заголовок цикла) алгоритма 7. Соединитель Указание связи прерванными линиями между потоками информации в пределах од- ного листа. 8. J Межстраничные соединения Указание связи между ин- формацией на разных листах 12
Начало Рис. 1.1. Блок-схема алгоритма нахождения минимума в последовательности чисел Приведем запись алгоритма нахождения минимального числа М в последовательности из п чисел дь аъ , ап (и # 0) в виде блок-схемы (рис. 1.1). Базовые структуры алгоритмов — это определенный набор бло- ков и стандартных способов их соединения для выполнения типичных последовательностей действий. К основным структурам относятся сле- дующие: линейные (рис. 1.2, а), разветвляющиеся (рис. 1.2, б), цикличе- ские (рис. 1.2, в, г). 13
а) б) Рнс. 1.2. Базовые структуры алгоритмов и программ Линейными называются алгоритмы, в которых действия осуществ- ляются последовательно друг за другом. Стандартная блок-схема ли- нейного алгоритма приводится на рис. 1.3, а (вычисление произведения двух чисел — А и В). Разветвляющимся называется алгоритм, в котором действие вы- полняется по одной из возможных ветвей решения задачи, в зависимо- сти от выполнения условий. В отличие от линейных алгоритмов, в кото- рых команды выполняются последовательно одна за другой, в разветв- ляющиеся алгоритмы входит условие, в зависимости от выполнения или невыполнения которого выполняется та или иная последовательность команд (действий). В качестве условия в разветвляющемся алгоритме может быть ис- пользовано любое понятное исполнителю утверждение, которое может соблюдаться (быть истинно) или не соблюдаться (быть ложно). Такое утверждение может быть выражено как словами, так и формулой. Таким образом, алгоритм ветвления состоит из условия и двух последователь- ностей команд. 14
Примером может являться разветвляющийся алгоритм, изображен- ный на рис. 1.3, б. Аргументами этого алгоритма являются числа А и В, а результатом — число X. Если условие А >= В истинно, то выполняет- ся операция умножения чисел (X = А * В), в противном случае выполня- ется операция сложения (X = А + В). В результате печатается то значе- ние X, которое получается в результате выполнения одного из действий. Циклическим называется алгоритм, в котором некоторая часть опе- раций (тело цикла — последовательность команд) выполняется много- кратно. Однако слово «многократно» не значит «до бесконечности». Организация циклов, никогда не приводящая к остановке в выполнении алгоритма, является нарушением требования его результативности — получения результата за конечное число шагов. Перед операцией цикла осуществляются операции присвоения начальных значений тем объектам, которые используются в теле цикла. В цикл входят в качестве базовых следующие структуры: блок проверки условия и блок, называемый телом цикла. Если тело цикла расположено после проверки условий (рис. 1.2, в — цикл с преду- словием), то может случиться, что при определенных условиях тело цикла не выполнится ни разу. Такой вариант организации цикла, управляемый предусловием, называется циклом типа пока (здесь условие — на продолжение цикла). Возможен другой случай, когда тело цикла выполняется по крайней мере один раз и будет повторяться до тех пор, пока не ста- нет истинным условие. Такая организация цикла, когда его тело рас- положено перед проверкой условия, носит название цикла с пост- условием, или цикла типа до (рис. 1.2, г). Истинность условия в этом случае — условие окончания цикла. Отметим, что возможна ситуация с постусловием и при организации цикла-лока. Итак, цикл- до завершается, когда условие становится истинным, а цикл-лока — когда условие становится ложным. Современные языки программирования имеют достаточный набор операторов, реализующих как цикл-лока, так и цикл-до. Рассмотрим циклический алгоритм типа пока (рис. 1.2, в) на при- мере алгоритма вычисления факториала. N— число, факториал которо- го вычисляется. Начальное значение N\ принимается равным 1. К будет меняться от 1 до N и вначале также равно 1. Цикл будет выполняться, пока справедливо условие N <3 К. Тело цикла состоит из двух операций N]=N] *КиК-К+ 1 (рис. 1.3, в). Циклические алгоритмы, в которых тело цикла выполняется задан- ное число раз, реализуются с помощью цикла со счетчиком. Цикл со счетчиком реализуется с помощью рекурсивного увеличения значения счетчика в теле цикла {К = К + 1 в алгоритме вычисления факториала). 15
Рис. 1.3. Примеры структур алгоритмов: а — линейный алгоритм; б— алгоритм с ветвлением; в — алгоритм с циклом Процесс решения сложной задачи довольно часто сводится к решению нескольких более простых подзадач. Соответственно процесс разработки сложного алгоритма может разбиваться на этапы составления отдельных алгоритмов, которые называются вспомогательными. Каждый такой вспо- могательный алгоритм описывает решение какой-либо подзадачи. Процесс построения алгоритма методом последовательной детали- зации состоит в следующем. Сначала алгоритм формулируется в «круп- ных» блоках (командах), которые могут быть непонятны исполнителю (не входят в его систему команд) и записываются как вызовы вспомога- тельных алгоритмов. Затем происходит детализация, и все вспомога- тельные алгоритмы подробно расписываются с использованием команд, понятных исполнителю. Примеры и решения 1. Рассмотрим следующую известную задачу: имеются два кув- шина емкостью 3 и 8 л. Необходимо составить алгоритм, с помощью которого, пользуясь только этими двумя кувшинами, можно набрать 7 л воды. Можно предположить, что кувшин емкостью 3 л необходимо ис- пользовать для того, чтобы отлить в него 1 л из полного кувшина емко- 16
стью 8 л. Таким образом, решение задачи сводится к поиску возможно- сти поместить, например, 2 л воды в трехлитровый кувшин, затем на- полнить восьмилитровый и перелить из него воду в трехлитровый кувшин, в котором до полного заполнения не хватает ровно 1 л. Задача реализуется следующим линейным алгоритмом (А — количество воды в трехлитровом кувшине, В — количество воды в восьмилитровом кув- шине): 2. Рассмотрим задачу вычисления наибольшего общего делителя (НОД) двух натуральных чисел А и В с применением алгоритма Евкли- да. Основная идея алгоритма в том, что НОД А и В есть также и НОД (А - В), т.е. последовательное вычитание из большего числа меньшего до тех пор, пока числа не сравняются, должно привести к искомому значению НОД. 17
Приведем сначала запись алгоритма Евклида на естественном языке: 1. Рассмотреть А как первое число, В как второе. Перейти к пункту 2. 2. Сравнить первое и второе число. Если они равны, перейти к пункту 5. Если нет, перейти к пункту 3. 3. Если первое число меньше второго, то поменять их местами. Перейти к пункту 4. 4. Вычесть из первого числа второе и рассмотреть полученную разность как первое число. Перейти к пункту 2. 5. Рассмотреть первое число как результат. Закончить выполнение алгоритма. Представленный алгоритм имеет разветвляющуюся структуру и в виде блок-схемы изображается следующим образом: 3. Пусть необходимо вывести на печать все числа ряда Фибоначчи (1, 1, 2, 3, 5, 8,...) до заданного натурального N. Очередной член ряда F; определяется как сумма двух предыдущих (Fj = Гц + Fj.2). Первые два члена ряда равны 1. Представим блок-схему алгоритма решения задачи с использованием циклической структуры с предусловием (F — очередной член ряда, Р — предыдущий член ряда). Алгоритм решения этой задачи с использованием циклической структуры с постусловием предлагается разработать самостоятельно. 18
Вопросы и задания 1. Охарактеризуйте базовые структуры алгоритмов. 2. Дано число X и последовательность действий: • умножить полученное число на 2; • сообщить результат; • разделить А'на 3; • вычесть из полученного числа 5; • прибавить к полученному числу 7. Сколько и каких различных алгоритмов можно составить из этой последо- вательности действий? Какие функции от X при этом вычисляются? 3. Приведите примеры задач, для реализации которых применимы: а) линей- ные алгоритмы; б) разветвляющиеся алгоритмы; в) циклические алгоритмы. 4. Охарактеризуйте разницу между циклом типа до и циклом типа пока. 5. Приведите примеры задач, для реализации которых целесообразно приме- нять циклические структуры: а) с постусловием; б) с предусловием. 6. Изобразите блок-схему алгоритма определения максимального числа в последовательности из п произвольных чисел. 7. Нарисуйте блок-схему алгоритма получения произведения п произвольных чисел (п = 15). 8. Нарисуйте блок-схему алгоритма вычисления суммы квадратов первых п чисел натурального ряда. 19
9. Модифицируйте алгоритм вычисления суммы квадратов первых и чисел натурального ряда для вычисления суммы квадратов: а) только четных чи- сел (до и); б) только нечетных чисел (до я). 10. Изобразите блок-схему простого диалогового алгоритма, который обраща- ется к пользователю с просьбой ввести сначала строку имя, а затем строку настроение. В результате диалога может появиться следующий общий со- вместный текст Программа> Здравствуйте! Как Ваше имя? Пользователе Гаврик Программа> Доброе утро, Гаврик! Как настроение? Пользователь> так себе Программа> У меня тоже так себе, Гаврик! 1.2. Данные. Понятие типа данных Данные. Алгоритм, реализующий решение некоторой конкретной задачи, всегда работает с данными. Данные — это любая информация, представленная в формализованном виде и пригодная для обработки алгоритмом. Данные, известные перед выполнением алгоритма, являются на- чальными, исходными данными. Результат решения задачи — это ко- нечные, выходные данные. В задачах нахождения максимума из после- довательности чисел и их произведения исходными данными являются числа, а результатами (выходными данными) — соответственно с и М. Данные делятся на переменные и константы. Переменные — это такие данные, значения которых могут изме- няться в процессе выполнения алгоритма. Например, для алгоритма вычисления площади круга необходимо объявить две переменные: переменную R, в которую будет заноситься значение радиуса окружности, и переменную S для вычисления площа- ди круга по формуле 5 = л7?2. Константы — это данные, значения которых не меняются в про- цессе выполнения алгоритма. В примере, описанном выше, константой является число л. Каждая переменная и константа должна иметь свое уникальное имя. Имена переменных и констант задаются идентификаторами. Иден- тификатор (по определению) представляет собой последовательность букв и цифр, начинающаяся с буквы. Типы данных. С данными тесно связано понятие типа данных. Любой константе, переменной, выражению (с точки зрения обработ- 20
ки на ЭВМ) всегда сопоставляется некоторый тип. Тип данных ха- рактеризует множество значений, к которым относится константа и которые может принимать переменная или выражение. Например, если переменная i в некотором алгоритме может принимать только значения из множества целых чисел, то ей ставится в соответствие целый тип данных. Типы данных принято делить на простые {базовые) и структури- рованные. К основным базовым типам относятся: • целый (INTEGER) — определяет подмножество допустимых значений из множества целых чисел; • вещественный (REAL) — определяет подмножество допусти- мых значений из множества вещественных чисел; • логический (BOOLEAN) — множество допустимых значений — истина и ложь; • символьный (CHAR) — цифры, буквы, знаки препинания и пр. Тип INTEGER задает подмножество целых чисел, мощность кото- рого зависит от такой характеристики ЭВМ, как размер машинного сло- ва. Если для представления целых чисел в машине используется п раз- рядов (причем один из них отводится под указание знака числа), то до- пустимые числа должны удовлетворять условию -2я'1 75} х 75} 2я |. Считается, что все операции над данными этого типа выполняются точ- но и соответствуют обычным правилам арифметики. Если результат выходит за пределы допустимых значений, то вычисления будут пре- рваны. Такое событие называется переполнением. Четыре арифметиче- ские операции считаются стандартными: сложение (+), вычитание (-), умножение (*) и деление (/). Для целых чисел может быть определен дополнительный стандарт- ный тип — целое без знака, задающий подмножество целых положи- тельных чисел. Если для представления целых без знака используется п разрядов, то переменным такого типа можно присваивать значения, удовлетворяющие условию 0 75} х 75} 2я. Тип REAL обозначает подмножество вещественных чисел, границы изменения которых также определяются характеристиками конкретной ЭВМ. И если считается, что арифметика с данными типа INTEGER дает точный результат, то допускается, что аналогичные действия со значе- ниями типа REAL могут быть неточными, в пределах ошибок округле- ний, вызванных вычислениями с конечным числом цифр. Это принци- пиальное различие между типами REAL и INTEGER. Два значения базового типа BOOLEAN обозначаются идентифика- торами TRUE FALSE. Операции над данными этого типа и правила их выполнения будут рассмотрены в п. 1.3. 21
В базовый тип CHAR входит множество печатаемых символов и символов-разделителей в соответствии с кодом ASCII (табл, приложе- ния 2), Приведем пример представления числовой информации в различ- ных типах данных применительно к ЭВМ с 16-разрядным машинным словом. Пусть задано число 12345. Если этой константе поставлен в соответствие тип INTEGER, то внутренняя стандартная форма представления в памяти (для обработки в двоичной арифметике) — 0011000000111001. Объем — 2 байт, 16 двоичных разрядов. Если для константы определен тип REAL, то число представляется в форме с плавающей точкой и занимает объем 4 байта, 32 двоичных разряда. И, наконец, если число рассматривается как последовательность символов типа CHAR (символьное представление), то каждая его цифра представляется байтом в соответствии с кодом ASCII. Представление 12345 есть —00110001 00110010 00110011 00110100 00110101. Объем — 5 байтов. Переменные и типы данных вводятся для того, чтобы использовать их в различных алгоритмах обработки. Следовательно, нужно иметь еще и множество операций, которые можно применять к данным того или иного типа. Так, к переменным типа REAL и INTEGER применимы арифметические операции, к переменным типа BOOLEAN — логиче- ские (см. п.1.3), к символьным переменным применима операция конка- тенации — «соединения» символов. Операции отношения или сравне- ния применяются к данным любого типа, но правила их применения различны. Рассмотрим, например, результат применения некоторых операций к значениям 123 и 45 в зависимости от их типа: Знак Операция Запись Тип данных Результат 4- Сложение 123+45 INTEGER 168 Конкатена- ция «123» + «45» CHAR «12345» > Больше 123 >45 INTEGER Истина (TRUE) «123» > «45» CHAR Ложь (FALSE) Таким образом, тип данных — это такая характеристика данных, которая, с одной стороны, задает множество значений для возможного изменения данных и, с другой стороны, определяет множество опера- ций, которые можно к этим данным применять, и правила выполнения этих операций. 22
До сих пор мы говорили о переменных, хранящих только одно зна- чение, и рассматривали возможности различного представления и ис- пользования этого значения при решении конкретных задач. На самом деле, огромное количество алгоритмов требует одновре- менного хранения в памяти целых наборов однородных объектов, при- чем длина этих наборов может быть заранее неизвестна. Например, пусть необходимо обрабатывать данные о среднесуточ- ной температуре за год для вычисления максимальной и минимальной температур, среднемесячной и среднегодовой температуры и т.п. Для реализации таких алгоритмов необходимо обеспечить хранение каждого отдельного значения среднесуточной температуры. Если иметь при этом в виду переменные базового типа Real, то таких переменных потребова- лось бы 365. Рассмотрим другой пример. Пусть решение некоторой задачи тре- бует вводить и обрабатывать следующие данные о студентах: фамилия, имя, отчество, дата рождения, год поступления в вуз, номер студенче- ского билета. При этом алгоритм должен обеспечить возможность ввода произвольного количества данных. Здесь речь идет об обработке одно- родных объектов, которые можно условно назвать «Информация о сту- денте», представляющих собой совокупность разнородных данных или атрибутов (фамилия, имя и т.д.). Для решения подобных задач применяются структуры данных, поддерживаемые множеством структурированных типов данных. Структурированные типы описывают наборы однотипных или разнотипных данных, с которыми алгоритм должен работать как с од- ной именованной переменной. Массив. Наиболее широко известная структура данных — массив. Массив представляет собой упорядоченную структуру однотипных дан- ных, которые называются элементами массива. Структура данных типа массив подходит, например, для решения задачи обработки среднесу- точных температур, описанной выше. Доступ к каждому отдельному элементу массива осуществляется с помощью индекса — в общем случае порядкового номера элемента в массиве. Массивы могут быть как одномерными (адрес каждого элемента определяется значением одного индекса), так и многомерными (адрес каждого элемента определяется значением нескольких индексов). Рассмотрим задачу сортировки (расположения) по возрастанию N целых чисел. Для ее решения, во-первых, необходимо обеспечить ввод всех N чисел, а затем применить один из известных методов сортиров- ки. Любой метод сортировки предполагает неоднократный проход всех или части чисел, поэтому числа целесообразно организовать в массив. 23
1 -й проход: 1_2_ 1 11 1 3 1 1 1 1 4 J i=1 1 -й элемент меняется с 4-м 1 2 3 4 5 2-й проход: | 1 1 и 1 3 1 2 1 1 4 ] i = 2 1 2 3 4 5 2-й элемент меняется с 4-м 3-й проход: | 1 1 2 | 3 1 и 1 4 ] i = 3 1 2 3 4 5 3-й элемент не меняет места 4-й проход: 1 1 1 2 | 3 1 11 1 4 ] i = 4 1 2 3 4 5 4-й элемент меняется с 5-м ш L_2j 3 1 4 1 11 ] Массив отсортирован 1 2 3 4 5 Рис. 1.4. Сортировка массива из 5 элементов Метод сортировки посредством простого выбора предполагает циклический просмотр элементов массива, начиная с z-го («= 1,2,..., N- 1), поиск минимального элемента и перестановку найденного минимально- го элемента с г-м. За N — 1 проход по массиву (элемент с номером N останется на своем месте) числа будут отсортированы (рис. 1.4). Подобный алгоритм, очевидно, состоит из трех этапов: • ввод элементов массива; • цикл обработки массива; • вывод отсортированных чисел из массива. Цикл обработки массива представляет собой совокупность двух «вложенных» циклов: первый цикл (с переменной цикла i) обеспечива- ет «внешний» проход по элементам массива, начиная с 1-го и кончая элементом с номером N— 1, который должен заканчиваться на каждом шаге перестановкой элементов; второй цикл (с переменной цикла/) реа- лизует поиск минимального элемента среди элементов с номерами от i + 1 до N (r.e.j меняется от i + 1 до N). Блок-схема предлагаемого алгоритма сортировки представлена на рис. 1.5. Запись. Наиболее общий метод получения структурных типов за- ключается в объединении элементов произвольных типов. Причем сами эти элементы могут быть в свою очередь структурными. Примером данных такого типа может быть объект «Информация о студенте», рас- смотренный выше. В математике это могут быть комплексные числа, состоящие из двух вещественных чисел, или координаты точки, опреде- ляемые двумя или более (в зависимости от размерности пространства) числами. 24
Начало Рис. 1.5. Блок-схема алгоритма сортировки элементов массива по возрастанию 25
Множество значений, определенное таким структурным типом, со- стоит из всех возможных комбинаций значений, относящихся к каждо- му из множеств значений элементов структуры. Таким образом, число таких комбинаций равно произведению числа элементов в каждом из составляющих множеств. При обработке данных структурные типы, описывающие объекты, обычно встречаются в файлах или базах данных, поэтому к данным та- кой природы стало широко применяться слово запись. Элементы записи называются полями. Величины типа запись могут быть представлены графически. На рис. 1.6 изображены следующие переменные: z типа Complex — объект, описывающий комплексное число, с по- лями ге и im типа Real; d типа Date — объект, описывающий дату, с полями day, month и year со значениями из подмножеств типа Integer (например, day может быть целым числом в интервале от 1 до 31); р типа Person — объект «Информация о студенте» с полями family, firstname, secondname, Date, year, num. Ранние языки программирования (ЯП) — Фортран, Алгол — буду- чи ориентированы исключительно на вычисления, не содержали разви- тых систем типов и структур данных. В Алголе, например, были определены два типа структур: элемен- тарные данные и массивы (векторы, матрицы, состоящие из арифмети- ческих или логических переменных), а символьные величины и пере- менные вообще не предусматривались. Основным нововведением, поя- вившимся первоначально в языке Cobol, (затем PL/1, Pascal и пр.) является символьный тип (цифры, буквы, знаки препинания и пр.), а также записи, структуры (синоним записи). В табл. 1.2 приведены для сравнения типы и структуры данных некоторых языков программиро- вания. Complex z Date d Person p 1.0 14 Иванов -1.0 4 Иван 2002 Иванович 1 | 4 1985 2002________________ 235/02 Рис. 1.6. Графическое представление записей 26
Таблица 1.2. Типы и структуры данных в некоторых языках программирования Язык программирования т и п д а н н ы X Algol Pascal Basic c Целое короткое (2 бай- та) - Integer Smallint Integer Short Целое норм. (4 байта) Integer Longint Integer Long Long Действительное норм. (4 байта) Real Single Single Float Действ, двойное (8 байт) — Real Double Currency Long float Логическое Boolean Boolean Boolean Boolean Символьное (1 байт) — Char — — Массивы Array Array Dim [] Записи (структуры) Record - Struct Примеры и решения 1. Рассмотрим алгоритм обработки двумерного массива. Аналогом двумерного массива в математике является матрица. Общий вид матри- цы А размерности М х N следующий: 'а„а12-а^ a2ia22-a2N <aM\aM2—aMN Для матрицы размерностью N х N (квадратной матрицы) определены понятия главной и побочной диагоналей. Элементы главной диагонали — |a(..,z = 1..2vJ, а элементы побочной диагонали— |а,(ЛГ_,+|), i = l.JVj. Пусть необходимо построить матрицу, элемент которой ау вычис- ляется как (z + j)2, а затем зеркально отобразить ее относительно глав- ной диагонали. Элементам главной диагонали при этом присвоить зна- чение 0. Для решения поставленной задачи воспользуемся структурой дан- ных — двумерный массив целых чисел. На первом этапе построим вло- женный цикл расчета элементов и заполнения массива. На втором этапе проведем «зеркальное отражение» элементов, находящихся под главной 27
диагональю. И, наконец, на третьем этапе обнулим элементы главной диагонали. Блок-схема алгоритма приведена на рис. l.A (N — размерность массива, I и J — индексы). Рис. 1.А 2. Рассмотрим задачу, решение которой основано на преобразова- нии типов данных. Пусть необходимо построить алгоритм сложения очень больших целых чисел, для представления которых не подходит тип данных INTEGER, имеющий ограничения на максимально возможные храни- мые значения. Используем для ввода в алгоритм сложения символьные представ- ления чисел, т.е. рассмотрим каждое число как последовательность символов (например, А = «1234567890» и В = «98765432199»). Для каж- 28
дого числа посчитаем длину его символьного представления (N — дли- на числа А, М — длина числа В) и присвоим некоторой переменной L значение максимальной длины. Следующим шагом разместим 3 массива байтов размерностью L + 1 (такова будет максимальная длина в символах полученного результата) и заполним их элементы числовым значением 0. В первый и второй массивы перешлем (начиная с конца) посимвольно числа А и В, преоб- разуя каждый символ в числовое представление (в соответствии с таб- лицей ASCII, это можно сделать, например, вычтя из числового значения кода цифры значение кода символа «0»). При этом в первом и втором массивах получим так называемые двоично-десятичные представления чисел (т.е. когда каждый разряд десятичного числа представляется дво- ичной комбинацией): А: | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | В: | 0| 9 | 8 | 7 | б| 5 | 4| з| 2 | 1| 9 | 9~| С: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Теперь, складывая побайтно (от элемента с номером L+I до эле- мента с номером 1) числовые значения в массивах А и В и помещая ре- зультат в соответствующий байт массива С, получим двоично-деся- тичный результат сложения двух чисел: А: 0 0 1 2 3 4 5 6 7 8 9 0 В: 0 9 8 7 6 5 4 3 2 1 9 9 1-я итерация: С: 0 0 0 0 0 0 0 0 0 0 0 9 2-я итерация: С: 0 0 0 0 0 0 0 0 0 1 8 9 Результат: С: 1 0 0 0 0 0 0 0 0 0 8 9 Перенос разряда при сложении может быть осуществлен так, как показано на блок-схеме (рис. 1.В). Последним шагом решения задачи должно быть преобразование С из двоично-десятичного представления в символьное для вывода ре- зультата. 29
Рис. 1.В 3. Пусть требуется посчитать количество слов в некоторой после- довательности символов. Определим слово как часть последовательно- сти (ненулевой длины), ограниченную с двух сторон (либо только спра- ва или слева) символом «пробел». Для решения задачи разместим обрабатываемую последователь- ность в массиве символов размерностью N (где N — длина последова- тельности). При построении алгоритма необходимо учесть, что после- довательность может начинаться или заканчиваться пробелами и может встречаться несколько пробелов подряд. Блок-схема алгоритма приведена на рис. 1 ,С (L — длина очередно- го слова, NW — количество слов в последовательности, знак пробела обозначен как «_»). Вопросы и задания 1. Какое максимальное (минимальное) целое значение без знака (со знаком) можно записать в 12 битов? 2. Сколько байтов потребуется для записи Вашей фамилии, имени и отче- ства? 30
Рис. l.C 3. Сколько записей «Информация о студенте» можно разместить в 1 мегабай- те памяти? 4. Охарактеризуйте различные способы представления числовой информации. 5. Охарактеризуйте разницу между простым и структурированным типом данных. 6. Изобразите в виде блок-схемы алгоритм нахождения максимального (ми- нимального) элемента в двумерном массиве. 7. Постройте алгоритм зеркального отображения элементов двумерного мас- сива размерностью TVxTV относительно побочной диагонали. 8. Постройте алгоритм формирования одномерного массива, каждый элемент кото- рого представляет собой сумму элементов строки некоторого двумерного массива. 31
9. Постройте алгоритм вычитания больших целых чисел. 10. Постройте алгоритм ввода данных в структуру «Информация о студенте». 11. Опишите в виде массива записей информацию о студентах группы и по- стройте алгоритм подсчета среднего возраста студентов. 1.3. Логические основы алгоритмизации Важной составляющей алгоритмов являются логические условия. Вычисление значений логических условий происходит в соответствии с аксиомами алгебры логики. Начало исследований в области формальной логики было поло- жено работами Аристотеля в IV в. до н. э. Однако математические подходы к этим вопросам впервые были указаны Джорджем Булем, который положил в основу математической логики алгебру логики (в честь ее создателя алгебру логики называют булевой алгеброй, а ло- гические значения — булевыми). Алгебра логики используется при построении основных узлов ЭВМ — шифратора, дешифратора, сум- матора. Алгебра логики оперирует с высказываниями. Под высказыванием понимают повествовательное предложение, относительно которого имеет смысл говорить, истинно оно или ложно. Например, выражение «Расстояние от Москвы до Киева больше, чем от Москвы до Тулы» ис- тинно. а выражение «4 < 3» — ложно. Высказывания принято обозначать большими буквами латинского алфавита: А, В, С... X, Y и т.д. Если высказывание С истинно, то пишут С = 1 (С = t, true), а если оно ложно, то С = 0 (С = f, false). В алгебре высказываний над высказываниями можно производить определенные логические операции, в результате которых получаются новые высказывания. Истинность полученных высказываний зависит от истинности исходных высказываний и использованных для их преобра- зования логических операций. Для образования новых высказываний наиболее часто используют- ся логические операции, выражаемые словами «не», «и», «или». Конъюнкция (логическое умножение). Соединение двух (или не- скольких) высказываний в одно с помощью союза И (OR) называется операцией, логического умножения, или конъюнкцией. Эту операцию принято обозначать знаками «л, &» или знаком умножения «х». Слож- ное высказывание А & В истинно только в том случае, когда истинны оба входящих в него высказывания. Истинность такого высказывания задается следующей таблицей: 32
А В A&B false false false false true false true false false true true true Дизъюнкция (логическое сложение). Объединение двух (или не- скольких) высказываний с помощью союза ИЛИ (OR) называется опе- рацией логического сложения, или дизъюнкцией. Эту операцию обозна- чают знаками «|, V» или знаком сложения «+». Сложное высказывание A v В истинно, если истинно хотя бы одно из входящих в него высказыва- ний. Таблица истинности для логической суммы высказываний имеет вид: A В A vB A xor В false false false false false true true true true false true true true true true false В последнем столбце данной таблицы размещены результаты мо- дифицированной операции ИЛИ — ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR), отличающееся от обычного ИЛИ последней строкой. Инверсия (логическое отрицание). Присоединение частицы НЕ (NOT) к данному высказыванию называется операцией отрицания (ин- версии). Она обозначается А (или —А)и читается не А . Если высказыва- ние А истинно, то В ложно, и наоборот. Таблица истинности в этом слу- чае имеет вид A -A false true true false Помимо операций И, ИЛИ, НЕ в алгебре высказываний существует ряд других операций. Например, операция эквиваленции (эквивалент- ности) А ~ В ( или А = В, A eqv В), которая имеет следующую таблицу истинности: A В A~B false false true false true false true false false true true true 33
Другим примером может служить логическая операция имплика- ции или логического следования (А —> В, A imp В), объединяющая вы- сказывания словами «если..., то» и имеющая следующую таблицу ис- тинности: А В A-+B false false true false true true true false false true true true Высказывания, образованные с помощью логических операций, на- зываются сложными. Истинность сложных высказываний можно уста- новить, используя таблицы истинности. Например, истинность сложно- го высказывания А & В определяется следующей таблицей: A В В -'A&B false false true true true false true true false false true false false true false true true false false false Высказывания, у которых таблицы истинности совпадают, назы- ваются равносильными. Для обозначения равносильных высказыва- ний используют знак «=» (А = В). Рассмотрим сложное высказывание (А & В) | (-'А & ^В) и запишем таблицу истинности этого высказывания: A В -'A -B A&B -ЛЛ-В (ЛЛВ)К-'ЛЛ-В) false false true true false true true false true true false false false false true false false true false false false true true false false true false true Если сравнить эту таблицу с таблицей истинности операции экви- валентности высказываний А и В, то можно увидеть, что высказывания (А & В) | (-•А & -•В) и А ~ В тождественны, т.е. А ~ В = (А & В) | (-А & -В). В алгебре высказываний можно проводить тождественные преоб- разования, заменяя одни высказывания равносильными им другими вы- сказываниями. Исходя из определений дизъюнкции, конъюнкции и отрицания, ус- танавливаются свойства этих операций и взаимные распределительные свойства. Приведем примеры некоторых из этих свойств: 34
• Коммутативность (перестановочность) АлВ = В лА Av В — Bv А • Закон идемпотентности А&А = A,AvA=A • Сочетательные (ассоциативные) законы Av (Bv C) = (Av B)v С = Av Bv C Aa(BaC) = (AaB)aC = AaBaC • Распределительные (дистрибутивные) законы Aa(BvC) = (AaB)v(AaC) Av(BaC) = (AvB)a(AvC) • Законы де Моргана 1. —(А л В) = —A v —В (условно его можно назвать 1-й); 2. —>(А v В) = —А л —В (2-й) — описывают результаты отрицания переменных, связанных операциями И, ИЛИ. Логическое значение NULL. В некоторых ЯП (например, Visual Basic) для расширения применимости логических выражений на те слу- чаи, когда значение одного или нескольких логических аргументов не- известны или не определены, вводится значение null (в дополнение к false и true). Как правило, такое значение присваивается компилятором логической переменной по умолчанию. С учетом значения null таблицы истинности основных логических операций приобретают вид, представленный в табл. 1.3 и 1.4. Таблица 1.3. Операция отрицания (одноместная,унарная) А -А false true true false null null Таблица 1.4. Некоторые двуместные (бинарные) операции A В A&B AvB A~>B A~B A xor В false false false false true true false false true false true true false true true false false true false false true true true true true true true false 35
Продолжение табл. 1.4 А В A&B AvB A—^B A~B A xor В false null false false true null null true null null true null null null null false false null null null null null true null true true null null null null null null null null null Побитовые операции. В набор операций некоторых современных ЯП включены операции побитового сравнения содержимого машинных слов (которые могут содержать числовые, строчные и др. данные) при этом каждый бит результата образуется в соответствии с табл. 1.4 (для бинарных операций). Унарная операция отрицания (not) в данном слу- чае реализует очевидную замену 1 на 0 и наоборот. Таблица 1.5. Операнды и результаты некоторых операций побитового сравнения X r X and Y Xor Y Ximp Y Xeqv Y X xor Y 0 0 0 0 1 1 0 0 1 0 1 1 0 1 1 0 0 1 0 0 1 1 1 1 1 1 1 0 Примеры и решения 1. Рассмотрим следующую задачу. Пусть требуется сформулиро- вать логическое выражение для отбора среди записей «Информация о студенте» тех, у которых в дате рождения стоит декабрь 1984 г. или ян- варь 1985 г. Построим сначала простые высказывания, затем объединим их в логическое выражение и приведем для него таблицу истинности: высказывание А — «Месяц рождения декабрь»; высказывание С — «Месяц рождения январь»; высказывание В — «Год рождения 1984»; высказывание D — «Год рождения 1985». Логическое выражение на базе простых высказываний имеет сле- дующий вид: (А & В) | (С & D). Построим таблицу истинности (1 — true, 0 — false, жирным шриф- том выделены наборы, на которых логическое выражение истинно): 36
А в с D А&В C&D (A&B)\(C&D) 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 1 1 0 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 2. Известен следующий закон свертки логического выражения: (Л & В) | (-А & С) | (В & С) = (А & В) | (-Л & Q, т.е. третье слагаемое логической суммы в левой части выражения не влияет на конечный результат. Проиллюстрируем этот закон с помощью таблиц истинности: А в с А&В -'А & С в&с (A&B) I (-Л&О I (В&С) (A&B) I (~А&С) 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 1 0 0 1 1 1 1 1 1 0 1 1 1 Вопросы и задания 1. Составьте таблицу истинности для (А & В) | (~А & -^В) с учетом значения null. 2. Составьте таблицы истинности для левого (->(А а В)) и правого (—A v —В ) выражений 1-го закона де Моргана. Проверьте их на соответствие. 3. Составьте таблицы истинности для левого (->(/lv В)) и правого ( -А л —.В ) выражений 2-го закона де Моргана. Проверьте их на соответствие. 37
4. Проверьте выполнимость законов де Моргана с учетом значения null. 5. Последний столбец таблицы истинности для двуместных операций, оче- видно, может содержать 16 = 24 различных сочетаний 1 и 0. Следовательно, всего может быть определено 16 логических операций над 2 переменными, из которых нами рассмотрены только 5 (табл. 1.4). Составьте таблицу ис- тинности для одной из 9 оставшихся вне рассмотрения функций и попы- тайтесь построить логическое выражение для этой функции. 1.4. Языки программирования: эволюция, классификация В развитии инструментального программного обеспечения (т.е. про- граммного обеспечения, служащего для создания программных средств в любой проблемной области) рассматривают пять поколений языков про- граммирования (ЯП). Языки программирования как средство общения че- ловека с ЭВМ от поколения к поколению улучшали свои характеристики, становясь все более доступными в освоении непрофессионалам. Первые три поколения ЯП характеризовались более сложным на- бором зарезервированных слов и синтаксисом. Языки четвертого поко- ления все еще требуют соблюдения определенного синтаксиса при на- писании программ, но он значительно легче для освоения. Естественные ЯП, разрабатываемые в настоящее время, составят пятое поколение и позволят определять необходимые процедуры обработки информации, используя предложения языка, весьма близкого к естественному и не требующего соблюдения особого синтаксиса (табл. 1.6) Таблица 1.6. Поколения ЯП Поколе- ния Языки программирования Характеристика Первое Машинные Ориентированы на использование в конкретной ЭВМ, сложны в освоении, требуют хорошего знания архитектуры ЭВМ Второе Ассемблеры, Макроассемблеры Более удобны для использования, но по-прежнему машинно-зависимы Третье Языки высокого уровня Мобильные, человеко- ориентированные, проще в освоении Четвертое Непроцедурные, объ- ектно-ориентирован- ные, языки запросов, параллельные Ориентированы на непрофессиональ- ного пользователя и на ЭВМ с парал- лельной архитектурой 38
Продолжение табл. 1.6 Поко- ления Языки программирования Характеристика Пятое Языки искусственного интеллекта, экспертных систем и баз знаний, естественные языки Ориентированы на повышение интел- лектуального уровня ЭВМ и интерфейса с языками ЯП первого поколения представляли собой набор машинных ко- манд в двоичном (бинарном) или восьмеричном формате, который оп- ределялся архитектурой конкретной ЭВМ. Каждый тип ЭВМ имел свой ЯП, программы на котором были пригодны только для данного типа ЭВМ. От программиста при этом требовалось хорошее знание не только машинного языка, но и архитектуры ЭВМ. Рассмотрим проблему программирования в абсолютных (машин- ных) адресах. Вычислительная машина (система), независимо от типа и поколе- ния, состоит из двух основных типов устройств: - центральное устройство (ЦУ), включающее в себя центральный процессор (ЦП) и оперативную память (ОП); - периферийные (внешние) устройства (ВУ). Несмотря на то что постоянно идут исследования в направлении разработки новых принципов структур и архитектур ЭВМ, в подавляю- щем большинстве современных машин реализованы так называемые принципы Фон Неймана: - ОП организована как совокупность машинных слов (МС) фиксиро- ванной длины или разрядности (имеется в виду количество двоичных еди- ниц или бит, содержащихся в каждом МС). Например, ранние ПЭВМ имели разрядность 8, затем появились 16-разрядные, а в последнее время — 32- и 64-разрядные машины. В свое время существовали также 45-разрядные (М-20, М-220), 35-разрядные (Минск-22, Минск-32) и др. машины. - ОП образует единое адресное пространство, адреса МС возрас- тают от младших к старшим; - в ОП размещаются как данные, так и программы, причем в облас- ти данных одно слово, как правило, соответствует одному числу, а в об- ласти программы — одной команде (машинной инструкции — мини- мальному и неделимому элементу программы); - команды выполняются в естественной последовательности (по возрастанию адресов в ОП), если/пока не встретится команда управле- ния (условного/безусловного перехода), в результате которой естест- венная последовательность нарушится; - ЦП может произвольно обращаться к любым адресам в ОП для выборки и/или записи в МС чисел или команд. 39
Обобщенный порядок функционирования некоторого абстрактного (упрощенного) ЦУ следующий: • извлечение из ОП очередной команды. Типичная команда со- держит: код операции (КОП), действия (сложение, вычитание чисел; сравнение строк; передача управления, обращение к ВУ и пр.); адреса операндов Al, А2 и т.д., участвующих в выпол- нении команды (чисел, строк, других команд программы); • расшифровка КС П; • выборка адресов Al, А2 и пр. в регистры адреса; • выполнение операции (арифметической, логической и пр.) и помещение результата в регистр результата; • запись результата по одному из адресов (если необходимо); • переход к следующей команде. Таким образом, программирование в машинных адресах требует знания системы команд конкретной ЭВМ и их адресности. При этом реализация даже довольно несложных вычислений требует разложения их на простые операции, что значительно увеличивает общий объем программы и затрудняет ее чтение и отладку. В качестве примера рассмотрим последовательность реализации вычисления по формуле у = (а + by - c/d. План последовательности машинных операций, выполнение кото- рой приведет к нужному результату, в данном случае следующий: 1. rl = а + Ь; — операция сложения 2. r2 = rl*rl; —операция умножения 3. гЗ = c/J; — операция деления 4. у = г2 - гЗ; — операция вычитания 5. Стоп. — завершение обработки Здесь количество переменных, необходимых для хранения проме- жуточных результатов, связано с адресностью системы команд и с тем, разрешено или нет в процессе вычислений изменять значения исходных данных. Некоторые из приведенных выше операций могут быть в свою оче- редь разложены на несколько операций. Это обусловливается самой системой команд (например, если операция сложения может складывать только величины, находящиеся в специально отведенных для этой цели регистрах, то потребуется дополнительно использовать операции пере- сылки данных в эти регистры). Адресность команд ЦП связана с разрядностью ОП. Типичная команда состоит из фиксированной части (КОП) и адресной части, в которой указаны адреса операндов. Увеличение разрядности позволяет увеличить адресность команды и длину адреса (т.е. объем памяти, дос- тупной данной команде). Увеличение адресности, в свою очередь, при- 40
водит к повышению быстродействия обработки (за счет снижения числа требуемых команд). С увеличением разрядности увеличивается также и диапазон значений обрабатываемых чисел и количества информации, извлекаемой за один такт работы машины из ОП, или записываемой в ОП. Ранние ПЭВМ имели разрядность 8, объем ОП 64 Кбайт, последние мо- дели обладают разрядностью 32-64 и объемом ОП 64-512 Мбайт. Второе поколение ЯП характеризуется созданием языков ассемб- лерного типа (ассемблеров, макроассемблеров), позволяющих вместо двоичных и других форматов машинных команд использовать их мне- монические символьные обозначения (имена). Являясь существенным шагом вперед, ассемблерные языки все еще оставались машинно- зависимыми, а программист все также должен был быть хорошо знаком с организацией и функционированием аппаратной среды конкретного типа ЭВМ. При этом ассемблерные программы все так же затрудни- тельны для чтения, трудоемки при отладке и требуют больших усилий для переноса на другие типы ЭВМ. Однако и сейчас ассемблерные язы- ки используются при необходимости разработки высокоэффективного программного обеспечения (минимального по объему и с максимальной производительностью). Текст исходной программы на ассемблере состоит из операторов, каждый из которых занимает отдельную строку этого текста. Различают два типа операторов: инструкции и директивы. Первые при трансляции преобразуются в команда про фессора, которые исполняются после за- грузки в память загрузочного модуля программы, имеющего расшире- ние .СОМ или .EXE . Операторы второго типа управляют процессом ассемблирования — преобразования текста исходной программы в коды объектного модуля (расширение .OBJ). Ассемблер интерпретирует и обрабатывает операторы один за другим, генерируя последовательность из команд процессора и байтов данных. Особо следует остановиться на использовании макрокоманд. При программировании на макроассемблере можно формировать обращение к часто повторяющейся последовательности команд при помощи одного оператора. Этот прием несколько напоминает вызов подпрограмм в языках высокого уровня, но между ними лежит значительное различие, заключающееся в том, что подпрограмма, занимающая некоторый уча- сток памяти, может быть исполнена неограниченное число раз путем передачи ей управления из вызывающей программы, в которую подпро- грамма сама затем возвращает управление. В ассемблере используются макровызовы макроопределений. Макроопределение — это последова- тельность операторов, которые могут содержать формальные парамет- ры. Макроопределение и команда обращения к макроопределению (макровызов) образуют макрокоманду. Макровызов — это оператор 41
вызова макроопределения. Если макроопределение содержит формаль- ные параметры, то макровызов обязан содержать фактические значения этих параметров, которые будут подставлены вместо соответствующих формальных. В результате макровызова формируется реальная последо- вательность команд — макрорасширение. Макрорасширение вставляет- ся в исходный текст программы на место оператора макровызова. Таким образом, в исходный текст программы макрорасширение одного и того же макроопределения может быть вставлено несколько раз, по числу макровызовов. Каждое макрорасширение после трансляции, естествен- но, занимает свой участок памяти. Третье поколение ЯП начинается с появления в 1956 г. первого языка высокого уровня — Fortran, разработанного под руководством Дж. Бэкуса в фирме IBM. За короткое время Fortran становится основ- ным ЯП при решении инженерно-технических и научных задач. Перво- начально Fortran обладал весьма ограниченными средствами обеспече- ния работы с символьной информацией и с системой ввода-вывода. Од- нако постоянное развитие языка сделало его одним из самых распространенных ЯВУ на ЭВМ всех классов — от микро- до супер- ЭВМ, а его версии используются и для вычислительных средств нетра- диционной параллельной архитектуры. Вскоре после языка Fortran появились такие ныне широко извест- ные языки, как Algol, Cobol, Basic, PL/1, Pascal, APL, ADA, C, Forth, Lisp, Modula и др. В настоящее время насчитывается свыше 2000 раз- личных языков высокого уровня. Языки четвертого поколения носят ярко выраженный непроцедур- ный характер, определяемый тем, что программы на таких языках опи- сывают только что, а не как надо сделать. В программах формируются скорее соотношения, а не последовательности шагов выполнения алго- ритмов. Типичными примерами непроцедурных языков являются языки, используемые для задач искусственного интеллекта (например, Prolog, Langin). Так как непроцедурные языки имеют минимальное число син- таксических правил, они значительно более пригодны для применения непрофессионалами в области программирования. Второй тенденцией развития ЯП четвертого поколения являются объектно-ориентированные языки, базирующиеся на понятии про- граммного объекта, впервые использованного в языке Simula-67 и со- ставившего впоследствии основу известного языка SmallTalk. Про- граммный объект состоит из структур данных и алгоритмов, при этом каждый объект знает, как выполнять операции со своими собственными данными. На самом деле, различные объекты могут пользоваться со- вершенно разными алгоритмами при выполнении действий, определен- ных одним и тем же ключевым словом (так называемое свойство поли- 42
морфизма). Например, объект с комплексными числами и массивами в качестве данных будет использовать различные алгоритмы для выпол- нения операции умножения. Такими свойствами обладают объектно- ориентированные Pascal, Basic, C++, SmallTalk, Simula, Actor и ряд дру- гих языков программирования. Третьим направлением развития языков четвертого поколения можно считать языки запросов, позволяющих пользователю получать информацию из баз данных. Языки запросов имеют свой особый син- таксис, который должен соблюдаться, как и в традиционных ЯП третье- го поколения, но при этом проще в использовании. Среди языков запро- сов фактическим стандартом стал язык SQL (Structured Query Language). И, наконец, четвертым направлением развития являются языки па- раллельного программирования (модификация ЯВУ Fortran, языки Occam, SISAL, FP и др.), которые ориентированы на создание про- граммного обеспечения для вычислительных средств параллельной ар- хитектуры (многомашинные, мультипроцессорные среды и др.), в отли- чие от языков третьего поколения, ориентированных на традиционную однопроцессорную архитектуру. К интенсивно развивающемуся в настоящее время пятому поколе- нию относятся языки искусственного интеллекта, экспертных систем, баз знаний (InterLisp, ExpertLisp, IQLisp, SAIL и др.), а также естествен- ные языки, не требующие освоения какого-либо специального синтак- сиса (в настоящее время успешно используются естественные ЯП с ог- раниченными возможностями — Clout, Q&A, HAL и др.). Классификация ЯП. Изучение ЯП часто начинают с их классифика- ции. Определяющие факторы классификации обычно жестко не фиксиру- ются. Чтобы продемонстрировать характер типичной классификации, опи- шем наиболее часто применяемые факторы, дадим им условные названия и приведем примеры ЯП для каждой из классификационных групп (табл. 1.7). Элементы языков программирования могут рассматриваться на следующих уровнях: алфавит — совокупность символов, отображаемых на устройствах печати и экранах и/или вводимых с клавиатуры терминала. Обычно это набор символов Latin-I, с исключением управляющих символов. Иногда в это множество включаются неотображаемые символы, с указанием правил их записи (комбинирование в лексемы); лексика — совокупность правил образования цепочек символов (лексем), образующих идентификаторы (переменные и метки), операто- ры, операции и другие лексические компоненты языка. Сюда же вклю- чаются зарезервированные (запрещенные, ключевые) слова ЯП, предна- значенные для обозначения операторов, встроенных функций и пр. 43
Таблица 1.7. Классификация ЯП Фактор Характеристика Группы Примеры ЯП Уровень ЯП Степень близости ЯП к архитектуре компьютера Низкий Автокод, ассемблер Высокий Fortran, Pascal, ADA, Basic, С и др. ЯВУ Сверхвысокий Сетл Специали- зация ЯП Потенциальная или реальная об- ласть применения Общего назначе- ния (универ- сальные) Algol, PL/1, Simula, Basic, Pascal Специализиро- ванные Fortran (инженерные рассчеты), Cobol (коммерческие задачи), Refal, Lisp (символь- ная обработка), Modula, Ada (про- граммирование в реальном времени) Алгорит- мичность (процедур- ность) Возможность аб- страгироваться от деталей алгоритма решения задачи. Алгоритмичность тем выше, чем точнее приходится планировать поря- док выполняемых действий Процедурные Ассемблер, Fortran, Basic, Pascal, Ada Непроцедурные Prolog, Langin Иногда эквивалентные лексемы, в зависимости от ЯП, могут обо- значаться как одним символом алфавита, так и несколькими. Например, операция присваивания значения в Си обозначается как «=», а в языке Pascal — «:=». Операторные скобки в Си задаются символами «{» и «}», а в Pascal — BEGIN и END. Граница между лексикой и алфавитом, та- ким образом, является весьма условной, тем более что компилятор обычно на фазе лексического анализа заменяет распознанные ключевые слова внутренним кодом (например, BEGIN — 512, END — 513) и в дальнейшем рассматривает их как отдельные символы; синтаксис — совокупность правил образования языковых конст- рукций, или предложений ЯП — блоков, процедур, составных операто- ров, условных операторов, операторов цикла и пр. Особенностью син- таксиса является принцип вложенности (рекурсивность) правил по- 44
строения конструкций. Это значит, что элемент синтаксиса языка в своем определении прямо или косвенно в одной из его частей содержит сам себя. Например, в определении оператора цикла телом цикла явля- ется оператор, частным случаем которого является все тот же опера- тор цикла; семантика — смысловое содержание конструкций, предложений языка, семантический анализ — это проверка смысловой правильности конструкции. Например, если мы в выражении используем перемен- ную, то она должна быть определена ранее по тексту программы, а из этого определения может быть получен ее тип. Исходя из типа пере- менной, можно говорит о допустимости операции с данной перемен- ной. Семантические ошибки возникают при недопустимом использова- нии операций, массивов, функций, операторов и пр. Исходная программа, как правило, состоит из следующих частей (впервые эти требования были сформулированы в языке Cobol): раздел идентификации — область, содержащая наименование про- граммы, а также дополнительную информацию для программистов и/или пользователей; раздел связи — фрагмент текста, описывающий внешние перемен- ные, передаваемые вызывающей программой (если таковая имеется), т.е. ту часть исходных данных, которая обязательно поступает на вход программы при ее запуске. Эти переменные часто называют параметра- ми программы; раздел оборудования (среда) — описание типа ЭВМ, процессора, требований к оперативной и внешней памяти, существенных с точки зрения выполнимости программы. Дело в том, что даже среди семейства однотипных ЭВМ могут суще- ствовать отличия в наборе машинных инструкций (команд), средств про- граммирования ввода-вывода, кодированного представления данных, в связи с чем описание среды, приводимое в данном разделе оказывается необходимым транслятору с языка с точки зрения оптимизации выполне- ния или вообще оценки возможности создания рабочей программы; раздел данных — идентификация (декларация, объявление, описа- ние) переменных, используемых в программе, и их типов. Понятие типа позволяет осуществлять проверку данных на совместимость в операци- ях еще на этапе трансляции программы и отвергнуть недопустимые преобразования; раздел процедур — собственно программная часть, содержащая описание процессов обработки данных. Элементами процедуры явля- ются операторы и стандартные функции, входящие в состав соответст- вующего языка программирования. К типовым операторам управления вычислительным процессом от- носятся следующие: 45
• организация циклов (выполняемых до исчерпания списка или до достижения управляющей переменной заданного значения, или пока выполняются некоторые условия); • ветвление программы — выполнение альтернативных групп операторов при заданных условиях; • блоки операторов — группы, выполняемые как целое. Примечание: перечисленные операторы принято называть операторами структурного программирования, языки, в которых других средств управления программой нет, — языками структурного программирования; • операторы перехода — условная или безусловная передача управления на определенный оператор, снабженный меткой, или условный/безусловный выход из цикла или блока. Важным компонентом управляющих операторов обычно являются логические условия, сопоставляющие значения констант, функций, пе- ременных отношениями типа <3, и вырабатывающие реше- ние о продолжении цикла, переходе по ветви, выходе из блока и т.д. Несколько простых условий объединяются в составные посредством логических операций И, ИЛИ, НЕ. Операторы присваивания значений выполняют следующие функции: • пересылку значений переменных, констант, функций в прини- мающую переменную; • вычисление значений арифметической (числовой) переменной в рамках существующих в языке правил построения арифме- тических выражений (обычно включающих символы «( )» — скобки, «+» — сложение, «-» — вычитание, «/» — деление, «*» — умножение, «**» или «л» — возведение в степень); • вычисление значений строчной (символьной) переменной пу- тем соединения, пересечения, вычисления строк; • вычисление логических переменных в рамках правил образо- вания логических выражений (обычно включающих скобки и операции И, ИЛИ, НЕ). Аналогично могут быть выделены типичные группы функций: • стандартные алгебраические и арифметические — SIN, COS, SQRT, MIN, MAX и др.; • стандартные строчные — выделение, вставка, удаление под- строки и т.д.; • нестандартные функции — описание операций и форматов ввода-вывода данных; преобразование типов данных; описание операций над данными, специфичными для конкретной систе- мы программирования, ОС или типа ЭВМ. В табл. 1.8 дается обзор основных операторных возможностей ЯП: Pascal, Basic и С (Си). 46
Таблица 1.8. Основные структуры некоторых ЯП Элемент языка Pascal Basic с Идентификация, связь PROGRAM Имя (Параметры) МА1М(Парамет ры) Циклы for ...do re- peat...until while...do For ...Next Do...Loop While...Wend for() do... while 0 while () Выход из цикла во вне break Exit For Exit Do break Переход к заголовку цикла continue continue Блоки, составные операторы begin... end {} Ветвление if...then...else case If...Then...Else Select Case if ()...else switch Переход на метку goto Label GoTo Label goto Label Логические опера- ции (НЕ, И, ИЛИ) not, and, or not, and, or !&& !! Пересылка, при- сваивание ’= = = Арифметические операции +, -, *, /, div, mod +, *, /. V A, mod +, -, *, /, % Стандартные число- вые функции Abs, Sqrt, Sqr, Sin, Cos, LogN Abs, Sqr, Sin, Cos, Exp, Log В стандартном языке — нет Строчные функции Pos, Copy, Delete, Insert Left, Right, Instr, Mid strcat, strlin Ввод-вывод Read, Write, BlockRead, BlockWrite Print, Input, Write, Get, Put printf, scanf, get, put ' Комментарии (*текст*) {текст } // текст REM текст ‘текст /* текст */ Необходимо отметить, что конкретные ЯП могут не требовать наличия всех выше перечисленных разделов исходного модуля. В некоторых случаях описания переменных могут размещаться произвольно в тексте, или опуска- ются, при этом тип переменной определяется компилятором, исходя из сис- темы умолчаний; есть средства программирования, в которых тип перемен- ной задается в момент присвоения ей значения другой переменной или кон- станты и т.д. Существуют фрагменты описания данных, которые могут быть отнесены как к разделу данных, так и к разделу оборудования (указания на устройство, длину и формат записи, организацию файла и т.п.). Подпрограммы. При разработке программ на алгоритмических языках широко используется понятие подпрограммы. Подпрограмма — это средство, позволяющее многократно использовать в разных местах 47
основной программы один раз описанный фрагмент алгоритма. В большинстве ЯП не проводится концептуального различия меж- ду такими объектами, как программа и подпрограмма (процедура, функция). В связи с этим всякая подпрограмма может приобретать ие- рархическую структуру, включая в себя подчиненные (вызываемые) подпрограммы, процедуры и т.д., имеющие стандартный состав. Объявления (типы, переменные, константы), использующиеся лю- бой подпрограммой, относятся к одной из двух категорий — категории локальных объявлений и категории глобальных объявлений. Локальные объявления принадлежат подпрограмме, описаны внутри нее и могут использоваться только ею. Глобальные объявления принадлежат про- грамме в целом и доступны как самой программе, так и всем ее подпро- граммам. Обмен данными между основной программой и ее подпро- граммами обычно осуществляется посредством глобальных переменных. Если имя глобального объявления совпадает с именем локального, то внутри подпрограммы обычно объявление интерпретируется как ло- кальное, и все изменения, вносимые, например, в значение такой пере- менной, актуальны только в рамках подпрограммы. Формальные и фактические параметры. Объявление подпрограммы может содержать список параметров, которые называются формальными. Каждый параметр из списка формальных параметров является локальным по отношению к подпрограмме, для которой он объявлен. Это означает, что глобальные переменные, имена которых совпадают с именами формальных параметров, становятся недоступными для использования в подпрограмме. Все формальные параметры можно разбить на две категории: • параметры, вызываемые подпрограммой по своему значению (т.е. параметры, которые передают в подпрограмму свое зна- чение и не меняются в результате выполнения подпрограммы); • параметры, вызываемые подпрограммой по наименованию (т.е. параметры, которые становятся доступными для изменения внутри подпрограммы). Главное различие этих двух категорий — в механизме передачи па- раметров в подпрограмму. При вызове параметра по значению происхо- дит копирование памяти, занимаемой параметром, в стек1 и использо- 1 Во многих случаях программе требуется временно запомнить информацию, а затем считывать ее в обратном порядке. Эта проблема в ПК решена посредством реализа- ции стека LIFO («последний пришел — первый ушел»), называемого также стеком включения/извлечсния (stack — кипа, например, бумаг). Наиболее важное ис- пользование стека связано с процедурами. Стек обычно рассчитан на косвенную ад- ресацию через регистр SP — указатель стека. При включении элементов в стек про- изводится автоматический декремент указателя стека, а при извлечении — инкре- мент, т. е. стек всега «растет» в сторону меньших адресов памяти. Адрес последнего включенного в стек элемента называется вершиной стека (TOS). 48
вание в дальнейшем в операторах подпрограммы локальной копии па- раметра. Основное значение параметра (глобальное по отношению к подпрограмме) при этом остается без изменения. Следует отметить, что использование такого механизма при передаче, например, массивов большой длины может отрицательно влиять на быстродействие про- граммы и заполняет стек лишней информацией. При вызове параметра по наименованию в подпрограмму передает- ся адрес памяти (глобальной по отношению к подпрограмме), в которой размещено значение параметра, т. е. в качестве локальной переменной выступает ссылка на глобальное размещение параметра, обеспечиваю- щая доступ к самому значению. При обращении к подпрограмме формальные параметры заменяют- ся на соответствующие по типу и категории фактические параметры вызывающей программы или подпрограммы. Способы описание языков программирования. Для описания язы- ков программирования в данной книге используются две системы описания: нотация Бэкуса (впервые предложена при описании языка ALGOL); нотация IBM (разработана фирмой для описания языков COBOL и JCL); нотация Бокуса (использована ниже для описания ЯП Паскаль, ко- торый является прямым потомком Алгола) содержит конструкции сле- дующего вида: «Оператор присваивания» ::= «Переменная» := «Выражение» «Идентификатор» ::= <Буква»|«ИдентификаторхБуква>| «ИдентификаторхЦифра» Левая часть определения конструкции языка содержит наименова- ние определяемого элемента, взятого в угловые скобки. Правая часть включает совокупность элементов, соединенных зна- ком |, который трактуется как «или» и объединяет альтернативы — раз- личные варианты значения определяемого элемента. Части соединяются оператором ::=, который может трактоваться как есть по определению. Существенно, что многие определения носят рекурсивный ха- рактер, т.е. левая часть содержит правую (как для элемента «Иден- тификатор» в вышеприведенных примерах), и вместо элементов в правой части определения можно подставлять любые их значения. Например, в определении идентификатора, начиная рассматривать его как букву А, можно затем получать любые комбинации: АА, АО, АА1, АА1В и т.п. Нотация IBM (используется ниже при описании конструкций ЯП С и Basic) включает следующие конструкции: 49
< > угловые скобки (или двойные кавычки " ")обозначают элемен- ты программы, определяемые пользователем (<идентификатор>, «спи- сок параметров> <условие> и пр. В соответствующих местах реальной программы будет находиться идентификатор переменной и т.д.); [ ] квадратные скобки — ограничивающие синтаксическую конст- рукцию, обозначают ее возможное отсутствие. Например, return ^вы- ражение^; В этой конструкции <выражение> не обязательно; | — вертикальная черта разделяет список значений обязательных элементов, одно из которых должно быть выбрано; ... — горизонтальное многоточие, следующее после некоторой синтаксической конструкции, обозначает последовательность конст- рукций той же самой формы, что и предшествующая многоточию кон- струкция. Например, ={<выражение> [,<выражение>]...} обозначает, что одно или более выражений, разделенных запятыми, может появить- ся между фигурными скобками. Например: [Private | Public | Friend] [Static] Sub «идентификатор» [(«список параметров»)] [«оператор»] [Exit Sub] [«оператор»] End Sub Рекурсивные определения в IBM-нотации не используются. Вопросы 1. Охарактеризуйте факторы классификации языков программирования. 2. Перечислите основные группы операторов языков программирования. 3. Опишите операцию ветвления с помощью нотации Бэкуса и с помощью IBM-нотации. 4. Опишите оператор цикла с использованием нотации Бэкуса и IBM-нотации. 5. Опишите оператор присваивания с использованием нотации Бэкуса. 6. Дайте определение арифметической операции для языков Pascal, Basic и С с использованием нотации Бэкуса. 7. Дайте определение логической операции для языков Pascal, Basic и С с использованием нотации Бэкуса. 8. Охарактеризуйте разницу между формальными и фактическими парамет- рами подпрограммы. 9. Охарактеризуйте разницу между вызовом параметров подпрограммы по значению и по наименованию. Приведите примеры. 10. Охарактеризуйте возможные параметры подпрограмм вычисления сле- дующих функций: cos х; е*; 2itR; log„ х. 50
И 1.5. Системы программирования Рассмотренные выше средства являются важными функциональ- ными компонентами соответствующей системы программирования, т.е. среды окружения программиста, позволяющей ему разрабатывать при- кладные программы (программировать приложения, разрабатывать при- ложения) для соответствующих ЭВМ и операционных систем. Система программирования представляет собой совокупность средств разработки программ (языки программирования, текстовые ре- дакторы, трансляторы, редакторы связей, библиотеки подпрограмм, утилиты и обслуживающие программы), обеспечивающих автоматиза- цию составления и отладки программ пользователя. Таблица 1.9. Классы систем программирования (СП) Признак классификации Типы Набор исходных языков Одноязыковые Многоязыковые Возможности расширения Замкнутые Открытые Трансляция Компиляция Интерпретация Системы программирования классифицируются по признакам, приведенным в табл. 1.9. Следует отметить, что: отличительной особенностью многоязыковых систем является то, что отдельные части (секции, модули или сегменты) программы могут быть подготовлены на различных языках и объединены во время или перед выполнением в единый модуль; в открытую систему можно ввести новый входной язык с трансля- тором, не требуя изменений в системе; в интерпретирующей системе осуществляется покомандная рас- шифровка и выполнение инструкций входного языка (в среде данной системы программирования); в компилирующей — подготовка резуль- тирующего модуля, который может выполняться на ЭВМ практически независимо от среды. Рассмотрим структуру абстрактной многоязыковой, открытой, компилирующей системы программирования и процесс разработки приложений в данной среде (рис. 1.7). Ввод. Программа на исходном языке (исходный модуль) готовится с помощью текстовых редакторов и в виде текстового файла или разде- ла библиотеки поступает на вход транслятора. 51
Рис. 1.7. Схема разработки прикладных программ в среде системы программирования 52
Трансляция. Трансляция исходной программы есть процедура пре- образования исходного модуля в промежуточную, так называемую объ- ектную форму. Трансляция в общем случае включает в себя препроцес- синг (предобработку) и компиляцию. Препроцессинг — необязательная фаза, состоящая в анализе ис- ходного текста, извлечения из него директив препроцессора и их вы- полнения. Директивы препроцессора представляют собой помеченные спец- символами (обычно %, #, &) строки, содержащие аббревиатуры или другие символические обозначения конструкций, включаемых в состав исходной программы перед ее обработкой компилятором. Данные для расширения исходного текста могут быть стандартны- ми, определяться пользователем либо содержаться в системных библио- теках ОС. Компиляция — в общем случае многоступенчатый процесс, вклю- чающий следующие фазы: синтаксический анализ — проверка правильности конструкций, ис- пользованных программистом при подготовке текста; семантический анализ — выявление несоответствий типов и струк- тур переменных, функций и процедур; генерация объектного кода — завершающая фаза трансляции. Выполнение трансляции (компиляции) может осуществляться в различных режимах, установка которых производится с помощью клю- чей, параметров или опций. Может быть, например, потребовано только выполнение фазы синтаксического анализа и т.п. Объектный модуль представляет собой текст программы на ма- шинном языке, включающий машинные инструкции, словари, служеб- ную информацию. Объектный модуль не работоспособен, поскольку содержит неразре- шенные ссылки на вызываемые подпрограммы библиотеки транслятора (в общем случае — системы программирования), реализующие функции вво- да-вывода, обработки числовых и строчных переменных, а также на другие программы пользователей или средства пакетов прикладных программ. Построение исполнительного модуля. Построение загрузочного модуля осуществляется специальными программными средствами — редактором связей, построителем задач, компоновщиком, основной функцией которых является объединение объектных и загрузочных мо- дулей в единый загрузочный модуль с последующей записью в библио- теку или файл. Полученный модуль в дальнейшем может использовать- ся для сборки других программ и т.д., что создает возможность наращи- вания программного обеспечения. Загрузка программы. Загрузочный модуль после сборки либо по- мещается в качестве раздела в пользовательскую библиотеку программ, 53
либо в качестве последовательного файла на накопителе на магнитном диске (НМД). Выполнение модуля состоит в загрузке его в оперативную память, настройке по месту в памяти и передаче ему управления. Образ загрузочного модуля в памяти называется абсолютным модулем, по- скольку все команды ЭВМ здесь приобретают окончательную форму и получают абсолютные адреса в памяти. Формирование абсолютного модуля может осуществляться как программно, путем обработки ко- мандных кодов модуля программой-загрузчиком, так и аппаратно, пу- тем применения индексирования и базирования команд загрузочного модуля и приведения указанных в них относительных адресов к абсо- лютной форме. Современные системы программирования позволяют удобно пере- ходить от одного этапа к другому. Это осуществляется в рамках так на- зываемой интегрированной среды программирования, которая содержит в себе текстовый редактор, компилятор, компоновщик, встроенный от- ладчик и, в зависимости от системы или ее версии, предоставляет про- граммисту дополнительные удобства для написания и отладки про- грамм. Библиотеки подпрограмм. В предыдущем разделе определено по- нятие подпрограммы как средства многократного использования одного и того же фрагмента алгоритма в разных местах основной программы (например, вычисление одной и той же арифметической функции при разных значениях аргумента). Однако довольно распространенной является и такая ситуация, ко- гда один и тот же алгоритм — вычисление значений элементарных функций, перевод чисел из одной системы в другую, преобразование символьной информации к строчному или заглавному представлению и т.д. — используется при решении самых разных задач. Если один из подобных алгоритмов уже был один раз разработан и реализован при решении некоторой задачи, то возникает естественное желание исполь- зовать уже готовую подпрограмму как часть любой другой программы. Таким образом, организовав сбор, хранение и удобное использование готовых подпрограмм, можно существенно упростить и ускорить разра- ботку программ различного назначения, записывая лишь обращения к готовым подпрограммам и заново проектируя уже гораздо меньшую часть кода. В процессе использования готовых подпрограмм возникает ряд проблем, связанных, с одной стороны, с хранением имеющихся подпро- грамм и размещением используемых подпрограмм в памяти ЭВМ, и с другой стороны — с организацией их взаимодействия с основной про- граммой. В связи с этим для обеспечения удобства практической работы выбирается определенная система использования подпрограмм, в кото- 54
рой тем или иным образом эти проблемы решены. Такая система всегда предъявляет определенные требования к подпрограммам с точки зрения их организации и оформления. Подпрограммы, которые удовлетворяют всем требованиям выбранной системы, называются стандартными, а организованный соответствующим образом набор таких подпрограмм называется библиотекой подпрограмм. С точки зрения компоновки и последующего взаимодействия с основным программным кодом библиотеки подпрограмм делятся на библиотеки статического вызова (статические библиотеки) и биб- лиотеки динамического вызова (динамические библиотеки). Охарак- теризуем разницу между статической и динамической компоновкой подпрограмм. В любом случае, когда подпрограмма оказывается не- доступной напрямую в исходном файле, компилятор добавляет эту подпрограмму в специальную внутреннюю таблицу, содержащую все внешние идентификаторы. Очевидно, что при этом компилятор дол- жен иметь в своем распоряжении объявление подпрограммы и ин- формацию о ее параметрах. После компиляции подпрограммы статической библиотеки ком- поновщик добавляет ее откомпилированный код к исполняемой про- грамме. Получившийся в результате исполнительный модуль содержит код программы и всех используемых подпрограмм. В случае динамической компоновки компоновщик просто исполь- зует информацию о подпрограмме для настройки соответствующих таблиц в исполняемом файле. Когда исполняемый модуль загружается в память, операционная система загружает также все необходимые дина- мические библиотеки и заполняет внутренние таблицы программы ад- ресами библиотечных подпрограмм в памяти, после чего программа запускается на исполнение. Схема вызова подпрограмм статической и динамической библиотек изображена на рис. 1.8. К преимуществам использования динамических библиотек можно отнести следующие: во-первых, если разные программы используют одну и ту же дина- мическую библиотеку, то библиотека загружается в память только один раз (в отличие от статической компоновки, при которой каждая отдель- ная программа содержит внутри себя коды всех используемых подпро- грамм). Таким образом, экономится системная память; во-вторых, при модификации динамической библиотеки можно просто заменить старую версию новой, не перестраивая при этом гото- вые программы (если, конечно, изменения не коснулись параметров вызова используемых программами подпрограмм). Эти общие преимущества оказываются полезными в следующих случаях: 55
Статическая библиотека Исходный код Рнс. 1.8. Компиляция статической и динамической библиотек • если несколько программ используют один и тот же сложный алгоритм: сохранение его в динамической библиотеке умень- шит размер исполнительного модуля и сэкономит память, ко- гда несколько программ будут запущены одновременно; • если необходимо постоянно вносить изменения в программу большого объема: разделение программы на исполняемую часть и динамическую библиотеку позволит модифицировать и затем распространять только измененную часть программы. Обработка исключительных ситуаций. Для обеспечения надеж- ности прикладных программ операционные системы предоставляют программисту специальные средства обработки исключительных ситуа- ций — аппарат обнаружения и обработки ошибок и граничных состоя- ний. Наличие подобных средств позволяет при написании программно- го кода сосредоточиться на решении основной задачи и отделить основ- ную работу от рутинной, хотя и необходимой, обработки ошибок. Рассмотрим действие такого аппарата на примере структурной об- работки исключений (SEH — Structured Exeption Handling), введенной фирмой Microsoft в операционную систему Win32. Основная нагрузка при поддержке SEH ложится не на операцион- ную систему, а на компилятор, который должен сгенерировать спе- циальный код на входах и выходах так называемых блоков исключений и 56
создать таблицы вспомогательных структур данных для поддержки SEH. Разные фирмы по-разному реализуют SEH в своих компиляторах, но большинство фирм-разработчиков компиляторов придерживается синтаксиса, рекомендованного Microsoft. SEH предоставляет две основные возможности: обработку завер- шения и обработку исключений. Обрабока завершения гарантирует, что некоторый блок кода (обра- ботчик завершения) будет исполнен независимо от того, каким образом произойдет выход из другого блока кода (защищенного блока). Общий синтаксис использования обработки завершения в прикладных про- граммах следующий: txy оащищенный блок> finally собработчик завершения> Ключевые слова try и finally обозначают два программных блока обработки завершения. Благодаря совместным действиям операционной системы и компилятора гарантируется, что код блока finally будет ис- полнен независимо от того, каким образом произойдет выход из защи- щенного блока. Порядок исполнения кода при этом следующий: 1. Исполняется код перед блоком try. 2. Исполняется код внутри блока try (защищенный блок). 3. Исполняется код внутри блока finally (обработчик завершения). 4. Исполняется код после блока finally. Рассмотрим пример: пусть необходимо разместить в оперативной памяти и заполнить некоторыми значениями массив целых чисел, про- вести обработку элементов массива (например, каждый элемент с чет- ным номером возвести в квадрат и посчитать сумму элементов) и осво- бодить оперативную память. Поместив обработку элементов массива в блок try, а освобождение памяти — в блок finally, можно таким образом обеспечить освобождение памяти при любом исходе — даже если в процессе выполнения алгоритма обработки массива произошла какая- нибудь ошибка. Схема решения такой задачи следующая: <выделение памяти для размещения массива> оаполнение элементов массива> txy <выполнение алгоритма обработки> <вывод результата> finally сосвобождение занятой памяти> Обработка исключений. Исключение — это не предполагаемое алгоритмом событие. В хорошо написанной программе обычно не 57
должно быть обращений по недопустимому адресу памяти или деления на нуль, но все же такие ошибки иногда случаются. За перехват попы- ток обращения по недопустимому адресу и деления на нуль отвечает центральный процессор, возбуждающий исключения в ответ на такие ошибки. Исключение, возбуждаемое процессором, называется аппа- ратным исключением. Операционная система и прикладная программа способны также вызывать и программные исключения. При возбуждении аппаратного или программного исключения опе- рационная система дает возможность прикладной программе опреде- лить тип исключения и самостоятельно его обработать. Синтаксис об- работки исключений следующий: try оащищенный блок> except <фильтр исключению- собработчик исключений> Ключевое слово except отделяет защищенный блок от блока обра- ботки исключений. Фильтр исключений предназначен для задания зна- чений, определяющих типы исключительных ситуаций, возникновение которых предполагается обрабатывать в блоке исключений. В отличие от обработчиков завершений, фильтры и обработчики исключений выполняются непосредственно операционной системой, нагрузка на компилятор при этом незначительна. Блок обработки исключений начинает выполняться тогда, когда возникла исключительная ситуация при выполнении защищенного бло- ка. После выполнения блока исключений управление передается на блок, следующий сразу за блоком исключений: 1. Исполняется код перед блоком try. 2. Исполняется код внутри блока try (защищенный блок). Если возникла исключительная ситуация, то переходим к пункту 3, иначе переходим к пункту 4. 3. Исполняется код внутри блока except (обработчик исключений). 4. Исполняется код после блока except. Из приведенной последовательности действий видно, что если при исполнении защищенного блока не произошло ошибки, то блок except не выполняется. В случае же возникновения ошибки внутри защищен- ного блока управление передается в обработчик исключений, а после обработки исключительной ситуации не возвращается в защищенный блок. Поясним это следующими примерами. Пример 1. Пусть защищенный блок содержит одно действие — присваивает целочисленной переменной X значение 0. Тогда предпо- сылок для возникновения ошибочной ситуации в защищенном блоке нет, и блок исключений никогда не выполняется: 58
try X = О except <фильтр исключению Сообщение: «Обработка ошибок» — этот код никогда не выполня- ется Пример 2. Пусть целочисленной переменной X присваивается зна- чение 0 перед входом в защищенный блок, а внутри защищенного блока выполняется последовательность из двух действий: У = 10/Х и X = X + 5. Тогда получаем следующую ситуацию: X = о try Y = 10/Х — возбуждается исключение X = X + 5 —этот код никогда не выполняется except <фильтр исключению Сообщение: «Деление на 0» — обработка исключения При программировании обработки исключений необходимо пом- нить, что за блоком try всегда должен следовать либо блок finally, либо блок except. Для одного блока try нельзя определить одновременно и блок finally, и блок except. Нельзя также и указать несколько блоков finally или except для одного блока try. Однако блоки try-except и try- finally могут вкладываться друг в друга, т.е. возможны следующие спо- собы использования: try try except <фильтр исключению finally или try finally try except <фильтр исключений Синтаксис использования аппарата SEH в конкретной системе про- граммирования будет рассмотрен в гл. 3. 59
Вопросы 1. Чем отличается объектный модуль от абсолютного? 2. Чем отличается объектный модуль от загрузочного? 3. Чем отличается компилирующая система трансляции от интерпретирую- щей? 4. Охарактеризуйте признаки классификации систем программирования. 5. Охарактеризуйте структуру абстрактной системы программирования. 6. В чем преимущества использования библиотек подпрограмм? 7. Приведите примеры алгоритмов, использующих фрагменты действий, ко- торые могут быть оформлены в виде библиотечных подпрограмм. 8. Охарактеризуйте разницу между использованием библиотеки статического вызова и библиотеки динамического вызова. 9. Каково назначение аппарата обработки исключений? 10. В чем разница в выполнении блоков try-except и try-finallyl 1.6. Файлы данных Ввод-вывод данных в языках программирования осуществляется путем взаимодействия программы с внешними файлами. Внешний файл — это поименованный файл на диске или устройство ввода-вывода (на- пример, клавиатура или дисплей). Во внешних файлах сохраняются ре- зультаты работы программы и располагаются данные, служащие источ- ником информации, необходимой для ее функционирования. Файл можно определить как логически непрерывный именованный набор данных на внешнем носителе. Понятие файла появляется впервые в операционной системе OS/360 фирмы IBM, причем в ранних версиях системы «настоящим файлом» считался только перфокарточный массив (file = картотека), данные на МД и МЛ обозначались как DS (Data Set — набор данных). В последующих ОС (RSX, UNIX, MS DOS) файлами становятся имено- ванные организованные наборы данных на любых носителях и устрой- ствах, за сохранность и обновляемость которых (а также передачу в прикладные программы / из прикладных программ) несет ответствен- ность ОС ЭВМ. Связь программы с внешними файлами осуществляется через спе- циальные переменные. Подробное описание установления связи про- граммных переменных с файлами было впервые сделано в ЯП Cobol (Common Business Oriented Language). Эта проблема была решена сле- дующим образом. Цикл обработки файла (например, внесение изменений в списки информации о студентах) включает следующие операции: 60
открытие файла — занятие устройства, на котором файл раз- мещен (например, МД), создание в ОП управляющего блока, в кото- ром записывается справка о состоянии файла, и буфера (или набора буферов — буферного пула) для хранения текущей, обрабатываемой записи файла; организация цикла, управляемого файлом (заканчивается по исчер- пании записей файла — наступлении состояния EOF — end-of-file). Цикл должен содержать команду типа READ, GET (ввод записи) или PUT, WRITE (вывод записи), либо REWRITE (обновить запись); закрытие файла — выполнение операций по внесению всех окон- чательных изменений в файл и его реквизиты, освобождение памяти, отведенной под файл, и устройства, на котором он размещался. Устройства, на которые осуществляется вывод данных из програм- мы, или с которых осуществляется ввод (это может быть одно и то же физическое устройство, как это было в случае ранних терминалов; в современных, так называемых ANSI-терминалах — монитор и клавиа- тура рассматриваются как два отдельных, независимых устройства) -— могут быть подразделены на следующие типы: • передача информации битовым потоком; • посимвольный обмен информацией; • передача информации порциями (записями). Фактически это как бы «портрет» устройства, каким его «видит» прикладная программа через посредство драйвера устройства и про- грамм операционной системы, ответственных за ввод-вывод информа- ции. Одно и то же устройство может быть представлено как генератор потока символов (потокоориентированное устройство) или записей (за- писеориентированное). Поэтому скорее стоит говорить о типе файлов, расположенных на том или ином устройстве. Могут быть рассмотрены следующие типы файлов: по типу записей: • файлы с записями фиксированной длины; • файлы с записями переменной или неопределенной длины; • файлы, образующие байтовый или битовый поток; по способу выборки информации: • файлы последовательного доступа; • файлы прямого доступа. Для всех этих типов файлов возникает проблема идентификации данных, размещенных на носителе (в файле). Каким образом можно правильно сопоставить тем или иным битовым комбинациям, разме- щенным в файле, те или иные области оперативной памяти, куда они должны считываться с внешнего носителя для последующей обработки или обновления? 61
Метод сопоставления, по-видимому, должен выполнять следующие функции: - «привязывание» данных, размещенных на внешнем носителе (в файле) к тем областям памяти, которые выделены для размещения этих данных (им соответствуют какие-то идентификаторы переменных в об- рабатывающей программе); - обнаружение и обработка ошибок при считывании данных (на- пример, несоответствие типа или длины данного ожидаемому и т.п.). Файлы последовательного доступа. Рассмотрим вначале файлы последовательного доступа. Для таких файлов характерны операции последовательного чтения и записи в конец файла. При считывании ин- формации из файла (эта функция может быть возложена как на опера- ционную систему, так и на пользовательскую программу или библио- течную процедуру) необходимо уметь: - определять начало и окончание элементарного данного; - определять начало и окончание записи файла. Здесь необходимо отдельно рассмотреть записи фиксированной длины и записи переменной (неопределенной) длины. В случае работы с записями фиксированной длины данные на носи- теле (в файле) должны иметь строго ту длину, которая задана в их опи- сании (в прикладной программе). Для таких записей в буфере считывания выделяется область, равная общей длине записи. Всякое нарушение длины и типа приводит к ошиб- ке считывания. В случае работы с записями переменной длины они должны быть отделены друг от друга разделителями (ограничителями) записей. Сле- дует отметить, что такой подход действителен для записей как перемен- ной, так и фиксированной длины. Возможен также и другой метод идентификации записи: например, первый байт каждой записи может содержать информацию о длине всей записи. Записи неопределенной длины возникают тогда, когда ограничите- лем является физическая метка, распознаваемая устройством или фай- ловой системой ОС. Примером файлов неопределенной длины являются текстовые файлы с признаком «возврат каретки — перевод строки» в конце каждой строки. Файлы прямого доступа. Для файлов прямого доступа характерны операции чтения и записи по произвольному адресу. Такие файлы раз- мещаются на дисках, как устройствах прямого доступа (в истории вы- числительной техники, однако, существовали и магнитные ленты пря- мого доступа для отечественной ЭВМ МИНСК-22(М)). Наипростейшим способом быстрого нахождения необходимой за- писи по ее номеру в файле является использование записей с фиксиро- 62
ванной длиной. Тогда физический адрес легко вычисляется как некото- рое смещение относительно начала файла, пропорциональное номеру выбираемой записи, например: если в файле Fразмещено Дозаписей длиной I байт, то: • общая длина всего файла — Length(F) = N-1 байт; • смещение z-й записи от начала файла—Adr(i) = I • (z - 1) байт. Все вышеперечисленное относится не только к файлам, ориен- тированным на записи, но и к потоковым файлам. Во многих языках программирования существует понятие двоичного файла, структура которого не ограничена байтами, записями, разделителями и др. Та- кой файл может интерпретироваться как очень большая битовая строка, любая часть которой может быть передана в пользователь- скую программу (и наоборот) путем указания смещения и длины та- кой «подстроки» в соответствующих функциях или командах обмена с внешними устройствами. Естественно, полную ответственность за обработку этих файлов несет пользовательская программа (а точнее, ее автор). Понятия записей (фиксированной или переменной длины, мето- да записи и пр.) возникают тогда, когда часть подобной ответствен- ности берет на себя операционная система ЭВМ и/или библиотечные программы соответствующей системы программирования (именно поэтому обычно различаются команды и функции доступа к файлам в различных ЯП). Примеры и решения 1. Пусть необходимо подсчитать количество слов в текстовом фай- ле, разделенном на строки с помощью последовательности символов «перевод каретки — возврат строки» (без использования переноса слов). Для решения такой задачи следует: • открыть файл на чтение; • обеспечить циклическое чтение строк файла в программный буфер; • к программному буферу применить подпрограмму подсчета количества слов в очередной строке (алгоритм подпрограммы см. в п.2 данной главы), накапливая в цикле общее количество слов в файле; • закрыть файл и вывести результат. Ниже приведена блок-схема алгоритма (N — количество слов в файле, I — количество слов в очередной строке). 63
Начало 2. Пусть в файле прямого доступа, содержащем записи длиной L, необходимо поменять местами пятую и шестую записи. Порядок реше- ния задачи следующий: • открыть файл на чтение-запись; • проверить, содержатся ли в файле записи с номером 5 и 6 (т.е. длина файла должна быть больше либо равна 6 • L); • определить позицию пятой записи и установить на нее указа- тель; • прочитать пятую запись и поместить ее в переменную Record 1; • прочитать следующую (шестую) запись и поместить ее в пере- менную Record2; • установить указатель в файле на пятую запись; • записать содержимое Record2; 64
• записать содержимое Record 1; • закрыть файл. Приведем блок-схему алгоритма (FL — длина файла, L — длина записи): Вопросы и задания 1. Охарактеризуйте основные типы файлов. 2. Сформулируйте, каковы достоинства и недостатки файлов с фиксирован- ной и переменной длиной записи. 65
3. Опишите физический состав файла, состоящего из записей фиксированной длины, содержащих «Информацию о студенте» (см. п. 1.2). 4. Разработайте алгоритм организации доступа к файлу, содержащему записи «Информация о студенте»: • добавление записи в конец файла; • поиск записи по фамилии; • редактирование найденной записи; • удаление найденной записи. 1.7. Объектно-ориентированный подход к программированию В истории применения компьютеров вычислительная техника все время используется на пределе своих возможностей. Каждое новое дос- тижение в аппаратном либо в программном обеспечении приводит к попыткам расширить сферу применения ЭВМ, что влечет за собой по- становку новых задач, для решения которых, в свою очередь, нужны новые вычислительные средства. Основа для массового промышленного программирования была создана с разработкой методов построения программ. Одной из первых и наиболее широко применяемых технологий программирования стало структурное программирование. Этот метод до сих пор не потерял сво- его значения для определенного класса задач. Структурный подход базируется на двух основополагающих прин- ципах: • использование процедурного стиля программирования; • последовательная декомпозиция алгоритма решения задачи сверху вниз. В соответствии с этим подходом задача решается путем выполне- ния следующей последовательности действий. Первоначально задача формулируется в терминах ввода данных — вывода результата: на вход программы подаются некоторые данные, программа работает и выдает ответ (рис. 1.9). После этого начинается последовательное разложение всей задачи на отдельные более простые действия. При этом на любом этапе декомпозиции программу можно проверить, применяя механизм так называемых «заглушек» — процедур, имитирующих вход и/или вы- ход процедур нижнего уровня. «Заглушки» позволяют проверить логику верхнего уровня до реализации следующего, т.е. на каждом шаге разра- ботки программы можно иметь работающий каркас, который постепен- но обрастает деталями. 66
Начало Обработка Вывод результата Q Выход Рис. 1.9. Верхний уровень структурного подхода Структурное программирование ясно определило значение мо- дульного построения программ (т.е. разбиения монолитных программ на группу отдельных модулей) при разработке больших проектов, но в языках программирования единственным способом структуризации программы оставалось составление ее из подпрограмм и функций. Объектно-ориентированное программирование родилось и получи- ло широкое распространение именно благодаря попыткам разрешения следующих проблем, возникавших в процессе проектирования и разра- ботки программных комплексов. 1. Развитие языков и методов программирования не успевало за все более растущими потребностями в прикладных программах. Единственным реальным способом снизить временные затраты на разработку был метод многократного использования разработанного программного обеспечения, т.е. проектирование новой программной системы на базе разработанных и отлаженных ранее модулей, кото- рые выступают в роли своеобразных «кирпичиков», ложащихся в фундамент новой разработки. 2. Ускорение разработки программного обеспечения требовало решения проблемы упрощения их сопровождения и модификации. 3. Не все задачи поддаются алгоритмическому описанию по тре- бованиям структурного программирования, поэтому в целях упрощения 67
процесса проектирования необходимо было решить проблему прибли- жения структуры программы к структуре решаемой задачи. Решение перечисленных проблем в рамках создания объектно- ориентированного подхода к программированию и породило три его основных достоинства: упрощение процесса проектирования программ- ных систем, легкость их сопровождения и модификации и минимизиро- вание времени разработки за счет многократного использования гото- вых модулей. Понятие объекта. В отличие от процедурного подхода к програм- мированию, когда описание алгоритма представляет собой последова- тельность действий, объектно-ориентированный подход предлагает описывать программные системы в виде взаимодействия объектов. Рассмотрим распространенный пример построения изображения «звездного неба» в двумерной проекции. Задача состоит в размещении и перемещении некоторого количества мерцающих разноцветных точек- звезд и кругов-планет на плоскости экрана. Пусть звезда в нашем определении — это точка на экране с коор- динатами X, Y , которая имеет цвет, может быть видимой или невиди- мой и может перемещаться по «звездному небу». Планета же — это цветной круг с координатами центра в точке X, Y, который тоже может быть видимым или невидимым и должен уметь перемещаться. Все размещаемые звезды (и планеты) в нашем примере однотипны и могут представляться одинаковыми свойствами и процедурами обра- ботки, т.е. для решения задачи необходимо создать так называемые объ- екты типа «Звезда» и «Планета». Таким образом, объект — это поня- тие, сочетающее в себе совокупность данных и действий над ними. Свойства — это характеристики состояния объекта, а действия над данными объекта называются методами. Наследование. Очевидно, что основой изображения любого эле- мента является его положение (Позиция) на экране (например, значения координат X и Y относительно левого верхнего угла экрана). Опишем объект «Позиция» с координатами X и Y и методом «Назначить коор- динаты»: Позиция(X , Y, НазначитьХУ) Рассмотрим теперь объект «Звезда», который по нашему определе- нию может быть описан следующим образом: Звезда(X , У, Видимость, Цвет, НазначитьХУ, Назначить цвет, Зажечь, Погасить, Переместить) Обратим внимание на то, что возможности объекта Позиция полно- стью входят в объект Звезда. Такое свойство объектов называется на- 68
следованием и позволяет описать объект Звезда с учетом наследования возможностей объекта Позиция'. Звезда(Позиция, Видимость, Цвет, Назначить цвет, За- жечь, Погасить, Переместить) Наследование позволяет повторно использовать уже созданную часть программного кода в других проектах. Посредством наследования формируются связи между объектами, а для выражения процесса насле- дования используют понятия «родители» и «потомки». В программиро- вании наследование служит для сокращения избыточности кода, и суть его заключается в том, что уже существующий интерфейс вместе с его программной частью можно использовать для других объектов. При наследовании могут также проводиться изменения интерфейсов. Инкапсуляция. В дальнейшем при работе с объектом Звезда дос- тупны все возможности объекта Позиция, причем методы объекта Звез- да могут использовать данные родительского объекта {Позиция). Объе- динение в одном месте всех данных и методов объекта (включая данные и методы объектов-предков) называется инкапсуляцией и облегчает по- нимание работы программы, а также и ее отладку и модификацию, так как только в очень редких случаях разработчика интересует внутренняя реализация объектов — главное, чтобы объект обеспечивал функции, которые он должен предоставить. Взаимодействие с объектом происходит через интерфейс. Обычно интерфейс определяет единственный способ входа в объект и выхода из него; детали реализации остаются инкапсулированными. При создании собственных объектов необходимо организовать такие же интерфейсы. В объектах интерфейсами являются свойства, методы и события. Толь- ко они предоставляются данным объектом в распоряжение других объ- ектов. Благодаря этому предотвращается доступ других объектов к внутренним переменным состояния объекта, которые могут обрабаты- ваться только предусмотренными для этого процедурами. Таким обра- зом, инкапсуляция обеспечивает использование объекта, не зная, как он реализован внутри. Полиморфизм. Перейдем к описанию объекта Планета. Очевидно, что этот объект должен наследовать все возможности объекта Звезда, но методы Назначить цвет, Зажечь, Погасить, Переместить, по резуль- тату действия являясь теми же самыми, фактически не могут быть реа- лизованы одинаковой последовательностью команд в случае со Звездой и Планетой. Эта ситуация разрешается следующим образом: методы объектов имеют одинаковое имя (и, соответственно, одинаковый ожи- даемый результат), а внутреннее описание методов будет различно для 69
разных объектов. Такое свойство объектов называется полиморфизмом, а объект Планета имеет следующее описание: Планета(Звезда, Радиус, Назначить цвет. Зажечь, Пога- сить, Переместить) Полиморфизм основывается на возможности включения в данные объекта также и информации о методах обработки этих данных. При этом различные объекты используют одинаковую абстракцию, т.е. мо- гут обладать свойствами и методами с одинаковыми именами. Однако обращение к ним будет вызывать различную реакцию для различных объектов. Большое достоинство полиморфизма состоит в том, что при использовании объекта можно вызывать определенное свойство или метод, не заботясь о том, как объект выполняет задачу. Классы и объекты. В предыдущем примере на самом деле описа- ны множества однотипных объектов (Позиция, Звезда, Планета), кото- рые обладают одинаковыми возможностями, т. е. Позиция, Звезда и Планета представляют целые классы объектов. Класс и объект — два общепринятых термина. Какова же разница между ними? Термин класс объединяет объекты с одинаковыми возможностями (данными и методами). Он описывает общее поведение и характеристи- ки набора аналогичных друг другу объектов. Объект — это экземпляр класса или, другими словами, переменная, тип которой задается клас- сом. Объекты в отличие от классов реальны, т. е. существуют и хранят- ся в памяти во время выполнения программы. Соотношения между объ- ектом и классом аналогичны соотношениям между переменной и типом. Компоненты. Использование библиотек классов повышает скорость разработки программ, но, с другой стороны, требует определенных усилий для изучения этих библиотек и понимания того, как они устроены. Кроме того, библиотека классов должна быть написана на том же языке програм- мирования, что и разрабатываемая программа. Конечно, существуют спо- собы сопряжения разных языков программирования, но все равно, для того чтобы использовать, например, для программы, написанной на языке Pascal, библиотеку классов C++, необходимо написать программу с вызо- вами нужных функций или порождением необходимых классов. Подобные неудобства привели к появлению концепции компонен- та — программного модуля или объекта, который готов для использо- вания в качестве составного блока программы и которым можно визу- ально манипулировать во время разработки программы. Компонент — это объект, объединяющий состояние и интерфейс (способ взаимодействия). Состояние компонента может быть изменено только с помощью изменения его свойств и вызова методов. 70
У компонента имеются два типа интерфейсов: интерфейс стадии проектирования и интерфейс стадии выполнения. Интерфейс проекти- рования позволяет включать компоненты в современные среды разра- ботки приложений, а интерфейс выполнения управляет работой компо- нента во время выполнения программы. При этом неважно, на каком языке программирования реализован компонент. Он должен просто удовлетворять определенным внешним параметрам и быть нейтрален по отношению к языку программирования, чтобы его можно было исполь- зовать в программе на любом языке, поддерживающем компонентную технологию. Так, например, компоненты стандарта ActiveX могут быть одинаково успешно включены в программу, реализованную в среде Visual Basic, и в приложение, разработанное средствами Delphi. Компоненты графического интерфейса, управляемые событиями, являются основным «строительным» материалом при разработке при- ложений средствами графических редакторов. Разработка любого при- ложения состоит из двух взаимосвязанных этапов: • проектирование и создание функционального интерфейса при- ложения (т.е. набора визуальных компонентов, которые будут обеспечивать взаимодействие пользователя и вычислительной среды); • программирование процедур обработки событий, возникаю- щих при работе пользователя с приложением. На первом этапе (т.е. на этапе проектирования интерфейса — формирования общего вида главного окна при выполнении приложения и способов управления работой приложения) для каждого компонента необходимо определить его внешний вид, размеры, способ и место раз- мещения в области окна приложения (т.е. реализовать интерфейс разра- ботки и интерфейс выполнения). Компоненты, доступные проектировщику на этапе разработки при- ложения, разбиты на функциональные подгруппы. С точки зрения внутренней структуры компоненты разбиваются на три группы. На рис. 1.10 представлена графическая интерпретация этого разбиения. Визуальные компоненты (элементы управления) характеризуются наличием свойств размеров и положения в области окна и на стадии разработки приложения обычно находятся на форме в том же месте, что и во время выполнения приложения (например, кнопки, списки, пере- ключатели, надписи). Визуальные компоненты имеют две разновидно- сти — «оконные» и «неоконные» (графические). • «Оконные» визуальные компоненты (самая многочисленная группа компонентов) — это компоненты, которые могут получать фокус ввода (т.е. становиться активными для взаимодействия с пользова- телем) и содержать другие визуальные компоненты. 71
Рис. 1.10. Иерархия групп компонентов схожей внутренней структуры • «Неоконные» (графические) визуальные компоненты не могут по- лучать фокус и содержать другие визуальные компоненты (напри- мер, надписи и графические кнопки). Невизуальные компоненты на стадии разработки не имеют своего фиксированного местоположения и размеров. Во время выполнения приложения некоторые из них иногда становятся видимыми (например, стандартные диалоговые окна открытия и сохранения файлов), а другие остаются невидимыми всегда (например, таблицы базы данных). Важной характеристикой компонента являются его свойства. Свойства компонента — это атрибуты, определяющие его состояние и поведение. Различают три типа свойств. Первые — свойства времени проектирования. Установленные для них значения будут использоваться в момент первого отображения ком- понента и в дальнейшем могут быть изменены во время выполнения приложения. Вторые — динамические свойства. Изменением их значений можно управлять только изнутри программного кода (во время выполнения приложения). Третьи — так называемые свойства толъко-для-чтения, которые могут быть прочитаны и использованы при выполнении программы, но не могут быть изменены. Второй этап — непосредственное программирование процедур обработки событий, исходящих от компонентов. Основная задача при разработке таких процедур — запрограммировать реакцию на все воз- можные изменения состояний объектов. Общий принцип работы приложения с графическим интерфейсом может быть представлен схемой, изображенной на рис. 1.11. Работаю- щее приложение находится в состоянии ожидания события, которое возникает в результате взаимодействия пользователя с элементами управления графического интерфейса приложения. При получении 72
Рис. 1.11. Схема работы приложения с графическим интерфейсом 73
события от компонента программа передает управление процедуре об- работки этого события (если таковая предусмотрена набором функций приложения). Вопросы и задания 1. Спроектируйте объект на базе записи «Информация о студенте». Каковы могут быть свойства и методы подобного объекта? 2. Охарактеризуйте основные преимущества объектно-ориентированного подхода при разработке программ. 3. Приведите примеры объектов, иллюстрирующие свойство наследования. 4. Приведите примеры объектов, иллюстрирующие свойство полиморфизма. 5. Охарактеризуйте понятия «класс» и «объект». 6. Охарактеризуйте основные различия между группами компонентов. 7. В чем, по-вашему, заключаются основные различия в методах «оконных» и «неоконных» компонентов? 1.8. Разработка программного обеспечения (ПО) Общие принципы разработки ПО. Программное обеспечение раз- личается по назначению, выполняемым функциям, формам реализации. В этом смысле ПО — сложная, достаточно уникальная система. Однако существуют некоторые общие принципы, которые следует использовать при разработке ПО. Частотный принцип. Основан на выделении в алгоритмах и в об- рабатываемых структурах действий и данных по частоте использования. Для действий, которые часто встречаются при работе ПО, обеспечива- ются условия их быстрого выполнения. К данным, которым происходит частое обращение, обеспечивается наиболее быстрый доступ, а подоб- ные операции стараются сделать более короткими. Принцип модульности. Под модулем в общем случае понимают функциональный элемент рассматриваемой системы, имеющий оформ- ление, законченное и выполненное в пределах требований системы, и средства сопряжения с подобными элементами или элементами более высокого уровня данной или другой системы. Способы обособления составных частей ПО в отдельные модули могут быть существенно различными. Чаще всего разделение происхо- дит по функциональному признаку. В значительной степени разделение системы на модули определяется используемым методом проектирова- ния ПО. Принцип функциональной избирательности. Этот принцип являет- ся логическим продолжением частотного и модульного принципов и 74
используется при проектировании ПО, объем которого существенно превосходит имеющийся объем оперативной памяти. В ПО выделяется некоторая часть важных модулей, которые постоянно должны быть в состоянии готовности для эффективной организации вычислительного процесса. Эту часть в ПО называют ядром или монитором. При форми- ровании состава монитора требуется удовлетворить два противоречи- вые требования. В состав монитора, помимо чисто управляющих моду- лей, должны войти наиболее часто используемые модули. Количество модулей должно быть таким, чтобы объем памяти, занимаемой монито- ром, был не слишком большим. Программы, входящие в состав монито- ра, постоянно находятся в оперативной памяти. Остальные части ПО постоянно хранятся во внешних запоминающих устройствах и загру- жаются в оперативную память только при необходимости, иногда пере- крывая друг друга. Принцип генерируемости. Данный принцип определяет такой спо- соб исходного представления ПО, который бы позволял осуществлять настройку на конкретную конфигурацию технических средств, круг решаемых проблем, условия работы пользователя. Принцип функциональной избыточности. Этот принцип учитывает возможность проведения одной и той же работы (функции) различными средствами. Особенно важен учет этого принципа при разработке поль- зовательского интерфейса для выдачи данных из-за психологических различий в восприятии информации. Принцип «по умолчанию». Применяется для облегчения организа- ции связей с системой как на стадии генерации, так и при работе с уже готовым ПО. Принцип основан на хранении в системе некоторых базо- вых описаний структур, модулей, конфигураций оборудования и дан- ных, определяющих условия работы с ПО. Эту информацию ПО ис- пользует в качестве заданной, если пользователь забудет или сознатель- но не конкретизирует ее. В данном случае ПО само установит соответствующие значения. Общесистемные принципы. При создании и развитии ПО реко- мендуется применять следующие общесистемные принципы: - принцип включения, который предусматривает, что требования к созданию, функционированию и развитию ПО определяются со сторо- ны более сложной, включающей его в себя системы; - принцип системного единства, который состоит в том, что на всех стадиях создания, функционирования и развития ПО его целост- ность будет обеспечиваться связями между подсистемами, а также функционированием подсистемы управления; - принцип развития, который предусматривает в ПО возможность его наращивания и совершенствования компонентов и связей между ними; 75
- принцип комплексности, который заключается в том, что ПО обеспечивает связность обработки информации как отдельных элемен- тов, так и для всего объема данных в целом на всех стадиях обработки; - принцип информационного единства, определяющий, что во всех подсистемах, средствах обеспечения и компонентах ПО используются единые термины, символы, условные обозначения и способы представ- ления; - принцип совместимости, который состоит в том, что язык, сим- волы, коды и средства обеспечения ПО согласованы, обеспечивают со- вместное функционирование всех его подсистем и сохраняют открытой структуру системы в целом; - принцип инвариантности, который предопределяет, что подсис- темы и компоненты ПО инвариантны к обрабатываемой информации, т.е. являются универсальными или типовыми. Жизненный цикл программного обеспечения. Рассмотрим жиз- ненный цикл программного обеспечения, т.е. процесс его создания и применения от начала до конца. Этот процесс состоит из нескольких стадий: определения требований и спецификаций, проектирования, про- граммирования, отладки и сопровождения. Первая стадия — определение требований и спецификаций, может быть названа стадией системного анализа. На ней устанавливаются об- щие требования к ПО: по надежности, технологичности, правильности, универсальности, эффективности, информационной согласованности и т.д. Они дополняются требованиями заказчика, включающими про- странственно-временные ограничения, необходимые функции и воз- можности, режимы функционирования, требования точности и надеж- ности и т.д., т.е. вырабатывается описание системы с точки зрения поль- зователя. При определении спецификаций производится точное описание функций ПО, разрабатываются и утверждаются входные и промежуточные языки, форма выходной информации для каждой из подсистем, описывается возможное взаимодействие с другими про- граммными комплексами, специфицируются средства расширения и модификации ПО, разрабатываются интерфейсы обслуживающих и ос- новных подсистем, решаются вопросы базы данных, утверждаются ос- новные алгоритмы. Итогом выполнения этого этапа являются эксплуа- тационные и функциональные спецификации, содержащие конкретное описание ПО. Разработка спецификаций требует тщательной работы системных аналитиков, постоянно контактирующих с заказчиками, ко- торые в большинстве случаев не способны сформулировать четкие и реальные требования. Эксплуатационные спецификации содержат сведения о быстродей- ствии ПО, затратах памяти, требуемых технических средствах, надеж- ности и т.д. 76
Функциональные спецификации определяют функции, которые должно выполнять ПО, т.е. в них определяется, что надо делать систе- ме, а не то, как это делать. Спецификации должны быть полными, точными и ясными. Полно- та исключает необходимость получения разработчиками ПО в процессе их работы от заказчиков иных сведений, кроме содержащихся в специ- фикациях. Точность не позволяет различных толкований. Ясность под- разумевает легкость понимания как заказчиком, так и разработчиком при однозначном толковании. Значение спецификаций. 1. Спецификации являются заданием на разработку ПО и их вы- полнение — закон для разработчика. 2. Спецификации используются для проверки готовности ПО. 3. Спецификации являются неотъемлемой частью программной до- кументации, облегчают сопровождение и модификацию ПО. Второй стадией является проектирование ПО. На этом этапе: 1. Формируется структура ПО и разрабатываются алгоритмы, зада- ваемые спецификациями. 2. Устанавливается состав модулей с разделением их на иерархиче- ские уровни на основе изучения схем алгоритмов. 3. Выбирается структура информационных массивов. 4. Фиксируются межмодульные интерфейсы. Цель этапа — иерархическое разбиение сложных задач создания ПО на подзадачи меньшей сложности. Результатом работы на этом эта- пе являются спецификации на отдельные модули, дальнейшая декомпо- зиция которых нецелесообразна. Третья стадия — программирование. На данном этапе производит- ся программирование модулей. Этап менее сложен по сравнению со всеми остальными. Проектные решения, полученные на предыдущей стадии, реализуются в виде программ. Разрабатываются отдельные бло- ки и подключаются к создаваемой системе. Одна из задач — обосно- ванный выбор языков программирования. На этой же стадии решаются все вопросы, связанные с особенностями типа ЭВМ. Четвертая стадия — отладка ПО заключается в проверке выпол- нения всех требований, всех структурных элементов системы на таком количестве всевозможных комбинаций данных, какое только позволяют здравый смысл и бюджет. Этап предполагает выявление в программах ошибок, проверку работоспособности ПО, а также его соответствие спецификациям. Пятая стадия — сопровождение, т.е. процесс исправления оши- бок, координации всех элементов системы в соответствии с требова- ниями пользователя, внесения всех необходимых ему исправлений и изменений. Он вызван, как минимум, двумя причинами: во-первых, в 77
ПО остаются ошибки, не выявленные при отладке; во-вторых, пользова- тели в ходе эксплуатации ПО настаивают на внесении в него изменений и его дальнейшем совершенствовании. В общем случае, в процессе разработки ПО требуется осуществить несколько итераций (последовательных приближений) к приемлемому варианту системы. Распределение временных и стоимостных затрат по отдельным ста- диям жизненного цикла программного обеспечения представлено в табл.1.17 и 1.18. Таблица 1.17. Затраты по стадиям жизненного цикла ПО (Сведения из разных источников) Стадии жизненного цикла программного обеспечения Гласс Р. Нуазо Р. Энкар наччо Ж. Боэм Б. Федорчук Чернень- кий Виш- няков Сред- нее Определение требо- ваний и спецификаций 10 15 6 6 6 8,6 Проектирование 10 12 18 5 6 10,2 Программирование 10 12 9 7 6 8,8 Отладка 20 15 17 15 14 16,2 Сопровождение 50 46 50 67 68 56,2 Всего 100 100 100 100 100 100 Таблица 1.18. Основные параметры стадий жизненного цикла ПО Стадии жизненного цикла программного обеспечения Количество ошибок,% Обнаружение ошибок, % Затраты на устранение ошибок, % Определение требований и спецификаций 9 Проектирование 61-64 6 Программирование 36-39 10 Отладка 46 12 Сопровождение 54 63 Исходя из этого, возникает ряд требований к инженерному про- граммированию. Они состоят в необходимости разработки и сопровож- дения ПО, которое гарантирует, что ВС являются: • исключительно надежными; • удобными в использовании; 78
• естественными; • проверяемыми; • труднодоступными для злоупотреблений; • оставляющими главную роль за человеком, а не за машиной. Попытка сформулировать общую цель инженерного программиро- вания, достижение которой привело бы к удовлетворению всех указан- ных требований, обречена на неудачу, так как некоторые требования противоречат друг другу. Как видно, область инженерного программирования, к счастью (или к несчастью), не столь проста. Существует большое количество предлагаемых в качестве рекомен- даций «стандартов» программирования: «разрабатывайте сверху-вниз», «доказывайте правильность программ», «разрабатывайте дважды», «ис- пользуйте метод главного программиста», «программируйте структур- но», «четко определяйте этапы», «привлекайте к работе пользователя» и т.д. Каждый из этих советов способствует выполнению определенных требований, но никак не учитывает другие и фактически препятствует их удовлетворению. Вопросы 1. Охарактеризуйте общие принципы разработки ПО. 2. В чем заключаются общесистемные принципы разработки? 3. Охарактеризуйте стадии жизненного цикла ПО. 4. Какие стадии жизненного цикла требуют наибольших затрат и почему? 5. На каких стадиях жизненного цикла затраты на устранение ошибок макси- мальны и почему?
Глава 2. Язык программирования PASCAL На протяжении многих лет язык программирования Pascal доволь- но часто упоминается как в учебной, так и в научной литературе. Соз- данный специально с педагогическими целями, Pascal оказался крайне удачным не только в силу того, что ему несложно научиться, но и как основа обсуждения вообще языков программирования. Предварительное описание языка программирования Pascal было опубликовано в 1968 г. его создателем — профессором кафедры вычис- лительной техники Швейцарского федерального института технологии Николасом Виртом (название свое язык получил в честь великого фран- цузского математика и механика Блеза Паскаля, создавшего в 1642 г. первую счетную машину). Это был язык, по духу продолжавший линию языков Алгол-60 и Алгол-W. Затем, после периода интенсивного разви- тия, в 1970 г. заработал первый компилятор. Растущий интерес к созда- нию компиляторов на других машинах привел к распространению язы- ка, и после двух лет его использования потребовалось внести в язык небольшие изменения. Поэтому в 1973 г. было опубликовано Пересмот- ренное сообщение, где язык был уже определен в терминах множества символов ISO. В начале 80-х годов Pascal еще более упрочил свои позиции с появ- лением трансляторов MS-Pascal и Turbo-Pascal для персональных ЭВМ. С этого времени язык Pascal становится одним из наиболее широко ис- пользуемых языков программирования для персональных ЭВМ не толь- ко как рабочий инструмент пользователя, но и как средство обучения студентов высших учебных заведений программированию. Далее в развитии языка стала заметна тенденция его привязки к компьютеру IBM PC, стремление сделать его гибким и удобным инст- рументом. Следующий шаг усовершенствования — версия Object Pascal, используемая в среде разработки приложений Delphi. Включив в себя понятие класса, заменившего объект, эта версия языка поддержи- вает предыдущие версии Pascal в реализации фирмы Borland. И 2.1. Примеры программ В своей простейшей форме программа Pascal состоит из заголовка программы, который именует ее, и основного программного блока, вы- 80
полняюшего назначение программы. В основном программном блоке находится секция кода, заключенная между ключевыми словами begin и end. Рассмотрим простую диалоговую программу: Program Dialog; Var Nam, Nastr: string; Begin Repeat Writein('Здравствуйте! Как Вас зовут?'); Readln(Nam); If Nam = '*' Then Break; If Nam = '' Then Continue; Repeat Writein('Добрый день, '+ Nam); Writein('Как настроение?'); Readln(nastr); if nastr = '' then Continue; if nastr = '*' then Break; Writeln('y меня тоже '+nastr+', '+Nam); Until nastr <> '*' Until Nam <> ' * ' ; End; Диалог с данной программой может выглядеть следующим образом: Здравствуйте! как Вас зовут? Гаврик Добрый день, Гаврик Как настроение? Плохое У меня тоже Плохое, Гаврик В программе объявлены две строчные переменные NAM и NASTR. Тело программы ограничено операторными скобками begin и end. Программа состоит из двух вложенных циклов Repeat... Until. Вы- ход из внешнего и внутреннего циклов происходит при условии ввода в строке NAM или NASTR символа «*» (звездочка). С помощью операто- ра Break осуществляется выход во внешний цикл из внутреннего и из внешнего цикла на окончание работы. При попытке ввести пустую строку в ответ на запрос, программа возвращается в заголовок (оператор Continue), соответственно, внут- реннего или внешнего цикла и повторяет запрос на ввод строки. Управ- ление условными переходами осуществляется условным оператором If. Операторы Writein и Readln осуществляют, соответственно, вывод на экран и ввод с клавиатуры строк текста. Оператор «+» осуществляет в данном случае соединение строк. Рассмотрим далее программу сортировки элементов одномерного массива по возрастанию. 81
Program Sort_Array; Var W_Array : Array [1..100] of Integer; I, J, min, I_min, N : Integer; Begin Write('Введите количество элементов массива (N<=100): '); Readin(N); For I := 1 to N do // Ввод элементов массива Write('Введите элемент массива №', I , Readln(W_Array[I]) End; For I := 1 to N do // Внешний цикл прохода // по элементам Begin I_min := I; // Начальное задание индекса // минимального элемента For J := I + 1 to N do // Внутренний цикл поиска // минимального элемента // в пределах от I + 1 до N If W_Array[J] < W_Array[I_min] then I_min:= J min := W_Array[I_ min]; // Сохранение минимального // значения // Перестановка элементов: 1-го // и минимального W_Array[I_min]:= W_Array[I]; W_Array[I]:= min; End; For I := 1 to N do // Вывод отсортированного // массива Writein('Элемент массива №', I, W_Array[I]); End; Структурно программа выглядит следующим образом: - цикл ввода исходного массива; - два вложенных цикла сортировки массива; - цикл вывода результатов сортировки. В результате работы программы на экране может быть отображен следующий диалог: Введите количество элементов массива (N<=100):10 Введите элемент массива № 1: 2 Введите элемент массива № 2: 11 Введите элемент массива Na 3: 3 82
Введите элемент массива № 4: 1 Введите элемент массива № 5: 4 Введите элемент массива № 6: 16 Введите элемент массива № 7: 5 Введите элемент массива № 8: 12 Введите элемент массива № 9: 7 Введите элемент массива № 10: 8 Элемент массива №1=1 Элемент массива №2 = 2 Элемент массива №3 = 3 Элемент массива №4 = 4 Элемент массива №5 = 5 Элемент массива №6 = 7 Элемент массива №7 = 8 Элемент массива №8 = 11 Элемент массива №9 = 12 Элемент массива №10 = 16 В программе объявляются переменные — массив целых чисел, размерности не более 100 элементов (W Array), целые элементарные данные — I, J, min, I_min, N. Циклы в данном примере организуются вложенными операторами For I := 1 to N do и For J : = I + 1 to N do. Комментарии, как это очевидно из контекста, отделяются призна- ком //. И 2.2. Лексика языка Алфавит — это набор символов, из которых можно строить запись алгоритма. Чтобы задать алфавит, необходимо определить, что такое символ: <Символ> ::= <Буква>|<Цифра>|<Ограничитель> При записи текстов программ на языке Pascal разрешается исполь- зовать заглавные и строчные буквы латинского алфавита и знак «_» («подчеркивание»), т.е. понятие «Буква» задается следующей формулой: <Буква> ::= AjB|C|D|E|F|G|H|I|J|G|K]L|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|_| a|b|c|d|e|f]g|h|i[j|g|k|l|n|o|p|q|r|s|t|u|v|w|x|y|z| Для определения понятия «Цифра» используется формула: <Цифра> ::= 0|1|2|3|4|5|6|7|8|9 Ограничители в тексте программы разделяют элементы фраз. Это 83
могут быть знаки операций, скобки и другие служебные знаки или слу- жебные слова, для которых не удалось подобрать служебные знаки. <Ограничитель> ::= <Служебный знак>|<Служебное слово> Служебные знаки, выполняющие в языке определенные функции, можно разделить на следующие группы: <Служебный знак> ::= <3нак пунктуации>|<3нак операции>| <Разделитель> Приведем определение каждой из групп: <3нак пунктуации» ::= {|}|(*|*)|(|)|[|]|;|:Г|#|@|$|.|,|..|=|:= Назначение знаков пунктуации Знаки Назначение (* *) {} Скобки комментария. Текст, заключенный между скобками, поясня- ет алгоритм и не является его частью [] Задание индексов массива, размера строки, элементов множества О Выделение части выражения, задание списков параметров 3 Отделение одного предложения программы от другого, разделение параметров (в части объявления) Отделение переменной или константы от типа (в части объявлений), отделение метки от оператора, следующего за ней э Разделение элементов списка, параметров процедуры и функции при вызове @ Обозначение адреса (переменной, константы, процедуры, функции, метода) $ Признак числа в шестнадцатеричной системе, обозначение директи- вы компилятора # Обозначение символа по его коду Разделение границ диапазона в типе-диапазоне Знак оператора присваивания Отделение идентификатора типа (константы) от его описания (зна- чения) • Апостроф — признак символа или строковой константы. <3нак операции» ::= +|-|/|*|л|=1>1>=1<1<= «Разделитель» ::= <Пробел»|<Управляющий символ (коды от 0 до 31)> Служебные (зарезервированные) слова. Следующие слова могут быть использованы только по своему специальному назначению: 84
and array as asm begin case class const constructor destructor dispinterface div do downto else end except exports file finalization finally for function goto if implementation in inherited initialization inline interface is label library mod nil not object of or out packed procedure program property raise record repeat resourcestring set shl shr string then threadvar to try type unit until uses var while with xor Для именования различных алгоритмических объектов служат язы- ковые конструкции, называемые идентификаторами. Идентификатор определяется как последовательность букв и цифр, начинающаяся с буквы: <Идентификатор> ::= <Буква>|<Идентификатор><Буква>| <Идентификатор><Цифра> При записи текста программы нет разницы между строчными и прописными буквами. 2.3. Переменные и константы. Типы данных Для объявления переменных и констант в программе выделены особые синтаксические разделы. Раздел объявления констант начинает- ся со служебного слова const и содержит перечень всех используемых в программе констант, например, описание константы Radius, прини- мающей значение 4, в программе выглядит следующим образом: Const Radius = 4; Раздел объявления переменных начинается со служебного слова var и содержит описание всех переменных: Var Radius: integer 85
Типы данных. С понятием данных тесно связано понятие типа данных. Тип — это такая характеристика данных, которая, с одной сто- роны, задает границы изменения данных, а с другой — определяет множество операций над ними. Типы данных принято делить на простые (или базовые) и струк- турирование! е. К простым (базовым) типам в языке Pascal относятся: • целый; • вещественный; • логический; • символьный; • перечисляемый; • тип-диапазон. Структурированные типы описывают наборы однотипных или раз- нотипных данных. Типы данных, образующих набор, в свою очередь могут быть как простыми, так и структурированными. Если тип компо- нент набора является структурным, то говорят, что получаемый в ре- зультате структурированный тип имеет более одного уровня структури- рования. Структурированный тип может иметь неограниченное количе- ство уровней структурирования. К стандартным структурированным типам относятся: • массив; • запись; • строка; • множество; • файл. Целый тип данных Целый тип присваивается данным (переменным и константам), ко- торые во время работы программы могут принимать лишь целочислен- ные значения. Например, программа вычисления функции факториал для нату- рального числаN(y = Nl = 1-2-3-...-N) оперируете целыми числовыми значениями: N иу— натуральные числа. В стандарте языка Pascal определен единственный целый тип дан- ных Integer. В современных реализациях языка целый тип данных пред- ставлен множеством типов: 86
Название типа Область изменения данных Занимаемый размер в бантах Знак числа Integer -32768..32767 (-23l..231-1) 2(4) Целое со знаком Shortint -128.. 127 1 Целое со знаком Smallint -32768..32767 2 Целое со знаком Longint -23l..23l-l 4 Целое со знаком Byte 0..255 1 Целое без знака Word 0..216- 1 2 Целое без знака Longword 0..4294967295 4 Целое без знака Например, для переменных X, Y, Z, описанных в разделе объявле- ния переменных, как Var X: byte; Y: smallint; Z: word; операторы X := 2000; Y ;= —40000: Z := -2 будут некорректными, так как переменная X не может принимать значение, большее чем 255, Y не может быть меньше чем -32768, Z должно быть положительным. Логический тип данных Данные логического типа (Boolean) в стандарте языка могут при- нимать одно из двух значений: True или False. Переменная или констан- та логического типа занимает 1 байт, в который записывается 1, если переменная или константа имеет значение True, и 0 в противном случае. Например, для переменной Flag, описанной в разделе объявления переменных как Var Flag: Boolean; корректны операторы: Flag := true; Flag := false; В современных реализациях языка (Turbo Pascal V 7.0, Object Pascal) добавлено еще три логических типа (для совместимости с другими языками программирования и со средой Windows). Основ- ные отличия этих типов от стандартного заключаются, во-первых, в фактическом размере (в байтах), а во-вторых — в величине, соответ- ствующей значению True. Для всех логических типов значению False соответствует число 0, записанное в соответствующее количество байтов. Значению же True в случае стандартного типа соответствует 87
только 1, записанная в байт, а в случае других логических типов — любое число, отличное от 1. Название типа Область изменения данных Занимаемый размер в байтах Boolean True..False 1 ByteBool True..False 1 WordBool True..False 2 LongBool True..False 4 Между значениями True и False имеют место следующие отношения: Boolean ByteBool, WordBool, LongBool False < True False о True Ord(False) = 0 Ord(False) = 0 Ord(True) = 1 Ord(True) о 0 Succ(False) = True Succ(False) = True Pred(True) = False Pred(False) = True Символьный тип данных Данные стандартного символьного типа представляют собой сим- волы раскладки ASCII. Переменная или константа символьного типа занимает 1 байт памяти. В соответствии с синтаксисом языка значение символа заключается в одинарные кавычки: ‘Р’, ‘a’, ‘s’, ‘с’, ‘а’, ‘1’. При вызове функции Ord(Ch), где Ch — значение типа Char, воз- вращается порядковый номер Ch. Любое значение символьного типа можно получить с помощью стандартной функции Chr(X), где X — зна- чение типа byte (или используя знак «#» перед числом, например #30 = Chr(30)). Для следующих объявлений переменных var С:Char; В:byte; справедливы операторы: С := #3 ; В := Ord(С); В := 125; С := Chr(B); 88
Перечисляемый тип Перечисляемый тип данных задается упорядоченным набором идентификаторов, с которыми могут совпадать значения переменной (или константы) этого типа. Список идентификаторов указывается в круглых скобках, а сами идентификаторы разделяются запятыми: Перечисляемый тип> := <Идентификатор типа> = (<Список идентификаторов»); <Идентификатор типа > := < Идентификатор» <Список идентификаторов» := <Идентификатор»|<Идентификатор>, <Список идентификаторов» Упорядочение наборов выполняется в соответствии с последова- тельностью, в которой перечисляются идентификаторы. Порядковый номер перечисляемой константы определяется ее позицией в списке идентификаторов при объявлении. Первый идентификатор в списке имеет порядковый номер 0, второй — порядковый номер 1 и т.д. Один и тот же идентификатор можно использовать в объявлении только одного перечисляемого типа. Приведем примеры перечисляемого типа: Туре Color = (Red, Green, Blue, Black); Operator = (Plus, Minus, Multiply, Divide); Порядковые типы Типы данных логический, символьный, целый и перечисляемый относятся к так называемым порядковым типам, т.е. представляют со- бой множества значений, для которых определен порядок следования. Для величин порядкового типа существуют стандартные процеду- ры и функции, позволяющие выполнить ряд действий. В таблице, при- веденной ниже, перечислены эти процедуры и функции. Название Действие Тип аргумента (параметра) Тип результата Примеры Ord(X) Возвращает целое число,которое показывает, какое положение зани- мает значение X по отношению к другим значе- ниям. Логический, символьный, целый, пере- числяемый Longint Ord(false) = 0; Ord(‘l’) = 34; Ord(Club) = 0 89
Продолжение табл. Название Действие Тип аргумента (параметра) Тип результата Примеры Pred(X) Возвращает преды- дущее значение X. При применении к первому элементу возникает ошибка Логический, символьный, целый, пере- числяемый Тот же, что и у аргу- мента Pred(true) = false; Pred(O) = -l; Pred(Minus) = Plus Pred(Plus) — er- ror! Succ(X) Возвращает после- дующее значение X. При примене- нии к последнему элементу возника- ет ошибка Логический, символьный, целый, пере- числяемый Тот же, что и у аргу- мента Succ(false) = true; Succ(‘l’) = ‘2’; Succ(0) = 1; Odd(X) Проверка аргумен- та на нечетность: возвращает true, если аргумент не- четный, и false, если аргумент четный Longint Boolean Odd(3) = true; Odd(6) = false; Inc(X [,N]) Процедура. Увели- чивает значение переменной X на 1 (если второй пара- метр отсутствует) или на N Тип X — це- лый, логический, символьный, перечисляе- мый; Тип N — Longint Inc(X) аналогично X:=X+1, если X — целое; Succ(X), если X — перечис- ляемое Dec(X [,N]) Процедура. Умень- шает значение пе- ременной X на 1 (если второй пара- метр отсутствует) или на N Тип X — це- лый, логический, символьный, перечисляе- мый; Тип N — Longint Dec(X) аналогично X:=X—1, если X — целое; Pred (X), если X — перечис- ляемое Тип-диапазон Тип-диапазон представляет собой диапазон значений из порядково- го типа, называемого главным типом. Определение типа-диапазона за- дает все значения из главного типа, находящиеся между наименьшим и наибольшим значением, включая сами границы, и имеет следующий синтаксис: 90
<Тип-диапазон> := <Имя типа> = <Мин. значение»..<Макс. значение>; <Имя типа> := <Идентификатор> <Мин. значение> := <Константа> <Макс. значение» := <Константа> Обе константы должны быть одного порядкового типа, при этом минимальное значение не должно превышать максимального. Приведем примеры типов-диапазонов: О.. 999 -100.. 100 Red.. Blue Переменная типа диапазон имеет все свойства переменных главно- го типа, но ее значение на этапе выполнения должно принадлежать ука- занному интервалу. Для задания границ диапазона разрешается использовать констант- ные выражения, в связи с чем может возникнуть синтаксическая неоп- ределенность. Рассмотрим следующие объявления: const X = 5; Y = 10; type Operator = (Plus, Minus, Multiply, Divide); Height = (X - Y) * 2 .. (X + Y) * 2; В соответствии с правилами синтаксиса языка любое определение ти- па, начинающееся с круглой скобки, воспринимается, как определение пе- речисляемого типа (см. определение типа Operator). Однако целью объяв- ления Height является задание типа-диапазона. Решением этой проблемы является либо преобразование первого выражения, задающего минималь- ное значение, так, чтобы оно не начиналось с круглой скобки, либо задание другой константы, равной значению этого выражения, и затем использова- ние этой константы в определении типа, например: type Height = 2 * (X - Y) .. (X + Y) * 2; Вещественный тип данных Данные вещественного типа — это вещественные значения, запи- санные в памяти в виде чисел с плавающей точкой. Область возможного изменения значений определяется размером (в байтах), отводимым под конкретную реализацию типа. Вещественный тип в стандарте языка Pascal называется Real. По- мимо типа Real в современных реализациях Pascal определены еще 91
шесть стандартных вещественных типов. Каждый тип характеризуется своей областью изменения возможных значений. Тип Comp фактически является целым типом расширенного диапазона, но при этом на считается порядковым (т.е. к переменным типа Comp нельзя применять процедуры и функции, определенные только для порядковых типов — Inc, Dec и т.п.). Выбор конкретного типа для переменной связан с требуемой точ- ностью вычислений. Название типа Область изменения данных Занимаемый размер в байтах Кол-во знача- щих цифр Real 5.0-1 (Г324.. 1.7-10308 8 15-16 Real48 2.9-10'39.. 1.7-1038 6 11-12 Single 1.5-Ю^.ЗЛ! О38 4 7-8 Double 5.0-10’324.. 1.7-10308 8 15-16 Extended 3.61 О’4951..! ЛЮ4932 10 19-20 Comp -263 + 1..263- 1 8 19-20 Currency -922337203685477.5808 ..922337203685477.5807 8 19-20 Для переменных вещественного типа определены 2 функции, по- зволяющие преобразовать переменную вещественного типа в перемен- ную целого типа. В качестве аргументов функций выступают значения вещественного типа, а результат принадлежит целому типу. Названия и результат действия этих функций приведены в таблице: Название Назначение Примеры Round(X) Округление вещественного числа до целого Round(3.456) = 3; Round(5.678) = 6; Trunc(X) Выделение целой части числа Trunc(3.456) = 3; Trunc(5.678) = 5; Типизованные константы Типизованные константы можно использовать точно так же, как переменные того же самого типа, и они могут появляться в левой части оператора присваивания. Отметим, что типизированные константы за- даются только один раз — в начале выполнения программы. Таким об- разом, при каждом новом входе в процедуру или функцию локально объявленные типизированные константы заново не инициализируются. Синтаксис объявления типизированной константы следующий: 92
<Типизированная константа» ::= <Идентификатор>:<Тип данных» = «Значение»; Константы простого типа Объявление типизированной константы простого типа содержит в своем описании указание на простой тип данных (например, Integer, Real или Char): const Maximum : Integer = 999; Factor : Real = -0.1; Breakchar : Char = #3; Поскольку типизированная константа фактически представляет со- бой переменную с константным значением, она не может заменять обычную константу. Например, она не может использоваться в объяв- лении других констант или типов. const Min : Integer = 1; Max : Integer = 1000; type Vector = array [Min..Max] of Integer; Объявление Vector является недопустимым, поскольку Min и Max являются типизированными константами. 2.4. Выражения и операции Выражение — это синтаксическая единица языка, задающая поря- док и способ вычисления некоторого значения. В соответствии с правилами формирования выражение представля- ет собой последовательность операндов, соединяющихся друг с другом знаками операций. Некоторые фрагменты выражения могут быть за- ключены в круглые скобки. «Выражение» ::= «Операнд» | «ВыражениехБинарная операция» «Выражение» | «Унарная операция»«Выражение> | («Выражение») В качестве операнда в конструкции выступают переменные, кон- станты, функции: «Операнд» ::= «Переменная» | «Константа»! «Функция» 93
Операции подразделяются на несколько групп: • арифметические операции; • операции отношения; • логические операции; • операции с битами информации. Каждой группе операций соответствуют определенные типы пере- менных и констант. По количеству операндов, участвующих в операциях, их делят на унарные (операции с одним операндом) и бинарные (операции с двумя операндами). В бинарных операциях используется обычное алгебраиче- ское представление, например: А + В. В унарных операциях операция всегда предшествует операнду, например -В. В более сложных выражениях порядок, в котором выполняются операции, соответствует приоритету операций: Операции Приоритет Категория @, not, +, - Первый (высший) Унарные операции *, /, div, mod, and, shl, shr Второй Операции умножения +,-,or,xor Третий Операции сложения =, o, <, >,<=, >=, in Четвертый (низший) Операции отношения Для определения старшинства операций применяются три основ- ных правила: 1. Операнд, находящийся между двумя операциями с различными приоритетами, связывается с операцией, имеющей более высо- кий приоритет. 2. Операнд, находящийся между двумя операциями с равными приоритетами, связывается с операцией, которая находится слева от него. 3. Выражение, заключенное в круглые скобки, перед выполнени- ем вычисляется, как отдельный операнд. Операции с равным приоритетом обычно выполняются слева на- право, хотя иногда компилятор при генерации оптимального кода мо- жет переупорядочить операнды. Арифметические операции Арифметические операции могут применяться только к операндам целых и вещественных типов: 94
Знак Операция Кол-во операндов Тип операндов Тип результата Описание + Признак положитель- ного числа Унарная Целый Вещест- венный Целый Веществен- ный Не меняет значения операнда Признак от- рицательного числа Унарная Целый Вещест- венный Целый Веществен- ный Меняет зна- чение опе- ранда на отрицатель- ное + Сложение Бинарная Целый Хотя бы один ве- ществен- ный Целый Веществен- ный Результат — сумма двух чисел Вычитание Бинарная Целый Хотя бы один ве- ществен- ный Целый Веществен- ный Результат — разность двух чисел * Умножение Бинарная Целый Хотя бы один веще- ственный Целый Вещест- венный Результат — произведение двух чисел / Деление Бинарная Целый или вещест- венный Вещест- венный Результат — частное от деления двух чисел div Деление це- лых чисел Бинарная Целый Целый Результат — целая часть от деления целых чисел: 25 div 6 = 4 mod Остаток от деления це- лых чисел Бинарная Целый Целый Результат — остаток от деления це- лых чисел: 25 mod 6 = 1 В качестве операндов арифметических операций могут выступать стандартные арифметические (или тригонометрические) функции, т.е. функ- ции с результатом целого или вещественного типа, аргументами которых являются выражения целого или вещественного типа. В таблице приведены характеристики некоторых арифметических и тригонометрических функ- ций (список функций приведен для расширения языка — Object Pascal). 95
Функция Назначение Тип аргумента Тип результата Abs(X) Модуль (абсолютная величина) аргумента Целый Вещественный Целый Вещественный Arccos(X) Арккосинус аргумента Вещественный, Abs(X) <= 1 Вещественный Arcsin(X) Арксинус аргумента Вещественный, Abs(X)<=l Вещественный ArcTan(X) Арктангенс аргумента Вещественный Вещественный Ceil(X) Минимальное целое число, большее или равное X Целый или вещест- венный Целый. Например: Ceil(-2.8) =-2 Ceil(2.8) = 3 Cos(X) Косинус аргумента Вещественный Вещественный Cotan(X) Котангенс аргумента Вещественный Вещественный Hypot(X,Y) Гипотенуза прямо- угольного треугольни- ка с катетами X,Y Вещественный Вещественный LoglO(X) Десятичный логарифм аргумента Вещественный Вещественный Log2(X) Логарифм аргумента по основанию 2 Вещественный Вещественный LogN(N,X) Логарифм аргумента X по основанию N Вещественные Вещественный Max(A,B) Максимальное из чисел А, В Оба целые или оба вещественные Совпадает с типом аргументов Min (A,B) Минимальное из чисел А, В Оба целые или оба вещественные Совпадает с типом аргументов Pi Число р Вещественный Power(X, Ex) Возведение аргумента X в степень Ех Вещественные Вещественный Sin(X) Синус аргумента Вещественный Вещественный SqitX) Квадрат аргумента Вещественный Вещественный Sqrt(X) Квадратный корень аргумента Вещественный Вещественный Tan(X) Тангенс аргумента Вещественный Вещественный Примеры записи арифметических выражений: (Мах(Х, Y) + sqrt(Z)) * 2; Ceil(Tan(X) - 1) ; Pi * Sqr(R); (Cos(X) + Y)/Z; Log2(X) + X/Y; Power(P, Q)/Power(R, (S + T) ) ; 96
Операции отношения В результате выполнения операций отношения получается значение логического типа: true или false. При этом операнды, участвующие в опе- рации, должны быть сравнимых типов, например: целого и целого; целого и вещественного; логического и логического и т.п. Ошибочно сравнивать операнды символьного и целого типа, или целого и логического. Операция Описание Примеры — Равно X = Abs(X) — true, если X — положительное число, false, если X — отрицательное А = А — true 2 = 5 —false Не равно X о Abs(X) — true, если X — отрицательное число, false, если X — положительное А о А — false 2 о 5 — true < Меньше А < А — false 2 < 5 — true <= Меньше или равно А <= А — true Max(X,Y)<=X — false > Больше A+l > А— true Min(X,Y)>X —false >= Больше или равно A >= A — true Max(X,Y)>=X — true Логические операции Логические операции применяются к операндам логического типа. Результат выполнения логических операций тоже логического типа. Вычисление логических выражений происходит в соответствии с таб- лицами истинности логических операций. Таблицы истинности задают соответствие между значениями операндов и результатом выполнения операции (см. табл. 1.3,1.4). Примеры записи логических выражений: (X >= 0) and (X <= 1) ((X > 0) and (X < 0.5)) or (X > 3) (N mod 2=0) and (N > 0) Операции с битами информации Оператор not является унарным оператором. Операции I shl J и I shr J сдвигают значение 1 влево или вправо на J битов. Тип результата будет таким же, как тип 1. 97
Операция Описание Кол-во операндов Тип операндов и результата Примеры Not Битовое отрицание Унарная Целый not 3 = -4; not (-4) = 3 And И (битовое) Бинарная Целый 3 and 5 = 1; 15and7 = 7 Or ИЛИ (битовое) Бинарная Целый 3or5 = 7; 15or7= 15 Xor Исключающее ИЛИ (битовое) Бинарная Целый 3 xor 5 = 6; 15xor7 = 8 Shi Сдвиг влево Бинарная Целый 10 shl 2 = 40; 11 shl 2 = 44 Shr Сдвиг вправо Бинарная целый 10 shr 2 = 2; 11 shr2 = 2 2.5. Операторы языка Оператор присваивания С помощью этого оператора переменной, имя которой указывается в левой части оператора, присваивается значение выражения, стоящего в правой части и вычисляемого перед присваиванием. Синтаксис: <Оператор присваивания> ::= <Переменная> := <Выражение> В левой части оператора присваивания может стоять переменная любого типа (кроме типа File), но при этом типы переменной и выраже- ния должны совпадать. Примеры: 1. Вычисление длины окружности L (тип — real): L := 2*Pi*R R — радиус окружности (тип — integer или real); Pi — константа, равная значению л. 2. Присвоить переменной Flag (тип — Boolean) значение true, если переменная X (тип — real) находится в интервале (0; 1), и false — в про- тивном случае: Flag :=(Х>0) and(X< 1) 98
3. Присвоить переменной Rect (тип — Boolean) значение true, если из отрезков длиной X, Y, Z (тип — real) можно построить треугольник, и false — в противном случае Rect := (X < Y + Z) and (Y < X + Z) and (Z < X + Y) Оператор безусловного перехода Оператор goto позволяет изменить стандартный последовательный порядок выполнения операторов и передать управление заданному опе- ратору, которому в этом случае должна предшествовать метка. Эта же метка должна быть указана при операторе goto. Метка представляет собой либо целое число в пределах от 0 до 9999, либо идентификатор. Синтаксис оператора безусловного перехода: <Оператор безусловного перехода> ::= goto <Метка>; <Метка> ::= <Идентификатор>|<Целое число от 0 до 9999> Все метки должны быть перечислены в разделе объявления меток, который начинается со служебного слова Label: Label 99, 100, MyLabel; Одной меткой помечается только один оператор. Метка от опера- тора отделяется двоеточием: <Помеченный оператор> ::= <Метка>:<Оператор> При использовании оператора безусловного перехода должны со- блюдаться следующие правила. 1. Метка, указанная в операторе перехода, должна находиться в том же блоке или модуле, что и сам оператор перехода. Другими словами, не допускаются переходы из процедуры или функции или внутрь нее. 2. Переход извне внутрь составного оператора (т.е. переход на бо- лее глубокий уровень вложенности) может вызвать некорректную рабо- ту программы, хотя компилятор не выдает сообщения об ошибке. Следует также иметь в виду, что слишком частое применение опе- ратора безусловного перехода ухудшает логику программы. Пример: if Odd(I) then begin X : = Z * X; goto 1; end; I := I div 2; X := Sqr(X); 1:; // Пустой оператор 99
Пустой оператор Пустой оператор не выполняет никакого действия в программе, но может иногда потребоваться для осуществления на него безусловного перехода. Пустой оператор может отображаться в программе точкой с запя- той, отделяющей пустой оператор от предшествующего, или меткой (см. Пример выше). Условный оператор Назначение условного оператора — обеспечить ветвление алго- ритма в зависимости от выполнения некоторого условия. Например, некоторой переменной X необходимо присвоить значение Y, если X > О, и значение Z, если X < 0. В этом случае необходимо сначала определить знак переменной X, а затем выполнить один из двух операторов при- сваивания. В языке Pascal существует две формы условного оператора: услов- ный оператор If и условный оператор Case. Условный оператор If имеет следующий синтаксис: <Условный оператор If* ::= <Условие><оператор>[ <условие><оператор> else <оператор> <условие> ::= if «логическое выражение> then Условный оператор в неполной форме (if «логическое выражение* then < оператор >) выполняется следующим образом: сначала вычисляется логическое выражение, затем — если значение логического выражения true (истина) — выполняется оператор, стоящий за then. Если значение логиче- ского выражения false (ложь), условный оператор действует как пустой. В случае использования условного оператора в полной форме (if «логическое выражение* then < операторПие > else «onepaiop false*) операторное выполняется, если значение логического выражения истинно, а опера- Top false — если значение логического выражения ложно. Пример: Написать последовательность действий для решения следующей задачи: присвоить переменной у значение sin х, если х > 0, и значение cos х, если х 0. В этом случае необходимо использовать условный оператор в пол- ной форме: if х>0 then y:=sin(x) else y:=cos(x); Изменим условие задачи следующим образом. Присвоить перемен- ной у значение sinx, еслих* 1, и значение cosx, если х<3 2. 100
Переменная у должна поменять свое значение только в том случае, если переменная х принадлежит одному из интервалов: (-00,1) или [2, +оо). Если же х лежит в интервале [1, 2) переменная у не меняет сво- его значения. Такой алгоритм может быть реализован последовательностью из двух условных операторов в неполной форме: If х<1 then у:= sin(x); If х>=2 then у := cos(x); Второй оператор приведенной записи решения может быть исполь- зован в качестве оператора_е1ве в условном операторе в полной форме. Тогда решение можно записать следующим образом: If х<1 then у:= sin(x) else if х>=2 then у := cos(х) Рассмотрим алгоритм поиска корней квадратного уравнения ах2+Ьх+с = 0 (а^О). Корни уравнения вычисляются по следующей формуле: -b + yjb2 -4ас *|.2=-----1------ 2а и существуют при условии, что D = b2-4ас О 0. Если D = 0, то ко- , b рень уравнения один и вычисляется по формуле: х =-. 2а Для решения задачи объявим следующие переменные: • D (тип — real) — значение дискриминанта; • х! (тип — real) — первый корень уравнения; • х2 (тип — real) — второй корень уравнения. Первый шаг решения — вычисление D. Реализуется оператором присваивания: D := Ь*Ь + 4*а*с; Если вычисленное значение меньше 0, то корней у уравнения нет: If D < 0 then Writein('Корней нет'); (Здесь в записи оператора используется стандартная функция Writein, обеспечивающая вывод сообщения на экран монитора. Об- щий синтаксис и область применения функции будут рассмотрены ниже). Если значение переменной равно 0, то имеем один корень (xl): 101
If D = 0 then Begin xl: = -b/(2*a); Writein('Уравнение имеет один корень — ’,xl) end; При положительном ненулевом значении D вычисляем оба корня: If D > 0 then begin xl := (-b + sqrt(D)) / (2*а); х2 := (—b — sqrt(D)) / (2*а); Writein('Уравнение имеет два корня — xl, х2) end; Общий вид фрагмента программы вычисления корней квадратного уравнения: D := Ь*Ь + 4*а*с; If D < 0 then Writein('Корней нет'); If D = 0 then Begin xl: = -b/(2*a); Writein('Уравнение имеет один корень — ’,х1) end; If D > О then begin xl := (-b + sqrt(D)) / (2*a); x2 := (—b — sqrt(D)) / (2*a); Writein('Уравнение имеет два корня — ', xl, х2) end; Заметим, что в приведенном фрагменте в любом случае осуществ- ляется проверка всех трех условий. Чтобы избежать этого, воспользуем- ся возможностью формирования вложенных условных операторов (т.е. возможностью использования условных операторов в качестве операто- pa true и onepaTopa false). Тогда фрагмент программы можно записать следующим образом: D := b*b + 4*а*с; If D < 0 then Writein('Корней нет') else If D = 0 then begin xl:= -b/(2*a); Writein('Уравнение имеет один корень — ', xl) end; else begin xl := (-b + sqrt(D)) / (2*a); x2 := (-b — sqrt(D)) / (2*a); Writein('Уравнение имеет два корня — ', xl, х2) end; 102
Условный оператор Case Оператор предназначен для организации выбора одной из любого количества ветвей алгоритма в зависимости от значения некоторого выражения. Синтаксис: «Условный оператор Case» ::= Case «выражение» of «последовательность Case- ветвей» else «оператор» end; «последовательность Case-ветвей» ::= <Са5е-ветвь»;|«последовательность Case-ветвей» «Case-ветвь» «Case-ветвь» ::= «константа»: «оператор» Оператор Case-ветви выполняется в том случае, если выражение в заголовке оператора Case принимает значение, равное константе Case- ветви. Структура оператора, принятая в языке: Case S of C_V1: operator_Vl; C_V2: operator_V2; C_VN: operator_VN; Else operator_Else End; Здесь S — выражение, значение которого вычисляется; C_V1, C_V2, ..., C_VN — константы, которые определяют разветвление алго- ритма и последовательно сравниваются со значением выражения S; operatorVl, operatorVl,..., operator_VN — операторы ветвей; operator Else — оператор, выполняющийся в том случае, если значение выражения не совпадает ни с одной из констант. При использовании оператора Case необходимо помнить о том, что значение выражения и константы должны быть одного типа. Пример: Присвоить строке S значение дня недели для заданного числа D при условии, что в месяце 31 день и 1-ое число — понедельник. Для построения алгоритма воспользуемся операцией mod, позволяю- щей вычислить остаток от деления двух чисел, и условием, что 1-ое число — понедельник. Тогда можно записать следующий оператор Case: Case D mod 7 of 1: S := 'понедельник'; 2: S := 'вторник'; 3: S := 'среда'; 4: S := 'четверг'; 5: S := 'пятница'; 103
6: S := 'суббота'; 0: S := 'воскресенье'; End; C помощью оператора вычисляется остаток от деления D на 7 и, в зависимости от полученного значения, в переменную S заносится стро- ка, соответствующая дню недели. В предложенной записи отсутствует оператор Else. Это объясняет- ся тем, что выражение D mod 7 может принимать только одно из ука- занных значений. Запись алгоритма аналогична, например, следующей записи в полной форме: Саве D mod 7 of 1: S := 1 1 понедельник' 2: S := 1 'вторник'; 3: S := ' 1 среда'; 4: S := ' 'четверг'; 5: S := ' 'пятница’; 6: S := ’ 'суббота'; else s S : = 'воскресенье End; Чтобы завершить построение алгоритма решения поставленной за- дачи, необходимо ограничить область возможных значений переменной D (тип — integer): If (D>=1) and (D<=31) then Case D mod 7 of 1: S := 'понедельник'; 2: S := 'вторник'; 3: S := 'среда'; 4: S := 'четверг'; 5: S := 'пятница'; 6 : S :- 'Суббота'; else S := 'воскресенье' End; Оператор цикла Оператор цикла служит для организации выполнения циклических процессов (таких, когда одни и те же действия многократно повторяются). Обобщенный оператор цикла имеет следующий синтаксис: «Оператор цикла> ::= «заголовок цикла> «тело цикла> «тело цикла> ::= «оператор» Заголовок цикла содержит сведения об условиях выполнения цик- лических действий, а тело цикла представляет собой последователь- ность самих действий. 104
В языке Pascal реализовано три разновидности оператора цикла — операторы For, While и Repeat. Оператор For Синтаксис: «Оператор For> ::= For «переменная цикла> := <выражение_начало> to <выражение_конец> do «тело цикла>| For «переменная цикла> := «выраже- ние_начало> downto <выражение_конец> do «тело цикла> Переменная цикла должна принадлежать счетному множеству зна- чений (т. е. должна быть целого или перечисляемого типа). Выраже- ниеначало, выражение конец и переменная цикла должны быть со- вместимы по типу. Выполнение оператора происходит по следующему алгоритму: 1. Вычисляется значение выражения, определяющего выраже- ние начало (VStart), и присваивается переменной цикла (i: = VStart). 2. Вычисляется значение выражения, определяющего выраже- ниеконец (VFinish). 3. Значение переменной цикла (i) сравнивается со значением вы- ражения, вычисленного в п.2 (V Finish). Если i V Finish (в случае использования оператора с перечислением to) или i О V Finish (в случае использования оператора с перечисле- нием downto) — переход к п. 4, иначе — переход к п. 6. 4. Выполняется тело цикла. 5. В случае использования оператора с перечислением to пере- менной цикла присваивается следующее большее значение (например, если i целого типа, то i := i + 1), а в случае исполь- зования оператора с перечислением downto переменной цикла присваивается следующее меньшее значение (например, если i целого типа, то i: = i — 1). Переход к п. 3. 6. Завершение выполнения оператора. Из представленного алгоритма видно, что если V Start > V Finish (в случае использования оператора с перечислением to ) или V Start < < V Finish (в случае использования оператора с перечислением downto), то тело цикла ни разу не выполнится. Количество шагов цикла (n_Step) может быть вычислено по сле- дующим формулам: Для случая оператора с перечислением to: n Step = V Finish - V S tart + 1. 105
Для случая оператора с перечислением downto: n_Step = V_Start - V_Finish + 1. Пример: 1. Вычислить значение функции у = N! (у = 1х2хЗх.. ,.xN) для N = 100. Введем переменную целого типа у и присвоим ей начальное значе- ние 1. у := 1; Далее введем переменную целого типа i, которая будет возрастать от 2 до 100, и воспользуемся оператором цикла For: For i := 2 to 100 do у := y*i; Число шагов цикла = 100 — 2 + 1 = 99. Оператор цикла раскладывается на следующую последователь- ность операторов присваивания: 1 -й шаг (i = 2, у = 1): у := 1 *2 (после выполнения 1 -го шага у = 1 х 2 = 2); 2-й шаг (i = 3, у = 2): у := 2*3 (после выполнения 2-го шага у = 1 х 2 х 3 = 6); 3-й шаг (i = 4, у = 6): у := 6*4 (после выполнения 3-го шага у = 1 х 2 х 3 х х 4 = 24); 100-й шаг (i = 100, у = 991): у := 991*100 (после выполнения 100-го шага у=1 х2хЗх... х 100=1001). 2. Вычислить значение функции у = х”. Для вычисления функции воспользуемся операцией умножения и построим цикл, выполняющий эту операцию п раз: у:=1; For i := 1 to n do y:=y*x; 3. Найти максимальный делитель натурального числа к (за исклю- чением самого к). Алгоритм поиска максимального делителя можно построить сле- дующим образом: - вычислить целую часть от деления к пополам и присвоить пере- менной i значение целой части; - построить цикл с перечислением downto, V Start = i, V Finish = 1 и оператором цикла, вычисляющим остаток от деления к на i и прове- ряющим значение этого остатка. Как только остаток от деления будет первый раз равен 0, необходимо присвоить максимальному делителю (D) значение i и закончить выполнение цикла. Введем переменные целого типа k, i и D и построим последова- тельность операторов, реализующую предложенный алгоритм: 106
For i := к div 2 downto 1 do if к mod i = 0 then begin D : = i; goto 1 end; 1: ; Оператор While Синтаксис: •Оператор While > ::= While «условие» do <тело цикла» Условие — это логическое выражение, истинность которого прове- ряется в начале каждого шага цикла. Цикл будет выполняться до тех пор, пока значение логического выражения истинно. Если на первом же шаге значение логического выражения ложно, тело цикла не выполняется ни разу. В отличие от оператора For, в операторе While максимальное чис- ло шагов цикла заранее не известно. Оператор предусматривает измене- ние значения переменных, входящих в логическое выражение, внутри тела цикла. Рассмотрим Пример 3 предыдущего пункта. Решение этой задачи предполагает заранее неизвестное число шагов, которое понадобится для нахождения максимального делителя, поэтому использование в ал- горитме оператора While будет более эффективно. D:=0; i := k div 2; While D=0 do begin if k mod i = 0 then D := i; i := i-1 end; Оператор While будет выполняться до тех пор, пока D=0, т.е. пока не найден ни один делитель. Первый же найденный делитель будет мак- симальным, значение его занесется в переменную D, которая при этом станет отлична от нуля и сделает ложным логическое выражение в заго- ловке цикла. Оператор Repeat Синтаксис: «Оператор Repeat > ::= Repeat «тело цикла> until «условие» 107
Оператор Repeat отличается от остальных двух операторов цикла тем, что проверка условия продолжения цикла стоит после тела цикла. Это обеспечивает выполнение тела цикла хотя бы один раз. Служебные слова Repeat и until играют роль операторных скобок, поэтому тело цикла, состоящее из нескольких операторов, не нужно оформлять как составной оператор (как это необходимо делать в преды- дущих операторах цикла). Тело цикла выполняется до тех пор, пока логическое выражение, формирующее условие, не станет равным истинному значению. Рассмотрим следующий пример. Член ряда с номером п для п = 1,2,3,... определяется выражением 1 —. Написать последовательность операторов для вычисления суммы лх членов ряда от первого до члена с наименьшим номером, не превосхо- дящего 1(Л S:=0; N:=l; SN:=1; Repeat S:=S+SN; Inc(N); // Увеличение N на единицу (N:=N+1); SN := 1/Power(N,X); // Вычисление значения очередного члена ряда Until SN<= Power(10,-6); В этом цикле сначала происходит приращение суммы, а затем вы- числение значения очередного члена ряда. Это обусловлено тем, что первый же член ряда, значение которого будет больше, чем КГ6, не должен быть добавлен в сумму. Стандартные процедуры Break и Continue в операторах циклов В версиях Turbo Pascal 7.0 и Object Pascal появилась возможность использовать новые стандартные процедуры — Break и Continue. Процедура Break предназначена для осуществления принудитель- ного досрочного выхода из цикла. Например, цикл поиска максималь- ного делителя D числа к может выглядеть следующим образом: for i := к div 2 downto 1 do if к mod i = 0 then begin D : = i; // принудительный выход из цикла после // нахождении первого значения D Break end; 108
Процедура Continue принудительно вызывает начало новой итера- ции цикла, даже если предыдущая еще не закончена. Цикл поиска мак- симального нечетного делителя числа к может быть таким: for i := k div 2 downto 1 do begin if i mod 2=0 then Continue; // Переход на следующую итерацию, // если i четное if k mod i = 0 then begin D := i; Break end; end; Составной оператор Составные операторы задают порядок выполнения операторов, яв- ляющихся их элементами. Они должны выполняться в том порядке, в котором они записаны. Составные операторы обрабатываются, как один оператор, что имеет решающее значение там, где синтаксис языка допускает ис- пользование только одного оператора (например, в условном опера- торе if..then..else). Операторы заключаются в ограничители (опера- торные скобки) begin и end и отделяются друг от друга точками с запятой. Синтаксис: «Составной оператор» ::= begin «Последовательность операторов» end; «Последовательность операторов» ::= «Оператор»|«Последовательность операторе в»;«Оператор> Пример составного оператора: begin Z : = X; X : = 2* *Y; Y : = Z; end; И 2.6 Структурированные типы данных В языке Pascal определены следующие стандартные структуриро- ванные типы данных: • массив; • строка; • запись; • множество; • файл. 109
Массив Общий синтаксис объявления типа N-мерный массив: <имя> = array [<тип_индекса1>,<тип_индекса2>,...,<тип_индексаЫ>] of <тип_элемента> Из приведенного описания видно, что до объявления типа массива необходимо объявить типы индексов и элемента. Индексы массива должны принадлежать типу-диапазону, т.е. при- нимать множество последовательных значений порядкового типа. Элемент массива может принадлежать любому типу данных, в том числе и типу массив. Рассмотрим следующие примеры: туре Real_array = array [1..365] of real; Тип Realarray (массив из 365 вещественных чисел) можно исполь- зовать при решении задачи о среднесуточной температуре, введя, на- пример, переменную Temperature типа Real array: var Temperature : Real_array; Доступ к отдельному элементу массива осуществляется через ука- зание имени массива и значения индекса, заключенного в квадратные скобки: Temperature [ 5 ] : = 5.6; (среднесуточной температуре за 5-й день года присваивается значение 5.6). Значение индекса может быть задано выражением соответствую- щего типа: Temperature[п*7] := 10; В зависимости от значения переменной п (предположим, что п ме- няется от 1 до 52) значение 10 будет занесено в 7, 14, 21,..., 364 элемент массива. Рассмотрим фрагмент программы, позволяющий вычислить сред- несуточную температуру за год. Введем переменные Sum_Temp типа Real (для суммирования всех значений элементов массива), Mid_Temp типа Real (для вычисления средней температуры за год) и переменную-параметр цикла i типа integer. 110
Sum_Temp := 0; // первоначальное обнуление переменной For i := 1 to 365 do Sum_Temp := Sum_Temp + Temperature[ i ]; // цикл подсчета суммарной температуры Mid_Temp := Sum_Temp/365; // вычисление среднего значения Для решения подобной задачи, возможно, более наглядным будет введение двумерного массива вещественных чисел, где первый индекс будет меняться от 1 до 12 (в соответствии с количеством месяцев в го- ду), а второй индекс — от 1 до 31 (в соответствии с максимальным ко- личеством дней в месяце): Туре Real_array = array [1..12, 1..31] of real; В этом случае общее количество элементов в массиве — 12-31 = = 372, а доступ к отдельному элементу осуществляется через задание двух индексов: Temperature [2,7] — значение среднесуточной температуры 7 февраля. Для вычисления средней температуры за год необходимо объявить две переменные — параметры цикла — i и j, а фрагмент программы бу- дет иметь следующий вид: Sum_Temp := 0; // первоначальное обнуление переменной For i := 1 to 12 do For j := 1 to 31 do Sum_Temp := Sum_Temp + Temperature[i,j]; // вложенные циклы подсчета суммарной температуры Mid_Temp := Sum_Temp/(12*31); // вычисление среднего значения Задание значений массиву-константе При определении массива-константы значения элементов массива указываются в круглых скобках и разделяются запятыми. Если массив многомерный, то внешние скобки соответствуют самому левому индек- су, вложенные в них — следующему, и т. д. Например, для массивов, типы которых определяется предложе- ниями: Туре Vect_array = array [1..7] of integer; Matr_array = array [1..3, 1..4] of integer; 111
Могут быть заданы константы С1 и С2: const Cl: Vect_array = (1, 3, 5, 7, 9, 11, 13); С2: Matr_array = ((1, 3, 5, 7), (2, 4, 6, 8), (9, 11, 13, 15)); Константа С2 соответствует следующей структуре: 13 5 7 2 4 6 8 9 11 13 15 Операции над массивами Для одномерных массивов символов можно использовать опера- ции сравнения, даже если массивы не идентичных типов и имеют различный размер, например, для объявленных следующим образом массивов: var А : array [1..15] of char; В : array [1..10] of char; можно написать условный оператор вида: if А>В then Writeln(A) else Writein(В); Одному массиву можно присвоить значение другого, но только ес- ли они идентичных типов, например, если заданы массивы: var Al, А2 : array [1..15] of real; В : array [1..15] of real; то оператор присваивания допустим только между массивами А1 и А2 (Al := А2), даже несмотря на то, что размеры и типы элементов совпа- дают у всех трех массивов. Стандартные функции Для массивов целых и вещественных чисел в современных реа- лизациях языка (Object Pascal) определены следующие стандартные функции: 112
Функция Описание Тип массива Тип результата MaxlntValue(Data) Возвращает значение максимального элемента Целых чисел Целый MaxValue(Data) Возвращает значение максимального элемента Вещественных чисел Вещественный Mean(Data) Возвращает среднее арифметическое элемен- тов массива Вещественных чисел Вещественный MinIntValue(Data) Возвращает значение минимального элемента Целых чисел Целый MinValue(Data) Возвращает значение минимального элемента Вещественных чисел Вещественный Sum(Data) Возвращает сумму эле- ментов массива Вещественных чисел Вещественный SumInt(Data) Возвращает сумму эле- ментов массива Целых чисел Целый SumOfSquares(Data) Возвращает сумму квад- ратов элементов массива Вещественных чисел Вещественный Строка символов Тип строка символов (string) определяет последовательность сим- волов произвольной длины, записанную в одной строке программы и заключенную в одиночные кавычки (апострофы). Строку можно рас- сматривать как массив символов, однако, в связи с некоторыми особен- ностями использования строк по сравнению со стандартными массива- ми, символьный массив выделен в отдельный (строковый) тип данных. Строка символов, ничего не содержащая между апострофами, на- зывается пустой строкой. Два последовательных апострофа в строке символов обозначают один символ-апостроф. К символам в строке можно иметь доступ как к компонентам мас- сива, например, для объявленной строки MyString: var MyString : string; можно программировать следующие действия: MyString[l]:='Н'; MyString[2]:='Е'; MyString[3]:='L'; MyString [ 4 ] : = ' L' ; MyString [ 5 ] : = ’ О' ; Эта последовательность действий будет аналогична оператору MyString:='HELLO'; если в атрибуте длины строки в первом случае установить значение 5. 113
Атрибут длины строки содержится в символе с порядковым номе- ром 0 и представляет собой размер строки, числовое значение которого определяется как ord(string[0]). В стандарте языка строковый тип имеет фиксированный или дина- мический атрибут длины, но в любом случае длина строки не может превышать 255 символов. Фиксированный атрибут длины задается в квадратных скобках после слова string при объявлении типа. Например, строка, объявленная как var MyString : string[20]; может иметь длину не более 20 символов. Строковый тип, объявленный без атрибута длины, имеет установлен- ный по умолчанию атрибут длины, равный 255. Текущее значение атрибута длины можно получить с помощью стандартной функции Length. В качестве расширения стандарта языка в современных его реали- зациях разрешено вставлять в строку символов управляющие коды. Символ «#» с целой константой без знака в диапазоне от 0 до 255 обо- значает соответствующий этому значению символ в коде ASCII. Между символом «#» и целой константой не должно быть никаких разделителей. Аналогично, если несколько управляющих символов входят в стро- ку символов, то между ними не должно быть разделителей. Приведем несколько примеров строк символов: 'Object Pascal' 'You' '11 see! !! ' #13#10'Line 1'#13'Line 2'#7#7'The End!'#7#7 Длинные строки В целях преодоления ограничений на размер стандартных строк в 32-разрядных реализациях языка (в визуальной среде разработки при- ложений Delphi) введена поддержка длинных строк. В Object Pascal мо- гут быть объявлены следующие строковые типы: Тип Максимальная длина Описание ShortString 255 символов Соответствует стандартному типу String. Ка- ждый элемент строки имеет стандартный сим- вольный тип AnsiString ~2JI символов «Длинная» строка переменной длины. Память выделяется динамически. Каждый элемент строки имеет стандартный символьный тип Продолжение табл. 114
Тип Максимальная длина Описание WideString 2 символов «Длинная» строка переменной длины. Па- мять выделяется динамически. Каждый эле- мент строки представлен символом стандар- та UniCode «Длинные» строки ограничиваются нулевым терминатором, т.е. за- вершаются байтом с нулевым значением, что обеспечивает их совмес- тимость со строками языка С. Служебное слово String является общим при объявлении и корот- ких, и длинных строк. Например, объявление var S: string; создает переменную типа AnsiString, если компилятор поддерживает размещение длинных строк, и переменную типа ShortString в противном случае. Константы строкового типа Объявление типизированной константы строкового типа содержит максимальную длину строки и ее начальное значение: const HeadText : string [7] = 'Section'; Newline : string [2] = #13#10; TrueQue : string [3] = 'Yes'; Операции co строковыми типами Язык Pascal позволяет использовать знак операции «+» для объеди- нения двух строковых операндов. Результатом операции S+T, где S и Т имеют строковый тип, будет конкатенация S и Т. Операция Название Тип операнда Тип результата + Конкатенация Строковый, символьный Строковый Результат будет совместим с любым строковым типом (но не с символьным). Любые два значения строковых данных можно сравнить, поскольку все значения строковых данных совместимы. Операции отношения =, <>, <, >, <=, или >= применяются для сравне- 115
ния строк в соответствии с порядком расширенного набора символов кода ASCII. Отношение между любыми двумя строковыми значениями устанав- ливается согласно отношению порядка между значениями символов в оди- наковых позициях. Все операции отношения учитывают регистр. В двух строках разной длины каждый символ более длинной стро- ки, для которого нет соответствующего символа в более короткой стро- ке, принимает значение «больше», например: 'xs' больше, чем 'х', но ‘ххх’ меньше, чем ‘ху’. Пустые строки могут быть равны только другим пустым строкам, и они являются строками с наименьшим значением. Значения символьного типа совместимы со значениями строкового типа, и при их сравнении символьное значение обрабатывается как строковое значение длиной 1. Стандартные процедуры и функции для работы со строками Для работы с переменными строкового типа определены стандарт- ные процедуры и функции. Некоторые из них (наиболее часто исполь- зуемые) приведены в таблице (в столбце Статус значение F соответст- вует функции, Р — процедуре): Имя Ста- тус Назначение Аргументы (па- раметры) Результат CompareStr F Сравнение двух строк SI, S2-—сравни- ваемые строки <0, если51 <S2; = 0, если51 =S2; >0, ecnnSl >S2. Concat F Конкатенация (сложение) двух или более строк SI, S2,.... Sn — сцепляемые стро- ки Строка, равная Sl+S2 + ... + Sn Copy F Выделение под- строки Строка S; целые значения Index и Count Часть строки S, начиная с позиции Index, длиной Count Delete P Удаление части строки Строка S; целые значения Index и Count Новое значение строки S — без фрагмента, начиная с позиции Index, дли- ной Count Insert P Добавление под- строки в строку Строки Source и S, целое значение Index Новое значение стро- ки S после добавле- ния в нее Source, на- чиная с позиции Index Продолжение табл. 116
Имя Ста- тус Назначение Аргументы (па- раметры) Результат Length F Вычисление дли- ны строки СтрокаS Целое значение длины строки LowerCase F Преобразование строки к нижнему регистру СтрокаS Новое значение строки S — все сим- волы строчные Pos F Вычисляет пози- цию начала под- строки в строке Подстрока Substr, строкаS = 0, если Substr не содержится в S; целое >0 — позиция Substr в S Set Length P Назначает новую длину строки Строка S, целое значение NewLength Строка S с новым значением длины Str P Преобразует чи- словой тип в стро- ковый Целое или веще- ственное значение X, строкаS Строковая запись(в строке S) числа X StringOtChar F Формирует строку из последователь- ности символов Символ Ch, целое значение Count Строка символов Ch длиной Count Trim F Удаляет пробелы и управляющие символы в начале и в конце строки СтрокаS Новая строка TrimLeft F Удаляет пробелы и управляющие символы в начале строки Строка S Новая строка TrimRight F Удаляет пробелы и управляющие символы в конце строки СтрокаS Новая строка Uppercase F Преобразование строки к верхнему регистру СтрокаS Новое значение строки S — все сим- волы строчные Vai P Преобразование строкового типа в числовой Строка S, целое или вещественное значение V, целое значение Code Если Code =0, то целое или вещест- венное значение V, соответствующее строковой записи S. Если Code > 0, то преобразование невозможно и значе- ние Code указывает позицию ошибочного символа 117
Примеры использования строковых процедур и функций: выделение первого слова в предложении (разделитель слов — знак «пробел») S_Sentence := TrimLeft(S_Sentence); //Удаление пробелов в начале строки i := Pos(' S_Sentence); // Определение позиции первого // пробела в предложении S_Sentence if i > 0 then S_Word := Copy(S_Sentence, 1, i—1) else S_Word := S_Sentence; // В строковой переменной S_Word — // значение первого слова удаление из строки всех цифр L := Length(S_Sentence); // Вычисление длины строки i : = 1 ; while i <= L do if S_Sentence[i] in ['О'.-'Э'] then begin Delete(S_Sentence, i, 1); dec(L) //Уменьшение длины строки на 1 //в случае удаления цифры end else inc(i); подсчет количества букв ‘W’ в строке (независимо от регистра) N_w := 0; // Обнуление счетчика букв S_Sentence := Lowercase(S_Sentence); // Преобразование строки к // нижнему регистру L := Length(S_Sentence); // Подсчет длины строки for I := 1 to L do if S_Sentence[i] = 'w' then inc(N_w); // Увеличение счетчика букв на 1 Строка PChar Для совместимости с другими языками программирования (например, С) и средой Windows в расширениях стандарта языка Turbo Pascal и Object Pascal введен еще один вид строк — строки, оканчивающиеся нулевым байтом (#0). Этим строкам соответствует стандартный тип PChar. Фактиче- ски тип PChar эквивалентен массиву символов от 0 до N, где N — количе- ство символов, не считая завершающего нулевого байта: 118
PChar = array [0..N] of char; В отличие от типа string символ с индексом 0 в PChar-строке явля- ется первым, а символ с индексом N — завершающим с кодом 0. Для преобразования строчных типов в Object Pascal можно приме- нять следующие действия над типами string и PChar: пусть объявлены переменные var S_String: string; S_PChar: PChar; Тогда справедливы следующие операторы присваивания: S_String:= string(S_PChar); S_PChar:= PChar(S_String); В Object Pascal для работы с переменными типа PChar определены стандартные функции. Некоторые из них приведены в таблице: Имя Назначение Аргументы (параметры) Результат StrCat Объединение двух строк (добавление к PChar-строке Dest PChar-строки Source) PChar-строки Dest и Source Новая PChar- строка StrComp Сравнение двух PChar- строк Strl, Str2 — PChar-строки Целое число: <0, если Strl <Str2; =0, если Strl=Str2; >0, если Strl>Str2; StrCopy Копирование одной PChar- строки (Source) в другую (Etest) PChar-строки Dest и Source PChar-строка Dest StrECopy Копирование одной PChar- строки (Source) в другую (Dest). Возвращается указа- тель на нулевой байт новой строки. PChar-строки Dest и Source PChar-строка, начинающаяся с нулевого байта Dest StrEnd Возвращается указатель на PChar-строку PChar-строка Source PChar-строка, начинающаяся с нулевого байта Source StrLen Возвращается количество символов в PChar-строке (исключая нулевой байт) PChar-строка Source Целое число — количество сим- волов в строке StrLower Преобразование символов PChar-строки к нижнему регистру (все строчные) PChar-строка Source Преобразованная PChar-строка 119
Продолжение табл. Имя Назначение Аргументы (параметры) Результат StrMove Копирование заданного количества символов из одной PChar-строки (Source) в другую (Dest). PChar-строки Dest и Source; количество символов Count PChar-строка Dest StrPCopy Копирование строки Source типа string в PChar-строку Dest PChar-строка Dest, строка типа string Source PChar-строка Dest StrPos Возвращается указатель на начало PChar-строки STR2 внутри PChar-строки STR1. PChar-строки Strl, Str2 PChar-строка Str2 внутри Strl; nil — если вхож- дение не найдено StrRScan Возвращает указатель на последнее вхождение сим- вола Chr в PChar-строку Str PChar-строка Str, символ Chr PChar-строка, начиная с послед- него вхождения символа Chr в строку Str; nil — если вхождение не найдено StrScan Возвращает указатель на первое вхождение символа Chr в PChar-строку Str PChar-строка Str, символ Chr PChar-строка, начиная с первого вхождения симво- ла Chr в строку Str, nil — если вхождение не найдено StrUpper Преобразование символов PChar-строки к верхнему регистру (все прописные) PChar-строка Source Преобразованная PChar-строка Примеры использования функций: 1. Рассмотрим фрагменты программы: var VPStrl, VPStr2: PChar; // Объявление переменных типа PChar S: string; Следующий условный оператор проверяет вхождение PChar-строки VPStrl в PChar-строку VPStr2 и заносит значение в строку S: 120
if StrPos(VPStrl, VPStr2) <> nil then S:= 'Подстрока найдена' else S:= 'Подстрока не найдена'; Если перед выполнением оператора присвоены значения VPStrl= 'Object Pascal', a VPStr2='Pascal', то в строку S занесется значение ’ Подстрока найдена'. 2. Пример извлечения имени файла из полного пути. var // Объявления переменных FileName, Р: PChar; S: string; Фрагмент программы: Р := StrRScan(FileName, *\'); // Поиск последнего символа '\' // После выполнения оператора в // переменной Р // либо имя файла, либо nil if Р = nil then begin P := StrRScan(FileName, // Поиск последнего символа ':' // После выполнения оператора в // переменной Р // либо имя файла, либо nil if Р = nil then Р := FileName; end; S := string(P); // Занесение в S имени файла Запись Тип запись содержит установленное число компонент или полей, которые могут быть различных типов. Объявление типа запись задает для каждого поля идентификатор, который именует поле, и тип данных поля. Синтаксис: <3апись> ::= record <Список полей> end; <Список полей> ::= <Поле>| <Список полей>;<Поле> <Поле>::= «Список идентификаторов>:< Тип > «Список идентификаторов> ::= «Идентификатор», «Список идентификаторов», < Идентификатор» В соответствии с приведенным синтаксическим описанием поля в объявлении типа запись отделяются друг от друга точкой с запятой, а 121
при наличии в записи нескольких полей с одним и тем же типом данных идентификаторы этих полей могут быть описаны в одной строке и пере- числены при этом через запятую. Количество полей записи не ограни- чено. После объявления типа запись можно задать переменные или кон- станты этого типа, например: type TDateRec = record Year: Integer; Month: 1 .. 12; Day: 1 . . 31; end; TPerson = record FirstName, SecondName:string[20]; Sex: (Man, Woman); Age: integer end; var Person:TPerson; DateRec:TDateRec; Доступ к полям записи осуществляется путем указания имени пе- ременной (или константы) и имени поля, разделенным точкой, напри- мер, для переменных Person и DateRec, объявленных выше, операторы присваивания значений полям записываются следующим образом: Person.FirstName:='Victor'; Person.Sex := Man.- Person. Age := 20; DateRec.Year := 2002; Daterec.Day := 21; Объявление типа запись может иметь так называемую вариантную часть, воспринимаемую программой по-разному, в зависимости от те- кущего назначения. На самом деле, вариантная часть представляет со- бой фрагмент памяти, который может рассматриваться в разных случаях как разный набор полей. Вариантная часть в записи может быть только одна и в описании располагается в конце записи. Синтаксис типа запись с вариантной частью следующий: <3апись> ::= record «Фиксированная часть>; «Вариантная часть> end; «Фиксированная часть> ::= «Список полей> «Вариантная часть» ::=‘^Заголовок вариантной части> «Список вариантов> «Заголовок вариантной части> ::= case «Идентификатор переменной выбора варианта> : «Перечисляемый тип > of «Список вариантов> ::= «Вариант>|«Список вариантов>;«Вариант> «Вариант» ::= «Значение переменной выбора варианта>:(«Список полей») 122
В случае записи с вариантной частью фиксированная часть может отсутствовать. Переменная выбора варианта — это всегда переменная некоторого перечисляемого типа. Вариантная часть записи воспринимается как на- бор полей, соответствующий значению переменной выбора, поэтому доступ к информации может быть осуществлен более чем одним способом. При этом доступ к вариантным и фиксированным полям один и тот же. Рассмотрим несколько примеров записей с вариантной частью: туре TProfesion = (Student, Engeneer); TPerson = record FirstName, LastName: string[20J; BirthDay: DateRec; case Profesion: TProfesion of Student: (Course: integer; (Faculty : string[20]); Engeneer: (Speciality: string[30J) end; TFigure = (Rectangle, Triangle, Circle); TColor = (Red, Green, Yellow); TPolygon = record X, Y: Real; Color: TColor; case Kind: TFigure of Rectangle: (RHeight, RWidth: Real); Triangle: (TSizel, TSize2, Angle: Real); Circle: (Radius: Real); end; var Student_Person, Engeneer_Person: TPerson; MyRect, MyTriangle: TPolygon; В стандарте языка прежде, чем использовать в программе один из вариантов, необходимо задать соответствующее значение переменной выбора варианта, например: MyRect.Color := Red; Kind := Rectangle; MyRec t.RHeight := 5.23; В современных реализациях языка эту операцию делать не надо: нужный вариант однозначно определяется именем поля: Student_Person.Course := 5; Engeneer_Person.Speciality := 'Programmer'; MyTriangle.Angle := 1.0; 123
При объявлении типа записи с вариантной частью допускается не вводить специальную переменную выбора, а использовать стандартные перечисляемые типы и их возможные значения, например: Var TConvert = record case integer of l:(CPoint : longint); 2:(Carray : array [1..4] of byte) end; Константы с типом запись Объявление константы с типом запись содержит идентификатор и значение каждого поля, заключенные в скобки и разделенные точками с запятой. Синтаксис объявления константы следующий: <Константа-запись> ::= <Идентификатор>:<Тип-запись> = (<Список значений полей>); «Список значений полей> ::= «Значение поля>|«Список значений полей>;«3начение поля> «Значение поля> ::= «Идентификатор поля>: «Значение» Рассмотрим несколько примеров констант-записей: type TPoint = record X, Y : Real; end; TVector = array [0..1] of TPoint; TMonth = (Jan, Feb, Mar, Apr, May, Jun, Jly, Aug, Sep, Oct, Nov, Dec); TDateRec = record Day : 1..31; Month : TMonth; Year : 1950..2050; end; const Origin : TPoint = (X : 0.0; Y : 0.0); Line ; TVector= ((X : -3.1; Y : 1.5), (X : 5.8; Y : 3.0)); SomDay : TDateRec = (Day: 2; Month: Dec; Year: 2002); Поля должны указываться в том же порядке, как они следуют в объявлении типа запись. Если запись содержит вариантную часть, то можно указывать только поля одного выбранного варианта. 124
Оператор над записями (with) В операциях над записями оператор with удобно использовать для краткого обращения к полям записи. В операторе with к полям одной или более конкретных переменных типа запись можно обращаться, ис- пользуя только идентификаторы полей. Оператор with имеет следующий синтаксис: «Оператор with> ::= with «Идентификатор записи» do «Оператор»; Приведем пример оператора with: with DateRec do if Month = 12 then begin Month := 1; Year := Year + 1; end else Month := Month + 1; Это эквивалентно следующей записи условного оператора: if DateRec.Month = 12 then begin DateRec.Month := 1; DateRec.Year := DateRec.Year + 1; end else DateRec.Month := DateRec.Month + 1; В операторе with сначала проводится проверка каждого идентифи- катора переменной на соответствие полю указанной записи. Если иден- тификатор переменной совпадает с именем поля, то переменная всегда интерпретируется, как поле записи. Пусть объявлены следующие переменные: type TPoint = record X, Y: Integer; end; var X : TPoint; Y : Integer; В этом случае и к X, и к Y можно обращаться, как к переменной или как к полю записи. Например, оператор 125
with X do begin X := 1; Y := 100; end; эквивалентен последовательности операторов X.X := 1; X.Y := 100; так как X и Y внутри оператора with воспринимаются как поля записи типа TPoint. Множество Диапазон значений типа множество представляет собой множество всевозможных сочетаний объектов заданного порядкового типа. В этом случае заданный порядковый тип называется базовым типом. Каждое возможное значение типа множество является подмноже- ством возможных значений базового типа. Переменная типа множество может принимать как все значения множества, так и ни одного. Синтаксис: <Гип множество» ::= set of «Порядковый тип>; Базовый тип не должен иметь более 256 возможных значений, и порядковые значения верхней и нижней границы базового типа не должны превышать диапазона от 0 до 255. В силу этого базовыми типа- ми множества не могут быть типы Shorlnt, Integer, Longlnt, Word. Любой множественный тип может принимать значение [], которое называется пустым множеством. Примеры множеств: type Number = set of 1 0' . . ' 9'; Digit = set of 0..9; Oper = set of (Plus, Minus, Multiply, Divide); Тип множество можно ввести и непосредственно при объявлении переменных: var Digit :set of 0..9; Значение переменной типа множество присваивается путем пере- числения в квадратных скобках через запятую некоторых значений (или диапазонов значений) базового типа, например: 126
Digit := [1,3,5,7,9]; Digit := [0..5, 7, 8]; Константы типа множества Объявление константы типа множества может содержать несколько элементов, заключенных в квадратные скобки и разделенных запятыми. Каждый элемент такой константы представляет собой константу или диапазон, состоящий из 2-х констант, разделенных двумя точками, на- пример: type Digits = set of 0..9; Letters = set of 'A'..'Z'; const EvenDigits : Digits = [0, 2, 4, 6, 8]; Vowels : Letters= ['A', 'E', 'I', 'O’, 'U', 'Y']; HexDigits : set of 'O'.-'z' = ['O'..’9', 'A'..'F', 'a'..'f']; Операции над множествами Результаты операций над множествами подчиняются правилам ал- гебры множеств. Операция Назначение Типы операндов Действие 4- Объединение Множества с со- вместимыми типами Значение С принадлежит А+В только в том случае, если С принадлежит А или В. — Разность Множества с со- вместимыми типами Значение С принадлежит A-В только в том случае, если С принадлежит А, но не принадлежит В. * Пересечение Множества с со- вместимыми типами Значение С принадлежит А*В только в том случае, если С принадлежит и множеству А, и множе- ству В. Сравнение множеств Если операндами являются множества А и В, то при их сравнении получаются следующие результаты: 127
Операция Действие = Выражение А = В истинно только тогда, когда А и В содержат одни и те же элементы о Выражение А о В истинно только тогда, когда хотя бы один эле- мент множества А не содержится во множестве В или наоборот <= Выражение А <= В истинно, если каждый элемент множества А является также и элементом множества В >= Выражение А >= В истинно, когда каждый элемент множества В является также и элементом множества А in Проверка на принадлежность множеству Выражение х in А истинно, когда значение элемента порядкового типа является элементом типа множества, в противном случае выражение ложно. Тип данных файл В практике программирования часто встречаются задачи, при ре- шении которых можно хранить обрабатываемые данные на внешнем носителе. В этом случае данные оформляются в виде файлов. Под фай- лом понимают любой логически непрерывный набор данных, разме- щенный на внешнем запоминающем устройстве (например, на магнит- ном диске). Так, исходные данные для работы программы могут быть организованы в виде файла или совокупности файлов. Файлом может быть и результат работы программы. Поддержка работы с внешними файлами внутри программы в язы- ке Pascal осуществляется с помощью специализированных переменных, которым присваивается тип данных файл. Тип данных файл описывает файл как линейную последователь- ность однотипных компонентов, размещенных на внешнем запоминаю- щем устройстве, которые могут иметь любой тип данных за исключени- ем типа файл или структурного типа, содержащего компонент с типом файл. В отличие от массива длина файла, т.е. количество компонентов, не задается, а место элемента не определяется индексом. Синтаксис объявления типа данных файл следующий: <Тип файл> ::= «Идентификатор типа> = file of <Тип компоненты>; Если слово of и тип компонента опущены, то объявляется нетипи- зированный файл. Нетипизированные файлы используются для доступа к любому файлу на внешнем устройстве, независимо от его внутреннего формата. Стандартный файловый тип Text определяет файл, содержащий символы, объединенные в строки. Следует иметь в виду, что тип Text не идентичен типу file of char. 128
Приведем примеры объявлений файловых типов: type FileNuniber = file of Longint; // Файл длинных целых чисел FileDigit = file of ' 0 ' . . ' 9 ' ; // Файл символов-цифр TPoint = record X,Y:real end; FilePoint = file of TPoint; // Файл записей TPoint Объявив файловый тип, можно определить переменные файлового типа: var Filel: FileNumber; File2: FilePoint; File3: text; // Файл стандартного типа text File4: file; // Переменная нетипизированного // файла Переменные файлового типа имеют специфическое применение. Над ними нельзя выполнять никаких операций (например, присваивать значение, сравнивать), они используются лишь в качестве параметров специальных процедур работы с файлами. 2.7. Динамические данные Рассматриваемые до сих пор типы данных описывали фрагменты оперативной памяти, которые выделялись по мере объявления в про- грамме переменных того или иного типа. Такие переменные называются статическими, после объявления в разделе переменных под них отво- дится столько байтов оперативной памяти, сколько требует тип пере- менной. Размерами выделенной таким образом памяти уже нельзя управлять из программы, можно лишь присваивать таким переменным некоторые значения. Такое статическое объявление не всегда удобно при решении мно- гих практических задач. Например, когда речь идет об обработке зара- нее неизвестного количества однотипных данных, или о построении связанных структур, таких как списки, деревья и т.п. В этих случаях выделение максимальных размеров памяти приводит к неэффективной 129
работе программы, а порой и нельзя заранее предугадать, какое макси- мальное количество памяти потребуется для размещения данных. В языке Pascal предусмотрена возможность размещения перемен- ных в памяти по ходу выполнения программы. Для таких целей выделе- на особая область оперативной памяти, которая называется динамиче- ской, а переменные, размещаемые в ней, называются динамическими переменными, т.е. в процессе обработки данные можно размещать и (после обработки) удалять из оперативной памяти. Такая языковая возможность связана с наличием особых типов данных — указателей. Тип указатель Тип данных указатель задает множество значений, которые соот- ветствуют адресам переменных определенного типа, называемого базо- вым типом. Синтаксис: <Тип указатель> ::= л<Базовый тип>; «Базовый тип> ::= «Идентификатор типа>; Например: type TCoord = record X, Y: real end; PTCoord = ATCoord; var Pl, P2, P3 : PTCoord; Point : TCoord; P : Pointer; При объявлении переменной типа PTCoord резервируется память под физический адрес переменной типа TCoord (но не под сам тип TCoord). Присвоить значение переменной типа указатель можно двумя путями: во-первых, разместить в памяти новую (динамическую) перемен- ную с помощью стандартной процедуры New с параметром типа указа- тель, в который после выполнения процедуры будет занесено значение адреса размещения; во-вторых, присвоить значение адреса размещения объявленной переменной базового типа (т.е. статической переменной), используя знак операции «@». Процедура New отводит новую область в динамической памяти для переменной и сохраняет адрес этой области в переменной типа указатель. 130
Знак операции «@» устанавливает переменную типа указатель на область памяти, содержащую объявленную переменную. Зарезервированное слово nil обозначает константу с пустым значе- нием указателя. Встроенный тип Pointer обозначает нетипизированный указатель, т.е. указатель, который не указывает ни на какой определенный тип. Как и значение, обозначаемое словом nil, значения типа Pointer совместимы со всеми другими типами указателей. Для приведенных выше объявлений справедливы следующие опе- раторы: New(Pl); Р2 := ©Point; New(P); РЗ := Р; В языке определен ряд стандартных процедур и функций для пере- менных типа указатель. Перечень их представлен в таблице: Название Ста- тус Назначение Параметры Результат Addr(X) F Возвращает адрес статической пере- менной X — параметр любого типа (включая про- цедурный) Указатель типа Pointer Ptr(Address) F Преобразует значе- ние целого типа в указатель Address — па- раметр типа Integer Указатель типа Pointer New(P) P Размещает в памяти динамическую пе- ременную типа, базового для пара- метра Р. Размер выделяемой памяти совпадает с разме- ром базового типа. Р — параметр типа указатель В параметр Р заносится значение адреса пере- менной ба- зового типа. Dispose(P) P Освобождение па- мяти, на которую указывает параметр. Размер освобож- даемой памяти сов- падает с размером базового типа. Р — параметр типа указатель После вы- полнения процедуры значение Р не определе- но. 131
Продолжение табл. Название Ста- тус Назначение Параметры Результат GetMem(P, Size) p Выделяет память заданного размера под переменную Р — параметр типа указатель (включая тип Pointer); Size - параметр типа Integer В параметр Р заносится значение адреса пе- ременной FreeMem(P[,Size]) p Освобождает па- мять заданного размера, занятую переменной, на которую указыва- ет параметр. Если размер не указан, освобождается память, предвари- тельно выделен- ная с помощью процедуры GetMem Р — параметр типа указатель (включая тип Pointer); Size - параметр типа Integer После вы- полнения процедуры значение Р не опреде- лено Указатели, определяющие адреса переменных разного типа, сами являются переменными разного типа, и для них не допустимо использо- вание оператора присваивания. Исключение составляет тип Pointer, ко- торый совместим по присваиванию с любым другим типом указатель. К указателям одного типа можно применять операции сравнения = и о. Чтобы получить непосредственно значение переменной, адрес ко- торой содержится в указателе, необходимо после имени указателя по- ставить знак «Л», например: Point := Р1л; Р2Л := Point; Константы с типом указатель Объявление константы типа указатель обычно содержит знак опе- рации «@» и идентификатор константного значения некоторого типа, например: type TDirection = (Left, Right, Up, Down); TNodePtr = ЛЫос1е; 132
TNode = record Value : TDirection; Next : TNodePtr; end; const Nl: TNode = (Value: Down; Next: nil); N2: TNode = (Value: Up; Next: @N1) ; N3: TNode = (Value: Right; Next: @N2) ; N4: TNode = (Value: Left; Next: @N3); DirectionTable: TNodePtr = @N4; В приведенном примере необходимо обратить внимание на то, что синтаксис языка разрешает объявлять тип-указатель перед объявлением базового типа, например, в случае, когда речь идет о формировании по- следовательностей структур данных типа запись со ссылками друг на друга. Работа с динамическими структурами данных Рассмотрим примеры создания динамических структур данных, т.е. структур данных с переменным числом элементов. Чаще всего при ре- шении практических задач используют связанные линейные (списки, очереди) или иерархические структуры (деревья). Каждый элемент та- кой структуры состоит всегда из двух частей: - собственно информационной части, содержащей данные, подле- жащие обработке; - ссылочной части, содержащей набор ссылок на соседние элемен- ты структуры (количество и содержимое ссылок диктуется разновидно- стью структуры). Наилучшим для хранения такого рода элементов представляется тип данных запись, в которой одно или несколько полей отведены под указатели (см. описание записи TNode). В этом случае объявление типа указатель должно предшествовать объявлению типа запись, как показа- но в предыдущем примере, и синтаксис языка это разрешает. При работе с динамическими структурами данных необходимо программировать выполнение следующих основных операций: - добавление элемента в структуру; - исключение элемента из структуры; - поиск определенного элемента структуры по заданному признаку. Рассмотрим задачу формирование линейной структуры, называе- мой однонаправленным списком. Это структура, каждый элемент кото- рой имеет ссылку на следующий за ним. У последнего элемента такая ссылка равна пустому значению (nil). Пусть элемент структуры в информационной части содержит стро- ку. Тогда тип такого элемента можно описать следующим образом: 133
type TNodePtr = ~TNode; TNode = record Value : string; Next : TNodePtr; end; Для обеспечения работы co списком введем следующие перемен- ные: var NodeB, NodeE, NodeCur: TNodePtr; S: string; где NodeB — указатель на первый элемент списка, NodeE — указатель на последний элемент списка, NodeCur — указатель на текущий эле- мент списка. Перед формирование списка необходимо обнулить указатели на начало и конец списка, чтобы показать, что в списке нет элементов: NodeB := nil; NodeE := nil; Теперь необходимо разработать процедуры, выполняющие основ- ные операции работы со списком. Начнем с процедуры внесения элемента в список. Чтобы внести новый элемент в однонаправленный список со ссылкой на следующий, необходимо знать адрес того элемента, после которого необходимо до- бавить новый. Значит, одним из параметров процедуры должен быть указатель типа TNodePtr. В качестве другого параметра укажем значе- ние информационной части (параметр типа string). Объявление процедуры имеет вид (проверка Node на значение nil вынесена за рамки процедуры): procedure NewNode(Node: TNodePtr; Inf: string); Процедура должна обеспечить выполнение следующих действий: 1. Выделение фрагмента памяти под новый элемент: New(N); // N — локальная переменная // типа TnodePtr 2. Занесение в новый элемент информации: N*.Value := Inf; 3. Перенос в поле ссылки на следующий нового элемента значения из элемента Node: N^.Next := Node; 134
4. Занесение в поле ссылки на следующий Node адреса размещения нового элемента: NodeА.Next := N; Общее описание процедуры: procedure NewNode(Node: TNodePtr; Inf: string); var N: TNodePtr; begin New(N); N*.Value := Inf; NA.Next := Node; Node''.Next := N; end; По аналогии с процедурой внесения элемента опишем процедуру удаления элемента, размещенного после элемента Node (проверка Node на значение nil вынесена за рамки процедуры): procedure DeleteNodefNode: TNodePtr); var N: TNodePtr; Begin N := NodeA.Next; // Занесение в переменную N // адреса удаляемого элемента NodeA.Next := NA.Next; // Обеспечение обхода по ссылкам // удаляемого элемента Dispose(N); // Освобождение памяти end; Последняя операция — поиск элемента по заданному значению — может быть реализована с помощью функции, выполняющей такую по- следовательность действий: function SearchNode(inf: string): TNodePtr; var N: TNodePtr; begin N : = NodeB; // Занесение в переменную N // адреса начала списка While N <> nil do // Организация цикла по // элементам списка if (NA.Value = inf) then Break // Break — досрочный выход // из цикла else N := NA.Next; // Обеспечение перехода //на следующий элемента 135
SearchNode := N; end; // Результат функции — nil или // адрес найденного элемента Приступим к формированию списка. Для размещения первого эле- мента используем процедуру New: New(NodeB) ; Nodes'4. Value := S; //В переменную S предварительно // занесено значение // информационной составляющей // элемента Nodes'4. Next := nil; NodeE := NodeB; Добавить новый элемент в конец списка теперь можно с помощью процедуры: NewNode(NodeE, S) ; Пусть теперь необходимо найти элемент, в поле Value которого на- ходится значение ‘Волга’ и вставить после него в список элемент со значением ‘Урал’ в поле Value. Эта задача решается следующей после- довательностью операторов: NodeCur := SearchNode(•Волга 1); If NodeCur о nil then NewNode(NodeCur, ’Урал’); Удалим теперь из списка элемент, следующий за элементом со зна- чением Ока • в поле Value, если у следующего за ним элемента значе- ние в поле Value то же самое: NodeCur := SearchNode(’Ока’); if NodeCur о nil then // если элемент со значением //'Ока' в поле Value найден if NodeCur'4.Next о nil then // если есть следующий элемент if NodeCur74 .Next'4 .Value = 'Ока' // если в поле Value следующего // элемента — значение 'Ока' then DeleteNode(NodeCur); Первый элемент из списка удаляется путем переназначения первого элемента: NodeCur := NodeB; NodeB : = NodeB74. Next ; Dispose(NodeCur); Для удаления последнего необходимо сначала найти предпослед- ний, т.е. тот, у которого в поле Next стоит значение NodeE: 136
NodeCur := NodeB; While NodeCur''. Next <> NodeE do NodeCur : = NodeCur''. Next ; Затем необходимо переопределить указатель на последний элемент и удалить последний элемент: NodeE := NodeCur; DeleteNode(NodeCur); Графическая интерпретация работы с однонаправленным списком приведена на рис. 2.1, 2.2. Рис. 2.1. Добавление элемента в список. Пунктиром обозначена связь, переносимая в новый элемент Рис. 2.2. Удаление элемента из списка. Пунктиром обозначены удаляемые связи И 2.8. Процедуры и функции В языке Pascal существуют две разновидности подпрограмм — процедуры и функции. Каждое объявление процедуры или функции содержит обязательный заголовок, за которым следуют разделы локаль- ных объявлений (аналогичных разделам объявлений программы) и со- ставной оператор (блок), реализующий алгоритм подпрограммы. Вызов процедуры на исполнение активизируется с помощью опе- ратора процедуры. Функция активизируется при вычислении выраже- 137
ния, содержащего вызов функции, а возвращаемое функцией значение подставляется в это выражение. Объявление процедур Синтаксис объявления процедуры следующий: «Объявление процедуры > ::= «Заголовок процедуры»;«Тело процедуры»; «Заголовок процедуры» ::= procedure «Идентификатор» «Список формальных параметров» «Тело процедуры» ::= «Раздел локальных объявлений» «Составной оператор» В заголовке процедуры указывается имя процедуры и описывается список формальных параметров (если он присутствует). За заголовком может следовать раздел локальных объявлений (ме- ток, типов, констант, переменных, вложенных процедур и функций). Раздел локальных объявлений может отсутствовать в том случае, если процедура использует только глобальные объявления. Запуск процедуры на исполнение осуществляется с помощью опе- ратора процедуры, в котором содержатся имя процедуры и фактические параметры. Последовательность операторов, реализующих алгоритм процеду- ры, записывается внутри составного оператора (блока) процедуры. Если в каком-либо операторе внутри блока процедуры используется иденти- фикатор самой процедуры, то процедура будет выполняться рекурсивно, т.е. при выполнении будет обращаться сама к себе. Приведем пример объявления процедуры: // Преобразование целого числа в строку // восьмеричного представления procedure OctString(Nmb: Integer; var S: string); // Заголовок процедуры co списком формальных // параметров: Nmb — исходное целое число; // S — строка для записи результата // преобразования var Р: Integer; // Объявление локальной переменной Р begin Р := Abs(Nmb); S : = ' ' ; // «пустая» строка repeat S := chr(P mod 8) + S; P := P div 8; until P = 0; if Nmb < 0 then S := + S; end; 138
В дальнейшем для вызова процедуры из основной программы или другой подпрограммы необходимо записать оператор процедуры со списком фактических параметров, которые должны совпадать по коли- честву и типам с формальными параметрами процедуры. Например, в результате выполнения фрагмента программы: OctString(InpNmb,Resultstring); Resultstring := 'Число '+str(InpNmb)+ 'в восьмеричной с.с. равно '+ResultString; в переменной ResultString типа String формируется запись старого и нового представления целого числа InpNmb. Объявления функций Функция — это подпрограмма, вычисляющая и возвращающая не- которое значение. Оператор функции может быть использован в качест- ве операнда при построении выражения. Синтаксис объявления функции следующий: «Объявление функции> ::= «Заголовок функции >;<Тело функции:»; «Заголовок функции> ::= function «Идентификатор:» «Список формальных параметров»: «Тип результата» «Тип результата» ::= «Идентификатор типа» «Тело функции» ::= «Раздел локальных обьявленийхСоставной оператор» Основное отличие заголовка функции от заголовка процедуры (по- мимо используемого для объявления служебного слова) в том, что заго- ловок функции дополнительно содержит указание на тип возвращаемо- го функцией значения — тип результата. Оператор функции при ее вызове обычно стоит либо в правой час- ти оператора присваивания, либо входит на правах операнда в выраже- ние, либо указывается в качестве фактического параметра при вызове другой подпрограммы. Вызов функции содержит идентификатор функ- ции и список фактических параметров, совпадающий по размеру и ти- пам со списком формальных параметров. После выполнения тела функ- ции возвращается значение, тип которого совпадает с типом результата функции. Операторная часть тела функции содержит операторы, реализую- щие алгоритм получения результата объявленного типа, при этом в опе- раторной части должен находиться по крайней мере один оператор при- сваивания, в котором в левой части стоит идентификатор функции. Ре- зультатом выполнения функции будет последнее значение, присваиваемое идентификатору функции. Если такого оператора при- сваивания нет или он не выполняется, то возвращаемое функцией зна- чение не будет определено. 139
Если идентификатор функции используется для вызова функции внутри операторной части тела функции, то функция выполняется ре- курсивно. Приведем пример объявления функции: // Функция вычисления суммы квадратов первых // N чисел натурального ряда function SumSqr(N: Integer): integer; var S, i: Integer; begin S := 1; for i : = 2 to N do S := S+i*i; SumSqr := S; // Присваивание значения результату // функции end; Для вызова функции из основной программы или из другой под- программы (например, при вычислении значения выражения) необхо- димо в выражении указать оператор функции со списком фактических параметров: LineLen := LoglO(X)/SumSqr(I); Здесь LoglO(X) — вызов стандартной функции вычисления десятичного логарифма фактического параметра X; SumSqr(I) — вызов функции вычисления суммы квадратов с фак- тическим параметром I. Переменная Result в функции Реализация языка в версии Object Pascal облегчает программирова- ние функций путем автоматического объявления в каждой из них ло- кальной переменной Result. Эта переменная имеет тот же тип, что и ре- зультат функции. Присваивание значения переменной Result аналогич- но определению значения функции. Преимущество использования этой переменной при вычислении значения функции в том, что локальная переменная Result может стоять в правой части оператора присваивания и не вызывает при этом рекурсивного вычисления функции (в отличие от идентификатора функции, появление которого в вычисляемом выра- жении означает рекурсивный вызов функции). Например, представленная выше функция вычисления суммы квадратов натурального ряда может быть преобразована следующим образом: 140
function SumSqr(N: Integer): integer; var i: Integer; begin Result := 1; for i := 2 to N do Result := Result +i*i; // После выхода из цикла в переменной Result — // значение функции end; Заметим, что уменьшилось количество локальных переменных, не- обходимых для реализации алгоритма, и сам алгоритм стал короче на один оператор. Формальные и фактические параметры Объявление процедуры или функции содержит список формальных параметров. Каждый параметр из списка формальных параметров явля- ется локальным по отношению к процедуре или функции, для которой он объявлен. Это означает, что глобальные переменные, имена которых совпадают с именами формальных параметров, становятся недоступны- ми для использования в процедуре или функции. Отдельные объявления параметров в списке разделяются точкой с запятой. Синтаксис списка формальных параметров следующий: <Список формальных параметров» «Объявление параметра»! «Список формальных параметров»; «Объявление параметра» Параметры- значения Формальные параметры-значения действуют как переменные, ло- кальные по отношению к процедуре или функции. Изменения формаль- ных параметров-значений не влияют на значения соответствующих фактических параметров. Синтаксис объявления параметра-значения следующий: «Объявление параметра-значения» ::= «Список идентификаторов»:«Тип параметра» «Тип параметра» ::= «Идентификатор типа» «Список идентификаторов» ::= «Идентификатор»|«Список идентификато- ров», «Идентификатор» Фактический параметр, соответствующий параметру-значению, в операторе процедуры или вызове функции должен быть выражением, а 141
его значение не может быть файлового типа. Фактический параметр должен быть совместим по присваиванию с типом формального пара- метра-значения. Рассмотрим пример объявления функции с параметром-значением: // Функция вычисления количества запятых в строке function NmbComma( S: string ): integer; var i, L, Nmb :integer; begin Nmb := 0; L := Length(S); for i := 1 to L do if S[i] = then inc(Nmb); NmbComma := Nmb; end; Параметры-переменные Формальные параметры-переменные служат для модификации внутри подпрограммы значений соответствующих фактических пара- метров. Формальный параметр-переменная представляет фактическую переменную во время выполнения процедуры или функции, поэтому все изменения значения формального параметра отражаются на фактиче- ском параметре. Синтаксис объявления параметров-переменных следующий: Объявление параметров-переменных» ::= var Описок идентификаторов»: <Тип параметра»! var Описок идентификаторов» Внутри подпрограммы любое упоминание формального параметра- переменной обеспечивает доступ к самому фактическому параметру. Тип фактического параметра должен быть тождествен типу формально- го параметра-переменной (это ограничение можно обойти через исполь- зование параметров-переменных без типа). Файловые типы могут пере- даваться только как параметры-переменные. Рассмотрим пример объявления процедуры с параметром- переменной: // Процедура замены запятых на точки с запятой в строке procedure NmbComma(var S: string ); var i, L:integer; begin L := Length(S); for i := 1 to L do if S[i] = ',' then S[i] = ’;'; end; В результате применения этой процедуры к строке S Sentence 142
NnibComma (S_Sentence) ; будет изменено содержимое строки. Если до вызова процедуры в строке находилось значение ‘лимон, апельсин, банан’, то после вызова проце- дуры значение S Sentence будет равно ‘лимон; апельсин; банан’. Как видно из синтаксической формулы, объявление параметров- переменных может не сопровождаться указанием типа. Когда формаль- ный параметр является параметром-переменной без типа, соответст- вующий фактический параметр может быть переменной любого типа, а ответственность за правильность использования параметра ложится при этом на программиста. Внутри процедуры или функции такой параметр-переменная не имеет типа, т.е. он не совместим с переменными всех других типов до тех пор, пока ему не присвоен определенный тип. Рассмотрим пример передачи параметров-переменных без типа: function VarEqual(var Source, Dest; EqSize: Word): Boolean; // Функция сравнения переменных // Source и Dest длины EqSize type ArrBytes = array [0 .. 1000] of Byte; // Объявление вспомогательного // локального типа данных var N: Integer; begin N := 0; while (N < EqSize) and (ArrBytes(Dest)[N] = ArrBytes(Source)[N]) do Inc(N); VarEqual := N = EqSize; end; Эту функцию можно использовать для сравнения любых двух пе- ременных, размер которых не превышает 1000 байт. Например, в про- грамме присутствуют следующие объявления: type TVector = arrayfl .. 10] of Integer; TPoint = record X, Y: Integer; end; var Vectorl, Vector2: TVector; Pointl, Point2: TPoint; Тогда вызов функции: 143
VarEqual(Vectorl, Vector?, SizeOf(Vector)) обеспечит сравнение массивов Vectorl и Vector?. Вызов функции VarEqual(Vectorl, Vector?, SizeOf(Integer)*10) обеспечит сравнение первых 10 элементов массивов Vectorl и Vector?. Вызов функции VarEqual(Pointl, Point?, SizeOf(TPoint)) обеспечит сравнение пере- менных Pointl и Point?. Вызов функции VarEqual(Vectorl[l], Point?.Y, SizeOf(integer)) обеспечит сравнение значений Vectorl [1] и Point?.Y. Параметры-константы Формальные параметры-константы носят пограничный характер между двумя категориями. С одной стороны, это параметры, которые передаются в подпрограмму по наименованию, т.е. ссылкой на глобаль- ное размещение фактического параметра, но, с другой стороны, внутри подпрограммы действует запрет на изменение значения параметра. Ис- пользовать параметры-константы удобно вместо параметров-значений, когда параметр характеризуется большим размером занимаемой памяти. Параметры-константы введены в описание языка, только начиная с вер- сии Turbo Pascal 7.0. Синтаксис объявления параметров-констант следующий: «Объявление параметров-переменных> ::= const <Список идентификаторов»: <Тип параметра»! const <Список идентификаторов» В рассмотренном ранее примере объявления функции с парамет- ром-значением более эффективным будет использование параметра- константы, который обеспечит передачу в процедуру адреса размеще- ния строки: // Функция вычисления количества запятых в строке function NmbComma( const S: string ) : integer; var i, L, Nmb :integer; begin Nmb := 0; L := Length(S); for i : = 1 to L do if S[i] = then inc(Nmb); NmbComma := Nmb; end; Аналогично объявлению параметров-переменных, в объявлении параметров-констант может отсутствовать указание типа. В этом случае 144
фактический параметр может быть переменной любого типа, и исполь- зование его внутри подпрограммы предполагает предварительное пре- образование к конкретному типу. Массивы открытого типа В версии языка Turbo Pascal 7.0 введено понятие открытого масси- ва, т.е. массива, который можно передавать в процедуру и функцию в качестве параметра-значения или параметра-константы без указания длины. Формальным параметром в этом случае может выступать любой массив, состоящий из элементов того же типа, что и открытый массив. Введение такого параметра сделало возможным разрабатывать подпро- граммы, обрабатывающие массивы однотипных элементов любой раз- мерности. В объявлении открытого массива как параметра подпрограммы от- сутствует указание типа индекса (в отличие от объявления простого массива). Следует также иметь в виду, что нумерация элементов откры- того массива начинается с 0. Максимальное значение индекса фактиче- ского массива определяется с помощью функции High(X). Функция High(X) и симметричная ей функция Low(X) служат для получение максимального и, соответственно, минимального значения величины в зависимости от ее типа. Зависимость возвращаемых функ- циями значений от типа параметра X представлена в следующей таблице: Тип параметра X High(X) Low(X) Простой тип Максимальное значение из области определения Минимальное значение из области определения Массив Максимальное значение из области определения индекса Минимальное значение из области определения ин- декса Строка типа String Объявленное количество символов в строке 0 только для строк типа shortstring Открытый массив Значение целого типа, рав- ное количеству элементов фактического параметра- массива без единицы (т.к. нумерация элементов мас- сива начинается с нуля) 0 Параметр под- программы типа String Значение целого типа, рав- ное количеству символов фактического параметра- строки без единицы 0 145
Приведем пример использования массива открытого типа. Пусть функция вычисления суммы положительных элементов мас- сива вещественных чисел определена следующим образом: function SumPos(var X: array of Real): Real; var I: Word; S: Real; begin S := 0; // Нумерация элементов открытого массива — // с нуля for I := 0 to High(X) do if X[I] > 0 then S := S + X[I]; SumPos := S; end; Тогда для объявленных в программе массивов var Listl: array [0..30] of Real; List2: array [5..175] of Real; X: Word; S, TempStr: string; Можно использовать следующую последовательность операторов: // Умножение элементов массива на константу for X := Low(Listl) to High(Listl) do Listl[X] := X * 3.4; for X := Low(List2) to High(List2) do List2[X] := X * 0.125; // Вычисление суммы положительных элементов // и преобразование вещественного числа в // строку с указанием формата: 8 знаков всего, // 4 знака после десятичной точки Str(SumPos(Listl):8:4, S); // Вывод сообщения на экран Writein('Сумма положительных элементов Listl: ' +S); Str(SumPos(List2):8:4, S); Writein('Сумма положительных элементов List2: ' + S); Процедурные типы Стандартный Паскаль рассматривает процедуры и функции только как часть программы, которую можно выполнить посредством вызова. В Turbo Pascal и Object Pascal появилось понятие процедурного типа данных, которое позволило рассматривать обращение к процедуре или 146
функции как переменную, имеющую значение. Такая переменная может выступать и в качестве параметра подпрограммы. В объявлении процедурного типа присутствует список формальных параметров и (для функции) тип результата. «Процедурный тип> ::= «Идентификатор типа> = procedure «Список формальных параметров>;| «Идентификатор типа> = function «Список формальных параметров >:<Тип результата^ Синтаксис объявления процедурного типа совпадает с синтаксисом объявления заголовка процедуры или функции, за исключением того, что имя процедуры (функции) после ключевого слова procedure или function опускается. Примеры объявлений процедурного типа: type TMyProc = procedure; TSwapProc = procedure(var X, Y: Integer); TStrProc = procedure(S: String); TWriteFileFunc = function(var F: Text): Integer; TMathFunc = function(X: Real): Real; Имена параметров в объявлении процедурного типа чисто декора- тивные — они не воздействуют на смысл объявления. Константы процедурного типа Константа процедурного типа должна указывать идентификатор процедуры или функции, совместимый по присваиванию с типом кон- станты. Пример: type TErrorProc = procedure(ErrorCode: Integer); procedure DefaultError(ErrorCode: Integer); begin Writein('Error ErrorCode, '.'); end; const ErrorHandler: TErrorProc = DefaultError; В дальнейшем в программе использование вызова ErrorHandler(I) будет всегда эквивалентно выполнению процедуры DefaultError. Процедурные переменные Как только процедурный тип определен, можно объявлять пере- менные этого типа. Такие переменные называются процедурными пе- 147
ременными. Например, используя данное выше объявление типа, можно описать следующие переменные: var Р: TSwapProc; F: TMathFunc; Подобно тому, как целочисленной переменной можно присвоить целочисленное значение, процедурной переменной можно присвоить процедурное значение. Такое значение может, конечно, быть и другой процедурной пере- менной, но может так же быть и идентификатором процедуры или функции. В этой ситуации объявление процедуры или функции можно рассматривать как особый вид объявления константы, значением кото- рой является процедура или функция. Например, даны следующие объ- явления процедур и функций: procedure Swap(var А, В: Integer); var Tmp: Integer; begin Tmp := A; A := B; В := Temp; end; function SqrTan(Angle: Real): Real; begin SqrTan := Sqr(Tan(Angle)); end; Объявленным ранее переменным P и F можно присвоить значения Р := Swap; Т := SqrTan; Согласно этим операторам присваивания, вызов P(I, J) эквивален- тен вызову Swap(I, J), а вызов F(X) эквивалентен вызову SqrTan(X). Для того чтобы считаться совместимыми по присваиванию (т.е. ис- пользоваться в левой и правой части оператора присваивания), проце- дурные типы должны отвечать следующим требованиям: - иметь одинаковое число параметров; - параметры в соответствующих позициях должны быть тождест- венных типов; - типы результатов функций должны быть идентичны. Как отмечено выше, названия параметров не имеют значения, когда проверяется совместимость типа процедуры. В правой части оператора присваивания для процедурной перемен- 148
ной не могут находиться стандартные процедуры и функции и вложен- ные процедуры и функции. Чтобы использовать стандартную процедуру или функцию (напри- мер, WriteLn, ReadLn, Chr или Ord) в правой части оператора присваи- вания, ее необходимо дополнить внешней процедурой. Например, задан процедурный тип type IntProc = procedure(N: Integer); далее приведена совместимая по присваиванию процедура для вывода целого числа: procedure Writelnt(Number: Integer); begin Write(Number); // Стандартная процедура Write end; Вложенные процедуры и функции также нельзя использовать в правой части оператора присваивания. Процедура или функция называ- ется вложенной, если она объявлена внутри другой процедуры или функции. В следующем примере функция InnerProc вложена в OuterProc, следовательно, значение InnerProc нельзя присвоить проце- дурной переменной. procedure OuterProc; var S: string; function InnerProc(writestr:string):string; begin WriteLn(writestr+' — InnerProc '); end; begin S:='Вложенная процедура'; Writein(InnerProc(S)); end; Использование процедурного типа не ограничивается процедурны- ми переменными. Так же как и любой другой тип, процедурный тип может присутствовать в объявлении структурного типа, например: type TProc = procedure (X, Y: Integer; CurType:byte); TProcList = array[1..10] of TProc; TWindow = record Header: String[31]; Top, Left, Bottom, Right: integer; SetCursor: TProc; end; 149
var PList : TProcList; W : TWindow; Согласно этим объявлениям, следующие операторы являются вы- зовами процедур: Р[3](1,1,3) ; W. SetCursor(10,10,1) ; Параметры процедурного типа Так как процедурные типы допустимы в любом контексте, то мож- но объявить процедуры или функции, параметрами которых являются тоже процедуры или функции. Такие параметры являются параметрами- значениями, так как записываются в объявлении без служебного слова var. В качестве фактических параметров в этом случае используются объявленные процедуры или функции, имеющие соответствующее ко- личество параметров требуемых типов. Рассмотрим задачу формирования матрицы M[ij], элементами ко- торой являются: - сумма индексов i и j; - произведение индексов i и j; - сумма квадратов индексов i и j. Пусть раздел объявлений программы, реализующей поставленную задачу, содержит описания следующих типов, переменных, функций и процедур: type TFunc = function (X, Y: integer): integer; // Объявление процедурного типа TMatrix = array [1..20, 1..20] of integer; // Объявление типа для матрицы var M_Sum, M_Mult, M_SqrSum: TMatrix; // Функция суммы двух чисел function Sum(X, Y: integer): integer; begin Sum := X + Y; end; // Функция произведения // двух чисел function Mult(X, Y: integer): integer; begin Multiply := X * Y; end; 150
// Функция суммы квадратов двух чисел function SqrSum(X, Y: integer): integer; begin SqrSum := (X * X) + (Y * Y) ; end; // Процедура заполнения матрицы // с параметром процедурного типа procedure FillMatrix(M: TMatrix; Operation: TFunc); var I, J: integer; begin for I := 1 to 2 0 do // Вложенные циклы заполнения матрицы for J := I to 2 0 do // Элемент матрицы вычисляется // в зависимости от используемой // функции begin := Operation(I,J); M[J,I] := M[I,J]; end end; Тогда поставленная задача решается с помощью следующих опера- торов: FillMatrix(M_Sum, Sum); // Элемент матрицы — сумма индексов i и j; FillMatrix(M_Mult, Mult); // Элемент матрицы — произведение индексов // i и j; FillMatrix(M_SqrSum, SqrSum); // Элемент матрицы — сумма квадратов // индексов i и j. Параметры процедурного типа особенно полезны в ситуациях, ко- гда над множеством процедур или функций выполняются общие дейст- вия. В нашем случае процедура FillMatrix представляет общее действие, выполняемое над функциями Sum, Mult и SqrSum. И 2.9. Структура программы Программа на языке Pascal представляет собой последовательность заголовка, раздела объявлений и тела программы. Раздел объявлений включает в себя: 151
• объявление меток; • объявление констант; • объявление типов; • объявление переменных; • объявление процедур и функций; • объявление используемых модулей. Структурная схема программы на языке Pascal изображена на рис. 2.3. В Turbo Pascal и Object Pascal имеются особенности в структуре программы по равнению со стандартом языка: • заголовок программы не обязателен и не обрабатывается ком- пилятором; • порядок размещения разделов объявлений произвольный (в отличие от стандарта, где этот порядок строго фиксирован); • в программе может быть указано несколько одинаковых разде- лов объявлений; • существует раздел объявления используемых модулей (в стан- дарте языка этот раздел отсутствовал). Заголовок программы Заголовок программы содержит служебное слово program, имя программы и ее параметры. Синтаксис заголовка следующий: «Заголовок программы» := program «Идентификатор»; | program <Идентификатор»(«Список параметров программы»); Примечание. Список параметров программы в стандарте языка представлял собой список идентификаторов используемых программой файлов ввода-вывода, перечисленных через запятую. В современных реализациях языка список параметров игнорируется. Заголовок программы в современных реализациях языка является чисто декоративной деталью и компилятором не используется. Раздел объявления меток Синтаксис раздела объявления меток следующий: «Раздел объявления меток» ::= Label «Список идентификаторов меток»; «Список идентификаторов меток» ::= «Идентификатор метки>| «Список идентификаторов меток»,«Идентификаторов метки» Например: Label 1, Al, 99; 152
Рис. 2.3. Структурная схема программы на языке Pascal 153
Раздел объявления констант Синтаксис раздела объявления констант следующий: <Раздел объявления констант> ::= const <Список объявлений констант>; <Список объявлений констант> ::= «Объявление константы>| «Список объявлений констант>; «Объявление константы> «Объявление константы> ::= «Объявление простой константы>| «Объявление типизированной константы> «Объявление простой константы> ::= <Идентификатор> = <3начение> «Объявление типизированной константы> ::= «Идентификатор:»: <Тип> = «Значение:» Например: Const CInt = 999; OddDigits : set of 0..9 = [1, 3, 5, 7, 9] ; RArr : array [1..5] of real = (1.2, 0.5, -3.0, 75.12, 123.675); Раздел объявления типов Синтаксис раздела объявления типов следующий: «Раздел объявления типов> ::= type «Список объявлений типов>; «Список объявлений типов> ::= «Объявление типа>| «Список объявлений типов>; «Объявление типа> «Объявление типа> ::= «Идентификатор:» = <Тип> «Тип > ::= «Простой тип>| «Структурированный тип> Например: type TPoint = record X, Y, Z: real end; TArrPoint = array [1..1000] of TPoint; TFilePoint = file of TPoint; Раздел объявления переменных Синтаксис раздела объявления переменных следующий: «Раздел объявления переменных:» ::= var «Список объявлений переменных:»; «Список объявлений переменных:» ::= «Объявление переменных>| «Список объявлений переменных:»; «Объявление переменных:» 154
<Объявление переменных> ::= <Список идентификаторов» : <Тип> <Список идентификаторов» ::= <Идентификатор>|<Список идентификаторов», <Идентификатор> <Тип > ::= <Простой тип>| «Структурированный тип»|<Идентификатор типа>| Например: var PointA, PointB: TPoint; LineLen: real; i, j, k: integer; Sntess: string; ChrMess: set of 'A'..'Z'; Раздел объявления процедур и функций Синтаксис раздела объявления подпрограмм следующий: <Раздел объявления подпрограмм» ::= <Список объявлений подпрограмм»; <Список объявлений подпрограмм» ::= «Объявление подпрограммы»] «Список объявлений подпрограмм» «Объявление подпрограммы» «Объявление подпрограммы» ::= < Объявление процедуры»] < Объявление функции» Например: // Функция вычисления факториала // натурального числа function Factorial(N:integer): integer; var i, F:integer; begin F := 1; for i := 2 to N do F := F*i; Factorial := F end; // Процедура удаления пробелов из строки procedure DeleteBlank(var S:string); var i: integer; begin i := pos(' ',S); while i » 0 do begin Delete(S, i, 1) ; i := pos(' ',S) end; end; 155
Раздел объявления используемых модулей (предложение uses) Предложение uses идентифицирует все модули, используемые про- граммой. Синтаксис раздела объявления используемых модулей сле- дующий: «Раздел объявления используемых модулей > ::= uses «Список модулей>; «Список модулей> ::= «Имя модуля>| «Список модулей>, «Имя модуля> Например: uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Тело программы Тело программы представляет собой составной оператор — начи- нается словом begin и заканчивается словом end с точкой. Точка явля- ется признаком конца программы. И 2.10. Модули Синтаксис модулей Модули являются основой модульного программирования. Они ис- пользуются для создания библиотек, которые могут включаться в раз- личные программы (при этом становится необязательным иметь в нали- чии исходный код), а большие программы могут подразделяться на ло- гически связанные модули: < Модуль > ::= < Заголовок модуля >;< Интерфейсный раздел > < Раздел реализации >< Раздел инициализации >. Заголовок модуля В заголовке модуля определяется имя модуля: < Заголовок модуля > ::= unit < Идентификатор модуля > Имя модуля используется при ссылке на модуль в предложении uses. Это имя должно быть уникальным, так как два модуля с одним именем не могут использоваться одновременно. 156
Интерфейсный раздел В интерфейсном разделе объявляются те константы, типы, пере- менные, процедуры и функции, которые являются глобальными, т.е. доступными основной программе (программе или модулю, которые ис- пользуют данный модуль). Основная программа имеет доступ к этим элементам, как если бы они были бы объявлены в блоке, который вклю- чает главную программу. «Интерфейсный раздел > ::= interface < Предложение uses >;< Раздел объ- явления констант >;< Раздел объявления типов >;< Раздел объявления перемен- ных >;< Раздел заголовков процедур и функций >; «Раздел заголовков процедур и функций> «Список заголовков подпро- грамм > «Список заголовков подпрограмм > ::= «Заголовок подпрограммы>| «Спи- сок заголовков подпрограмм >;<3аголовок подпрограммы» «Заголовок подпрограммы > ::= «Заголовок процедуры>|«3аголовок функции» Интерфейсный раздел только перечисляет заголовки процедур и функций. Полные описания процедур и функций находятся в разделе реализации. Раздел реализации В разделе реализации приведены описания всех глобальных проце- дур и функций. В нем также описываются константы, типы, перемен- ные, процедуры и функции, являющиеся глобальными для модуля, но локальными по отношению к основной программе. «Раздел реализации» ::= implementation < Предложение uses >; < Раздел объявления меток >; < Раздел объявления констант >; < Раздел объявления ти- пов >; < Раздел объявления переменных >; < Раздел объявления процедур и функций > Заголовки процедур и функций могут быть продублированы из интер- фейсного раздела. Необязательно задавать список формальных параметров, но если список указан, то в случае несоответствия объявления в интер- фейсном разделе и разделе реализации компилятор выдаст ошибку. Раздел инициализации Раздел инициализации является последним разделом модуля. Он может состоять либо из зарезервированного слова end (в этом случае модуль не содержит кода инициализации), либо из операторной части, которая должна выполняться для инициализации модуля: «Раздел инициализации» ::= end| «Составной оператор > 157
Разделы инициализации модулей, которые используются програм- мой, выполняются в том же порядке, в каком модули указаны в предло- жении uses. 2.11. Организация ввода-вывода данных. Работа с файлами Связь с внешними файлами осуществляется через файловые пере- менные, т.е. переменные файлового типа. Pascal обеспечивает доступ к трем различным категориям файлов: • текстовые файлы (для связи с такими файлами необходимо объявить переменную типа Text); • типизированные файлы (для связи с ними объявляется пере- менная типа file of <Тип>); • нетипизированные файлы или файлы без типа (связываются с программой через переменную типа file). Работа с каждой из категорий имеет свою специфику, но есть и общие правила обеспечения процессов чтения-записи для всех категорий файлов. Работу с внешними файлами поддерживают стандартные процеду- ры и функции ввода-вывода. Перед использованием файловой перемен- ной любого типа ее необходимо связать с внешним файлом с помощью вызова процедуры Assign (В расширении языка — Object Pascal для Delphi — эта процедура называется AssignFile). После того, как связь с внешним файлом установлена, все операции ввода или вывода для внешнего файла осуществляются через присоединенную к нему файло- вую переменную. Перед непосредственным чтением-записью данных внешний файл необходимо «открыть». Существующий файл можно открыть с помо- щью процедуры Reset, а новый файл можно создать и открыть с помо- щью процедуры Rewrite. Кроме того, текстовые файлы могут быть от- крыты процедурой Append для добавления данных в конец файла, но при этом они доступны только для записи. Типизированные и нетипизированные файлы всегда допускают как чтение, так и запись, независимо от того, были они открыты с помощью процедуры Reset или с помощью процедуры Rewrite. Когда начинается выполнение программы, всегда автоматически открываются стандартные текстовые файловые переменные Input и Output (ввод и вывод). Input — это доступный только для чтения тексто- вый файл, связанный с клавиатурой, a Output — это доступный только для записи текстовый файл, связанный с дисплеем. 158
Любой файл представляет собой линейную последовательность элементов (записей). Каждая запись файла имеет свой порядковый но- мер. Первая запись файла считается нулевой и имеет порядковый номер 0. Для каждого файла определено понятие текущей позиции внутри файла, т.е. порядковый номер записи (или записей), к которой будут обращены стандартные процедуры чтения-записи. Обычно доступ к файлам организуется последовательно, т.е., когда некоторая запись считывается с помощью стандартной процедуры Read или записывается с помощью стандартной процедуры Write, текущая позиция файла перемещается к следующей по порядку записи файла. Однако к типизированным и нетипизированным файлам можно органи- зовать прямой доступ с помощью стандартной процедуры Seek, которая перемещает текущую позицию файла к записи с заданным порядковым номером. Текущую позицию в файле и текущий размер файла можно определить с помощью стандартных функций FilePos и FileSize. Когда программа завершит обработку файла, его необходимо за- крыть с помощью стандартной процедуры Close (В расширении языка — Object Pascal для Delphi — эта процедура называется CloseFile). После завершения процедуры Close связанный с файловой переменной внеш- ний файл обновляется, а файловая переменная может быть использова- на для обеспечения работы с другим внешним файлом. По умолчанию при всех обращениях к стандартным функциям и процедурам ввода-вывода автоматически производится проверка на на- личие ошибок. При обнаружении ошибки программа прекращает работу и выводит на экран сообщение об ошибке. С помощью директив компи- лятора {$!+} и {$!-} эту автоматическую проверку можно включить или выключить. Когда автоматическая проверка отключена (т.е. когда про- цедура или функция была скомпилирована с директивой {$!-}), ошибки ввода-вывода, возникающие при работе программы, не приводят к ее останову. Результат выполнения операции ввода-вывода при этом мож- но проверить с помощью стандартной функции lOResult. Текстовые файлы При открытии текстового файла внешний файл интерпретируется особым образом: считается, что он представляет собой последователь- ность символов, сгруппированных в строки, где каждая строка заканчи- вается признаком конца строки (end of line). Признак конца строки представлен символом перевода каретки, за которым, возможно, следу- ет символ перевода строки. Для текстовых файлов существует специальный вид операций чте- ния и записи (Read и Write), которые позволяют считывать и записывать последовательности значений, тип которых отличается от типа Char и 159
String. Такие значения автоматически переводятся в символьное пред- ставление и обратно. Рассмотрим, например, оператор процедуры Read(F, i, R) , где i — переменная типа integer, a R — переменная типа real. Ис- пользование этой процедуры приведет к считыванию двух последо- вательностей цифр и знаков, допустимых при записи чисел. Эти по- следовательности в файле должны быть разделены пробелом, знаком табуляции или признаком конца строки. Первая последовательность будет интерпретирована как десятичное число, значение которого будет занесено в переменную i, а вторая последовательность будет преобразована в вещественное число, которое будет занесено в пе- ременную R. Как было отмечено ранее, имеются две стандартные файловые пе- ременные текстового типа — Input и Output. Стандартная файловая пе- ременная Input — это доступный только для чтения файл, связанный со стандартным файлом ввода операционной системы (обычно это клавиа- тура), а стандартная файловая переменная Output — это доступный только для записи файл, связанный со стандартным файлом вывода операционной системы (обычно это дисплей). Перед началом выполне- ния программы файлы Input и Output автоматически открываются, как если бы были выполнены следующие операторы: Assign(Input,''); Reset(Input); Assign(Output,''); Rewrite(Output); После выполнения программы эти файлы автоматически закрыва- ются. Для некоторых стандартных процедур (например, Read, ReadLn, Write, WriteLn) не требуется явно указывать в качестве параметра фай- ловую переменную. Если этот параметр опущен, то по умолчанию бу- дут рассматриваться стандартные файловые переменные Input и Output, в зависимости от назначения процедуры или функции. Например, Read(X) соответствует Read(Input,X), a Write(X) соответствует Write(Output,X). В качестве примера рассмотрим следующую программу. Пусть на диске в текущей директории есть файл с именем filereaLtxt, в котором в нескольких строках записаны последовательно- сти действительных чисел в символьном представлении: 0.54 1.7 4.56 0.2 1.32 1.524 18 0.92 7.7 Необходимо вычислить сумму чисел и вывести результат на экран. 160
program SumReal; var FInput: text; // файловая переменная X: real; // переменная для чтения // числа из файла Sum: real; // переменная для вычисления // суммы begin {$!-} // Включение внутренней // проверки правильности // операций ввода-вывода Sum := 0; Assign(FInput, 'filereal.txt'); Reset (FInput); if lOResult <> 0 then Writein('Ошибка при открытии файла') // Вывод на экран сообщения else begin while not EoF(FInput) do // Цикл — до конца файла begin Read(FInput, X); Sum : = Sum + X end; Writein('сумма='; Sum:8:3); // Вывод на экран значения суммы в формате с // фиксированной точкой (общая длина числа — 8 // знаков, 3 знака после точки Close(FInput) end end. Типизированные файлы Использование стандартных процедур Read и Write для типизирован- ных файлов отличается от их использования для текстовых файлов тем, что переменные, в которые читается или из которых записывается информация, должны иметь тот же тип данных, что и компоненты типизированного файла. Таким образом, при чтении или записи типизированных файлов всегда происходит перемещение данных одинаковой длины. Функция FileSize для типизированных файлов возвращает количе- ство компонентов, записанных в файл, а функция FilePos позволяет оп- ределить номер текущего компонента файла. В качестве примера рассмотрим задачу создания типизированного файла из записей (Students.rec), содержащих следующую информацию о 161
студентах: фамилия, имя, отчество, курс, группа, предмет и экзаменаци- онная оценка. Заполнение записей должно осуществляться путем по- следовательного ввода запрашиваемых данных с клавиатуры. Для решения такой задачи предложим программу Students List. program Students_List; label 1, 2; type TStud = record // тип данных, представляющий // запись на одного студента Family : string[30]; Name : string[15]; Patr : string[20]; Group : string[5]; Course : integer; Subj ect: string[30]; Mark : integer; end; var RStud: TStud; FStud: file of TStud; // переменная типизированного // файла ch : char; begin {$!-) Assign(FStud, 'Students.rec'); Rewrite(FStud); // открытие файла на запись if lOResulto 0 then begin Writein(' Ошибка открытия выводного файла '); goto 2 end; repeat with RStud do // ввод с клавиатуры значений // полей очередной записи begin Write('Фамилия (30) : '); Readln(Family); Write('Имя (15) : '); Readln(Name); Write('Отчество (20) : '); Readln(Patr); Write('Группа (5): '); Readln(Group); Write('Курс : '); Readln(Course); Write('Предмет (20): '); Readln(Subject); Write('Оценка : '); Readln(Mark); end; Write(FStud,RStud); // занесение в файл очередной // записи 162
if lOResulto 0 then begin Writein(' Ошибка записи в файл '); goto 1 end; // запрос на продолжение или // окончание ввода данных Write('Продолжить ввод (д/н)? '); readln(ch); Until ch = 'н’; 1: Close(FStud); // закрытие файла 2: end. Нетипизированные файлы Нетипизированные файловые переменные используются в основ- ном для обеспечения прямого доступа к любому файлу на диске, неза- висимо от его типа и структуры. Любой нетипизированный файл объявляется со словом file без ат- рибутов, например: var Datafile : file; Для нетипизированных файлов в процедурах Reset и Rewrite допус- кается указывать дополнительный параметр, чтобы задать размер запи- си, использующийся при передаче данных. По умолчанию длина записи равна 128 байт. Предпочтительной длиной записи является длина записи, равная 1, поскольку это единст- венное значение, которое точно отражает размер любого файла (если длина записи равна 1, то неполные записи невозможны). За исключением процедур Read и Write для всех нетипизированных файлов допускается использование любой стандартной процедуры, ко- торую разрешено использовать с типизированными файлами. Вместо процедур Read и Write здесь используются соответственно процедуры BlockRead и BlockWrite, позволяющие пересылать данные с высокой скоростью. Рассмотрим задачу создания предметных ведомостей с подсчетом среднего балла за экзамен на основе файла, полученного в предыдущем примере. Необходимо получить текстовый файл, представляющий собой по- следовательный вывод оформленной в виде ведомости информации об экзамене по каждому предмету, наименование которого встречается в файле Students.rec. Предлагается следующий алгоритм обработки файла Students.rec: • прочитать файл и построить в памяти однонаправленный спи- сок записей типа TStud List; 163
• построить цикл обработки списка (до тех пор, пока он не ста- нет пустым) по следующему принципу; • по первой записи списка построить заголовок ведомости и за- помнить наименование предмета, курс и номер группы, уда- лить запись из списка; • далее, для каждой следующей записи списка провести сравне- ние значений соответствующих полей (предмет, курс, группа) с сохраненными ранее значениями. Если значения совпадают, то информация о фамилии, имени, отчестве и оценке студента заносится в выводной текстовый файл и запись удаляется. Алгоритм реализован в программе List Prep: program List_Prep; label 1; type TStud = record Family : string[30]; Name : string[15]; Patr : string[20]; Group : string[5]; Course : integer; Subject: string[30]; Mark : integer; end; TPStud_List = ~TStud_List; TStud_List = record RecStud : TStud; Next: TPStud_List; end; var Fstud : file; BList, EList, CurList : TPStud_List; ch : char; Gr : string[5]; SumMark, NStud, Cr : integer; Subj : string[30]; Flist : text; procedure DelRec(rec:TPSTud_List) ; // Процедура удаления записи //из списка begin if EList <> nil then EList^.Next := RecNext; if BList = rec then BList := rec~.Next; CurList := Rec^.Next; 164
Dispose(rec); end; begin {$!-} Assign(FStud, 'Students.rec'); Reset(FStud, SizeOf(TStud)); // Открытие входного файла if lOResult <> 0 then begin Writein('Ошибка открытия входного файла'); goto 1 end; Assign(FList, 'Students.txt'); Rewrite(FList); // Открытие файла для вывода // результата if lOResult о 0 then begin Writein('Ошибка открытия выводного файла'); goto 1 end; new(BList); BlockRead{FStud, BList~.RecStud,1); // Чтение первой записи, // организация начала списка if lOResult о 0 then begin Writein('Ошибка чтения входного файла'); goto 1 end; BListA.Next := nil; EList := BList; // Цикл создания списка while not Eof(FStud) do begin new(CurList); BlockRead (FStud, CurList"'.RecStud, 1) ; if lOResult <> 0 then begin Writein('Ошибка чтения входного файла'); goto 1 end; EListA.Next := CurList; EList:=CurList; EList^.Next:= nil end; // Цикл обработки списка while BList <> nil do begin CurList := BList; with CurList^.RecStud do begin Subj := Subject; Gr := Group; Cr := Course; SumMark := Mark; NStud := 1; // Формирование заголовка // ведомости 165
Writein(FList,'Курс : ', Cr); Writein(FList,'Группа : Gr) ; Writein(FList,'Предмет: ', Subj); Writein(FList); Writein(FList); Writein(FList, Family, ' ' : (30-ord(Length(Family))), Name, ' ':(15-ord(Length(Family))), Mark:3); end; BList:= CurLisf'.Next; Dispose(CurList); CurList := Blist; EList := nil; // Цикл выбора записей для // занесения в текущую ведомость while CurList о nil do begin with CurLisf.RecStud do if (Cr = Course) and (Gr = Group) and (subj = Subject) then begin Writein(FList,Family,' ': (30-ord(Length(Family))), Name, ' ':(15-ord(Length(Family))), Mark:3); inc(NStud); inc(SumMark,Mark); DelRec(CurList) end else begin EList := CurList; CurList := CurLisf'.Next end; end; Writeln(FList); // Завершение формирования // очередной ведомости Writein(FList,'Средний балл : ', (SumMark/NStud):3:2); Writein(FList,'Кол-во студентов: ', NStud); Writein(FList); Writein(FList) ; end; Close(FStud); Close(FList); // Закрытие файлов 1: end. 166
Стандартные процедуры и функции ввода-вывода Наименование Ста- тус Описание Append(F) P Открывает существующий текстовый файл для дозаписи. Файл при этом открывается только для записи, внутренний файловый указатель устанав- ливается на конец файла. Если строка имени файла пустая (AssignFile(F,")), осуществляется связь со стандартным файлом ввода. AssignFile(F, Name) As- sign^,Name) P Присваивает имя внешнего файла (Name) файло- вой переменной F. Если строка имени пустая, осуществляется связь со стандартным файлом вво- да или вывода. BlockRead (F,Buf,N[, NTransf]) P Читает N или менее (если при чтении достигнут конец файла) записей из нетипизированного фай- ла, с которым связана файловая переменная F. Результат чтения помещается в переменную Buf. Необязательный параметр NTransf указывает на количество фактически прочитанных записей. Максимальный размер считываемых данных равен Size*N, где Size — размер записи, задающийся процедурами Reset и Rewrite. После выполнения операции внутренний указатель файла перемеща- ется на количество считанных записей (NTransf). BlockWrite (F,Buf,N[, NTransf]) P Записывает N или менее (если при записи будет до конца заполнен диск) записей в нетипизированный файл, с которым связана файловая переменная F. Данные для записи берутся из переменной Buf. Необязательный параметр NTransf указывает на количество фактически перемещенных записей. Максимальный размер перемещаемых данных равен Size*N, где Size — размер записи, задаю- щийся процедурами Reset и Rewrite. После выпол- нения операции внутренний указатель файла пе- ремещается на количество помещенных в файл записей (NTransf). ChDir(SPath) P Меняет текущую директорию на директорию с именем, задающимся параметром SPath. Если па- раметр SPath содержит имя диска, то меняется и текущий диск. CloseFile(F) Close(F) P Закрывает ранее открытый внешний файл, с кото- рым связана файловая переменная F. При необхо- димости в содержимое файла вносятся произве- денные изменения. 167
Продолжение табл. Наименование Ста- тус Описание Eof(F) F Возвращает статус конца файла, с которым связана файловая переменная F. Если параметр опущен, рассматривается стандартный файл ввода. Прини- мает значение True, если обработана последняя запись файла или файл является пустым. В осталь- ных случаях принимает значение False. Eoln(F) F Возвращает статус конца строки для текстового файла, с которым связана файловая переменная F. Если параметр опущен, рассматривается стандарт- ный файл ввода. Принимает значение True, если текущим элементом файла является признак конца строки или Eof(F) принимает значение True. В остальных случаях принимает значение False. Erase(F) P Удаляет внешний файл, с которым связана файло- вая переменная F. Перед удалением файл обяза- тельно должен быть закрыт с помощью процедуры CloseFile (Close). FilePos(F) F Возвращает текущую позицию (в количестве запи- сей) указателя внутри типизированного или нети- пизированного файла, с которым связана файловая переменная F. Если указатель находится в начале файла, возвращает значение 0. Применяется только к открытым файлам. FileSize(F) F Возвращает текущий размер (в количестве запи- сей) типизированного или нетипизированного файла, с которым связана файловая переменная F. Применяется только к открытым файлам. Для пус- тых файлов возвращает значение 0. Flush(F) P Освобождение буфера текстового файла вывода. Ин- формация из буфера записывается во внешний тексто- вый файл, с которым связана файловая переменная F. Не работает для файлов, открытых на чтение. GetDir(D, CPath) F Возвращает в значение параметра CPath текущую директорию указанного устройства. Номер уст- ройства задается параметром D: D=0 — текущий диск, D=1 —диск A, D=2 —диск В и т.д. lOResult F Возвращает целое значение статуса последней операции ввода-вывода MkDir(Path) P Создает новую директорию, путь к которой зада- ется параметром Path. В строку Path не должно входить имя файла. 168
Продолжение табл. Наименование Ста- тус Описание Read(F,vl[,v2, ...,vn]) P Читает одно или более значений из файла в одну или более переменных. Используется для тексто- вых и типизированных файлов. Параметр F может отсутствовать. Связь в этом случае осуществляется со стандартным текстовым файлом ввода. Readln(F,vl[, v2,..., vn]) P Читает одно или более значений из текстового файла в одну или более переменных и переводит текущий указатель файла на начало следующей строки. Используется только для текстовых фай- лов. Параметр F может отсутствовать. Связь в этом случае осуществляется со стандартным текстовым файлом ввода. Вызов процедуры в форме Readln(F) переводит внутренний указатель файла на начало следующей строки (или на конец файла, если следующей строки не существует). Rename(F, NewNm) P Переименовывает внешний файл, с которым свя- зана файловая переменная F. Параметр NewNm содержит новое имя файла. Reset(F[,Size]) P Открывает на чтение и запись существующий файл, с которым связана файловая переменная F. Внут- ренний указатель файла устанавливается на начало файла. Если строка имени файла пустая (AssignFile(F,")), осуществляется связь со стандарт- ным файлом ввода Если файловая переменная F имеет тип text, то файл открывается только на чте- ние. После вызова процедуры Reset значение функ- ции Eof(F) всегда False (за исключением случая пустого файла — тогда Eof(F)=True). Необязатель- ный параметр Size (тип — целое) используется только для файлов без типа и задает размер пересы- лаемой записи в байтах. По умолчанию Size = 128. Rewrite(F[, Size]) P Создает и открывает на чтение и запись новый файл, имя которого задается процедурой AssignFile (Assign) для файловой переменной F. Если файл с таким име- нем уже существует, то он удаляется и на его месте создается новый пустой файл Внутренний указатель файла устанавливается на начало пустого файла. Если строка имени файла пустая (AssignFile(F,")), осуществляется связь со стандартным файлом выво- да. Если файловая переменная F имеет тип text, то 169
Продолжение табл. Наименование Ста- тус Описание файл открывается только на запись. После вызова процедуры Rewrite значение функции Eof(F) всегда True. Необязательный параметр Size (тип — целое) используется только для файлов без типа и задает размер пересылаемой записи в байтах. По умолчанию Size= 128. RmDir(Spath) P Удаляет пустую директорию, путь к которой ука- зан параметром Spath. Если указан путь к несуще- ствующей директории, непустой директории или к директории, установленной как текущая, выдается сообщение об ошибке. Seek(F, N) P Устанавливает текущую позицию указателя внут- ри типизированного или нетипизированного фай- ла, с которым связана файловая переменная F, на запись с номером N. Нумерация записей ведется со значения 0. Применяется только к открытым фай- лам. Seek(F, FileSize(F)) перемещает внутренний указатель на конец файла. SeekEof(F) F Возвращает статус конца текстового файла, с которым связана файловая переменная F. Если параметр опу- щен, рассматривается стандартный файл ввода. Отли- чается от Eof(F) тем, что стоящие в конце файла сим- волы пробела и табуляции игнорируются. SeekEoln(F) F Возвращает статус конца строки для текстового файла, с которым связана файловая переменная F. Если параметр опущен, рассматривается стандарт- ный файл ввода. Отличается от Eoln(F) тем, что стоящие в конце строки символы пробела и табу- ляции игнорируются. SetTextBuf (F,Buff,Size]) P Назначает буфер ввода-вывода для внешнего тексто- вого файла, с которым связана файловая переменная F. Параметр Buf указывает на буфер, который будет использоваться для ввода-вывода. Необязательный параметр Size задает размер буфера. Если размер не указан, используется вся переменная Buf. Если про- цедура не используется, ввод-вывод организуется с помощью стандартного буфера размером в 128 байт. Назначение действует до следующей процедуры AssignFile (Assign). Процедура не должна приме- няться к уже открытому файлу, для которого имели место операции ввода-вывода (это может привести к потере данных). 170
Продолжение табл. Наименование Ста- тус Описание Truncate(F) p Удаляет часть файла, с которым саязана файловая переменная F, начиная с текущей позиции до кон- ца. Используется только для типизированных и нетипизированных файлов. Применяется только к открытым файлам. Write(F,vl[,v2,.. .,vn]) p Записывает одно или более значений в файл, с которым связана файловая переменная F. Исполь- зуется для текстовых и типизированных файлов. Параметр F может отсутствовать. Связь в этом случае осуществляется со стандартным текстовым файлом ввода. Writeln(F,vl[,v2 ,...,vn]) p Записывает одно или более значений в файл, с которым связана файловая переменная F, и затем записывает признак конца строки (только для тек- стовых файлов). Используется только для тексто- вых файлов. Параметр F может отсутствовать. Связь в этом случае осуществляется со стандарт- ным текстовым файлом вывода. Вызов процедуры в форме Writeln(F) приводит к записи в файл при- знака конца строки. И Программная реализация алгоритмов главы 1 Рассмотрим программную реализацию некоторых алгоритмов, представленных в гл. 1, с использованием языка Pascal. 1. Программа нахождения наибольшего общего делителя (НОД) двух целых чисел (алгоритм Евклида): program Euklid; var А, В, R: integer; begin Write('Введите первое число : '); Readln(А); Write('введите второе число : '); Readln(B); While А о В do begin if А < В then {поменять местами А и В} begin R := А; А := В; В := R end; А := А - В end; Writein('НОД = ', А) end. 171
2. Программа формирования ряда Фибоначчи для любого нату- рального N: program Fibonachi; var N, F, P, R: integer; Begin Write('введите натуральное число N : '); Readin(N); F := 1; P := 0; While F <= N do begin Write(F, ' '); {вывод очередного члена ряда} R := F; {сохранение значения очередного члена ряда} F := F + P; {вычисление следующего значения} P := R {восстановление прельщу- щего значения} end; Writeln; end. 3. Программа формирования и обработки двумерного массива (матрицы): program Matrix; var А : Array [1..100, 1..100] of integer; I, J, N : integer; Begin Write('Введите размерность матрицы N (N<=100): '); Readln(N); for I := 1 to N do {Вложенные циклы формирования элементов матрицы} for J := 1 to N do A[I,J] := (I+J)*(I+J); for I := 2 to N do {Вложенные циклы алгоритма зеркального отображения} for J := 1 to 1-1 do A[J,I] := A[I,J]; for I := 1 to N do {Цикл обработки элементов главной диагонали} A[I,I] := 0; for I := 1 to N do {Построчный вывод элементов матрицы} begin for J := 1 to N do 172
Write(A[I,J]:6); {Отводится 6 позиций на вывод элемента} Writein; {Переход на начало следующей строки} end; end. 4. Программа вычисления суммы двух целых чисел, значения кото- рых превышают максимально допустимые для типа INTEGER: program Summa; type TArray = Array [1. . 2 5 5 ] of byte; PointArray = ЛТАггау; var А, В, C : PointArray; Nmbl, Nmb2 : string; L, Ll, L2, I,J : byte; begin {Ввод чисел как последовательностей символов} Write('Введите первое число : '); Readln(Nmbl); Write(’Введите второе число : '); Readin(Nmb2); Ll := Length(Nmbl); {Вычисление длины первого числа} L2 := Length(Nmb2); {Вычисление длины второго числа } {Вычисление размерности массивов байтов} if Ll > L2 then L := Ll else L : = L2; inc(L); {Динамическое размещение массивов А, В, С} GetMem(A,L); GetMem(B,L); GetMem(C,L); {Обнуление элементов массивов} for I := 1 to L do begin A~[I] := 0; ВЛ[1] := 0; СЛ[1] := 0 end; J := L; {Формирование двоично- десятичного представления для числа А} for I := Ll downto 1 do begin AA[J] : = ord(Nmbl[I]) — ord('O'); dec(J) end; J := L; 173
{Формирование двоично- десятичного представления для числа В} for I := L2 downto 1 do {} begin ВЛ[1] := ord(Nmb2[I]) — ord(’O’); dec(J) end; {Основной цикл вычисления результата} for I : = L downto 2 do begin СЛ[1] := СЛ[1] + АЛ[1] + ВЛ[1]; if СЛ[1] >= 10 then begin dec(СЛ[I],10) ; СЛ[1-1] := 1 end; end; if СЛ[1] = 0 then J := 2 else J := 1; {Пропуск лидирующего нуля перед выводом} for I := J to L do Write(chr(СЛ[I] + ord('O’))); {Посимвольный вывод результата} Writein; FreeMem(A, L); {Освобождение памяти, занимаемой массивами А, В, С} FreeMem(B, L); FreeMem(C, L) ; end. 5. Программа вычисления количества слов в последовательности символов: program Nuniber_Words; var А : String; L, NW, I, N : byte; begin Write('Введите фразу : '); Readin(A); N := Length(A); {Вычисление длины фразы} L := 0; NW := 0; {Обнуление длины слова и счетчика слов} for I := 1 to N do {Основной цикл обработки фразы} if А[1] = ' 1 then begin if L > 0 then {Увеличение счетчика слов и обнуление длины слова, если пе- ред пробелом слово ненулевой длины} begin inc(NW); L := 0 end end else inc(L); if L > 0 then inc(NW); 174
{Увеличение счетчика слов, если фраза не заканчивается пробелом} Writein('Количество слов = NW) ; end. 6. Программа подсчета количества слов в текстовом файле: program Text_File; var FName, FLine : string; {Строка — имя файла и строка — буфер чтения} Ь : boolean; NWords : integer; {Количество слов в файле} FText : text; {Файловая переменная} {Функция подсчета количества слов в строке} Function Number_Words(line : string): integer; var L, NW, I, N : byte; begin N := Length(line); {Вычисление длины строки} L := 0; NW := 0; for I := 1 to N do {Основной цикл обработки строки} if line[I] = ' ' then begin if L > 0 then begin inc(NW); L := 0 end end else inc(L); if L > 0 then inc(NW); Number_Words := NW; {Результат функции — количество слов в строке} end; begin {$!-} b := true; {Флаг для цикла ввода имени файла} while b do begin Write ('Введите имя файла : '); Readln(FName); if FName = ' ' then b : = false else begin Assign(FText, FName); {} Reset(FText); if lOResult <> 0 then Writein('Файл '+ FName + ' не может быть открыт') else b := false end; end; 175
if FName <> ’' then {Обработка файла, если имя файла не пусто} begin NWords := 0; while not Eof(FText) do {Цикл обработки файла} begin Readin(FText, FLine); NWords := NWords + Number_Words(FLine) end; Writeln('Количество слов в файле = ' , NWords); end; Close(FText); end. 7. Программа перестановки записей типизированного файла (мо- дифицированный алгоритм п.6 гл.1): program File_Access; type TRecord = string[20]; var Rec5, Rec6 : TRecord; FRecords : file of TRecord; begin {$!-} As s ign(FRecords, 'FTes t.rec’); {} Reset(FRecords); if lOResult <> 0 then Writein('Open Error!') else if Filesize(FRecords) >= 6 then begin Seek(FRecords, 4); Read(FRecords, Rec5); Read(FRecords, Rec6); Seek(FRecords, 4); Write(FRecords, Rec6); Write(FRecords, Rec5); end; Close(FRecords); end. Упражнения 1. Записать на языке Pascal арифметические выражения, приведен- ные в классической записи: 176
a) a + — -J. b) lnx + у x. I7/ 7\7/1 c)Jsin lx +1I + C0S |x +2.5 d) 2aarctgx. e) arcs in x. smx + cosx x +1 Й)2e~x. 2+cos« 2. Вычислить результат выражения: a) A*2+A>=A*A b) Cos(Abs(X)) = Cos(X) c) Sin(X) <= 1 d) Int(X) <= X e) Ln(100A2) = 4 f) X mod 3 > 3 g) A >= A div 5*5 3. При каких значениях X значения выражений истинны: a) Sin(X) = X b) X mod 2 = 0 с) Х*Х = Х+Х d)Sqr(X)>X 4. Записать логические выражения, которые принимают значение true только при выполнении указанных условий: переменная X принимает значение в интервале [2, 5); переменная X принимает значение вне интервала (0, 1); переменная X принимает значение в одном из интервалов (0, 0.3) и [-5,-2.5); 177
значение целой переменной N делится на 2 и на 3; значение целой переменной N делится на 5 или на 7; значение положительной целой переменной N при делении на 5 да- ет в остатке 3; ближайшее целое число к значению X четно и отлично от 0. 5. Записать в виде оператора присваивания следующие действия. Присвоить переменной Flag значение true, если переменная X при- нимает значение в одном из промежутков (0; 2) и (5; 25]. Присвоить переменной Flag значение true, если значение целочис- ленной переменной X четно (нечетно). Присвоить переменной Flag значение true, если значение целочис- ленной переменной X делится на 5 и на 3. Присвоить переменной Flag значение true, если треугольник со сто- ронами А, В, С равнобедренный. Записать оператор для вычисления следующих значений: - площади круга; - площади треугольника; - площади трапеции; - гипотенузы прямоугольного треугольника. 6. Записать алгоритмы решения следующих задач двумя способами: - пользуясь только условными операторами; - пользуясь операторами присваивания и условными операторами в неполной форме. Присвоить переменной у значение 0, если х <3 0, или значение х2-Зх + 5, еслих>0. Присвоить переменной у значение sinx + 1, если х < 1, значение cos х, если х > 2, и значение х в остальных случаях. Присвоить переменным max и min соответственно наибольшее и наименьшее из значений а, Ь. Присвоить переменной у значение , если х <3 1, а в осталь- ных случаях значение х2 + 2, если х < 0, и значение 2х + 1, если х <3 0. 7. Используя операторы цикла и присваивания, описать алгоритмы решения следующих задач: 1. Определить сумму первых т членов ряда, член с номером п (и = 1,2, 3,...) которого определяется выражением: 178
а)-----; (2 л)!’ (-1)" b)-----i— -----: и(и + 1)(и + 2) 2и(2и + 1) Определить сумму членов ряда от первого до члена с наименьшим номером, не превосходящего по абсолютной величине значения КГ6: п2 8. Разработать процедуры и функции для решения следующих за- дач обработки массивов: посчитать количество отрицательных элементов одномерного мас- сива вещественных чисел; посчитать сумму квадратов положительных элементов одномерно- го массива вещественных чисел; посчитать сумму квадратов отрицательных элементов одномерного массива вещественных чисел; преобразовать одномерный массив вещественных чисел, присвоив каждому элементу квадрат его значения; преобразовать одномерный массив вещественных чисел, уменьшив каждый элемент на абсолютную величину среднего значения элементов массива; преобразовать одномерный массив вещественных чисел, занеся в каждый элемент сумму всех предыдущих элементов (в первый элемент при этом необходимо поместить значение 0); посчитать сумму квадратов диагональных элементов двумерного массива вещественных чисел; посчитать максимальную сумму элементов в строках двумерного массива вещественных чисел; посчитать минимальную сумму элементов в столбцах двумерного массива вещественных чисел; преобразовать двумерный массив вещественных чисел, занеся зна- чение 0 во все элементы с двумя четными индексами.
9. Разработать процедуры и функции для решения следующих за- дач обработки строк: посчитать количество слов в строке (разделителем считать символ пробела); посчитать количество предложений в строке (разделителями счи- тать точку, вопросительный и восклицательный знаки); посчитать количество гласных (согласных) букв в строке; посчитать количество знаков препинания (знаки препинания: запя- тая, точка, точка с запятой, двоеточие); преобразовать строку, убрав лишние пробелы (оставить по одному пробелу между словами); удалить из строки повторы слов. 10. Разработать процедуры, реализующие операции вставки, удале- ния и поиска элемента для динамического двунаправленного списка (списка со ссылками на следующий и предыдущий элементы).
Глава 3. Интегрированная среда разработки приложений DELPHI Целью данной главы является краткое описание основных возмож- ностей и порядка разработки программ (далее по тексту — приложений) в среде программирования Delphi. Delphi позволяет создавать, компилировать, тестировать и редакти- ровать приложение в единой среде программирования. В качестве базо- вого языка в среде принят язык программирования Pascal, а именно его объектно-ориентированное расширение Object Pascal. Основные поль- зователи Delphi — это, во-первых, профессионалы — разработчики ин- формационных систем, и, во-вторых, программисты, применяющие сре- ду как средство быстрого решения своих задач. В начале главы дается краткое представление об основных интер- фейсных объектах среды Delphi, позволяющих управлять процессом создания, сборки и отладки приложения. Описаны интерфейсные окна, связанные с созданием и редактированием составляющих проекта. Дана характеристика файлов, составляющих приложение, и описан порядок компиляции, сборки и отладки приложения. Далее рассматривается процесс разработки приложения с точки зрения взаимосвязи понятий «Форма», «Компонент», «Свойства компо- нента», «Событие» и «Процедура-обработчик события». Описывается и поясняется на простых примерах содержательная составляющая поня- тий и последовательность действий при проектировании приложений. В конце главы приведены примеры разработки приложений «Каль- кулятор» и «Редактор», представляющие свойства и методы некоторых компонентов, а также их возможности по управлению функционирова- нием приложения. И 3.1. Интерфейс среды DELPHI В момент первой загрузки среды Delphi (далее будет представлена седьмая версия программного продукта — Delphi 7) на экране отобра- жаются четыре окна (рис. 3.1): 181
> , ______J-uBf : Fie Edt Search Mew Prefect Rin Component Oetebete Took Window Мй> :; j<Ncne> 1 0* ! f •i ® I @1-2? = ] j ;L Staridird I AddBonef j Win32] Svetemi DataAeoml Dele Conkofc j AEremej DeUSrwo] BDE | ADO | HarBew | Vl.L?..,t :згв я ► • н. j • :R sis" % apt и ju я » = ц jl .^;етд.7;яжхгыас^&««.я8ая^.:иетаа;^^ юаш^^^^^^ва^|а^иш1мимми^мвммижяв1 * * г:::Г:":.Ч::;;:::‘Т:П:Г:“"Т::““:’:'::’::::::::”.:::.":П':’':::*::Г::.:':':::::{ Ей:®йОйЙ:У|||Ж Рис. 3.1. Исходный вид Delphi • Главное окно Delphi с заголовком, содержащим имя открытого проекта (на рисунке — «Delphi? — Project2»); • Окно Обозревателя дерева объектов (Object Tree View); • Окно Инспектора объектов (Object Inspector); • Окно Конструктора формы с заголовком «Forml»; • Окно редактора кода, озаглавленное как «Unit 1.pas», которое первоначально перекрывается окном формы; • Окно Проводника кода (Exploring Unit 1 .pas). Окна Delphi можно перемещать, убирать с экрана, а также изменять их размеры. Delphi — однодокументная среда, т.е. среда, позволяющая одно- временно работать только с одним приложением. Главное окно. Главное окно Delphi, занимающее верхнюю часть экрана, содержит: строку заголовка, в которой отображается имя открытого проекта; строку меню с набором команд для управления разработкой и тес- тированием приложений; панель инструментов — кнопок, представляющих собой краткую форму функций меню; палитру компонентов, отображающую компоненты, с помощью которых создается приложение. Панель инструментов находится ниже меню в левой части экрана, а палитра компонентов — ниже меню и справа от панели инструментов. 182
Кнопками панели инструментов можно вызывать наиболее часто используемые команды, представленные в главном меню. Вызвать ко- манды главного меню можно также с помощью соответствующих ком- бинаций клавиш алфавитно-цифровой и функциональной клавиатур. Кнопки сгруппированы в шесть панелей инструментов: - стандартную панель, - панель просмотра, - панель отладки, - пользовательскую панель, - рабочий стол; - Internet. Можно управлять отображением панелей инструментов и изменять состав кнопок на них. Эти действия выполняются с помощью контекст- ного меню панелей инструментов или с помощью функции Главного меню (View/TooiBars/Customize...). Палитра компонентов используется для выбора и размещения на форме компонентов графического интерфейса. Группы компонентов размещаются на разных вкладках. В Delphi используется открытая компонентная архитектура, кото- рая позволяет добавлять компоненты в каждую группу и создавать но- вые группы компонентов. Разные конфигурации среды могут отличать- ся набором представленных компонентов, однако, всегда разрешено добавлять новые компоненты, независимо от того, где они были созданы. Компоненты являются «строительными блоками», из которых кон- струируются формы приложений. Окно Обозревателя дерева объектов. Обозреватель дерева объек- тов (Object Tree View) после запуска среды находится под главным ок- ном и отобажает древовидную структуру объектов текущей формы. Окно Инспектора объектов. Окно Инспектора объектов (Object Inspector) предназначено для управления компонентами (их размещени- ем и поведением на форме) на этапе проектирования интерфейса. Оно содержит две страницы — «Свойства» (Properties) и «События» (Events). Каждый компонент имеет свой набор свойств и событий, опре- деляющий ее индивидуальные характеристики и особенности, а также возможности по ее использованию в процессе работы приложения. Страница «Свойства» (список свойств) отображает и позволяет ме- нять характеристики текущего (выделенного) объекта в окне формы. Когда в среде Delphi создается новое приложение, первоначально Ин- спектор объектов отображает свойства текущей формы (см. рис. 3.1). Если при проектировании интерфейса нужно изменить что-нибудь, свя- занное с определенным компонентом, то это выполняется в Инспекторе Объектов. К примеру, можно изменить заголовок и размер компонента TLabel, изменяя свойства Caption, Height и Width (рис. 3.2). 183
’ Не ЕЛ Search View Project Ruf Component Database Toots Window He$> i: |<None> »} J tfjj t^.i : "?j =lp - @ И ФИ S'*****! |Addtionai| Win32 f Swsterel Date Access! Date Control* ‘ dbEroress] OalaSnao i BDE t ADO | InterBase |(S#C «li > • H I i >iii ti П1Г % А |лГ 4 Ш1 IS в Я И —ill i& I.....................I JJ g . TZ."”’."b?» ’ s -;ei &6si ♦ : ;7-’'r;::::7:::: 1 _________________________________Sg :|ТлЫЛ тЗгГ 3?- Properties j Events | | ' i>ign fafione : * Abgrnieint ВШ SAochors :(аМ.е11.акТар| |&| ' AutoSee it rue g|i| BD^i^ ''':'~ '’'’’1b^foRtfit’’ ’' JMBi f Caption '"" iLabSi —) f i Color QcBtnFace iiSConstiaH* . _ : rrSueCorntiainCj) | Cursor icrDefatJt | i D*eoCur*o* .Jc*D*aiJ . ". t : Draf^nd dkDrag ? DragMode jdrrHanual % ! Enabled True Al shown -; : j 1; i iModfed ilnsert ACodeXpiagram/ Рис. 3.2. Изменение свойства Caption для TLabel На странице «События» (список событий) задаются действия, кото- рые должны выполняться объектом при наступлении определенных со- бытий. Страница событий связана с Редактором кода: если курсор ма- нипулятора или текстовый курсор расположен в некоторой строке на правой половине страницы, то при двойном нажатии клавиши манипу- лятора «мышь» автоматически в окне Редактора кода создается заготов- ка для процедуры обработки соответствующего события. Если для какого-либо события объявлена процедура, то в дальней- шем при работе с приложением она выполняется автоматически при возникновении этого события. Такие процедуры называют обработчи- ками, так как они служат для обработки событий. Окно Конструктора формы. Окно Конструктора формы — это окно разрабатываемого приложения, внешний вид и наполнение кото- рого определяется при проектировании. Компоненты графического ин- терфейса помещаются на форму и располагаются на ней по желанию проектировщика (рис. 3.3). Приложение может иметь несколько форм. Первоначально форма имеет заголовок Forml. Объекты, разме- щаемые на форме, должны быть выбраны на Палитре компонентов. Для этого необходимо: ♦ щелкнуть мышью на пиктограмме нужного компонента; • щелкнуть в том месте формы, где должен быть расположен компонент. 184
Редактировать размещение компонента можно с помощью группы команд меню Edit, а также с помощью контекстного меню. Конструктор формы интуитивно понятен и прост в использовании, что в большой степени упрощает создание визуального интерфейса. Окно Редактора кода (рис. 3.4), первоначально скрытое окном формы, предназначено для редактирования исходного кода модуля про- граммы, описывающего данную форму. Это обычный текстовый редак- тор, с помощью которого можно редактировать текст модуля и другие текстовые файлы приложения (а также и простые текстовые файлы в кодировке Windows). В Редакторе кода можно открывать несколько файлов, каждый из которых размещается на отдельной странице. Между страницами можно переключаться с помощью мыши, т.е. «листать» файлы по ярлычкам. Окно Редактора кода первоначально размещено «под» окном формы, поскольку первый этап разработки формы — размещение на ней элементов графического интерфейса — сопровождается автоматическим генерирова- нием программного кода. Необходимость использования окна модуля для ввода и редактирования наступает, когда программируются обработчики событий для компонентов, размещенных на форме. Переключение между редактором кода и формой поддерживается функциональной клавишей F12. Окно Проводника кода. Окно Проводника кода располагается в ле- вой части окна редактора кода. В нем в виде дерева отображаются все объекты модуля формы, например переменные и процедуры. В окне Проводника кода удобно просматривать объекты приложения и обра- 185
^"^TFoirml i* * G3 Veriablej/Constants r+j GJ Uses Uni1 | :unit Unitl; interface Windows, Messages, SysUtils, Variants, Graphics, Controls, Forms,| Dialogs; . Л jtype | TForml = class(TForm) Editl: TEdit; I Memol: TMemo; i RadioGroupl: TRadioGroup; В utt о nl: ТВutton; ButtonZ: TButton; ComboBoxl: TComboBox; CheckBoxl: TCheckBox; CheckBoxZ: TCheckBox; private = ( Private declarations } public ( Public declarations } J end; S ЗШ..........._........ ............ .............. Modfed -lrcat [\Code/Diagram/ : Рис. 3.4. Окно Редактора кода щаться к ним, что особенно важно при работе с большими модулями. В функции Проводника входит и автоматизированное создание новых классов. При закрытии файла закрывается и Проводник кода. Проводник кода можно убирать и выставлять с помощью команды меню View/Code Explorer. И 3.2. Характеристика проекта Delphi Любой проект представляет собой совокупность не менее чем семи файлов: ♦ Главный файл проекта — файл с расширением .dpr, представляет собой основной модуль программы. • Файл главной формы (описания формы) — файл с расширением .dfin, используется для сохранения информации о внешнем виде главной формы. • Первый модуль программы (модуль главной формы) — файл с расширением .pas, автоматически появляется в начале работы. 186
Рис. 3.5. Структурная схема приложения Delphi • Файл ресурсов — файл с расширением .res, содержит иконку для проекта, создается автоматически и имеет то же имя, что и главный файл проекта. • Файл параметров проекта — файл с расширением .cfg, текстовый файл для сохранения конфигурации данного проекта. Имя файла совпадает с именем главного файла проекта. • Файл параметров среды (Delphi Options File) — файл с расширени- ем .dof, текстовый файл, в котором хранятся текущие установки параметров проекта, таких как параметры компиляции, рабочие ди- ректории, условные директивы, параметры командной строки. Имя файла совпадает с именем главного файла проекта. • Файл настроек рабочей области среды (Desktop File) — файл с расширением .dsk, в котором сохраняется состояние среды Delphi для проекта. Имя файла совпадает с именем главного файла проекта. Помимо перечисленных файлов в проект могут входить и дополни- тельные модули — файлы с расширением .pas (рис.3.5). Файлы проекта по умолчанию располагаются в одном каталоге. Если главный файл проекта сохраняется под другим именем, то это имя получают и файлы с расширением .res, .dof, .cfg и .dsk. При запуске Delphi автоматически создается новый проект с име- нем Projectl.dpr, который имеет в своем составе форму Forml.dfin и со- ответствующий ей модуль Unitl.pas. Главный файл проекта При выполнении операций с проектом код файла проекта формиру- ется средой Delphi автоматически: 187
program Projectl; // Имя программы uses // Далее следует перечисление используемых // модулей Forms, // Имя подключаемого модуля Unitl in 'Unitl.pas' {Forml}; // Перечисление модулей всех форм проекта {$R *.RES} // Директива подключения к проекту файла // ресурсов begin // Начало блока программы Application.Initialize; // Инициализация приложения Application.CreateForm(TForml, Forml) ; // Создание формы Application.Run; // Запуск приложения end. // Конец блока программы Служебное слово uses сообщает компилятору, какие модули долж- ны быть подключены при построении приложения. В приведенном примере подключается библиотечный модуль Forms и модуль с исход- ным кодом формы Unitl.pas. Имя формы (Forml) указано в виде ком- ментария. Если проект включает в себя несколько форм, то перечисля- ются модули всех форм проекта. Просмотреть и отредактировать код файла в окне Редактора кода можно с помощью команды Project/View Source (Проект/Просмотр исходного текста). Подключаемый файл ресурсов имеет имя, совпадающее с именем файла проекта. Файлы формы Для каждой формы автоматически создаются файл описания, пер- воначально имеющий имя Unitl.dfm, и файл модуля Unitl.pas (файлы модуля формы и описания формы имеют всегда одинаковое имя, но ко- торое отличается от имени файла проекта). Файл описания формы (*.dfm) — текстовый файл, содержащий па- раметры формы и ее компонентов. При конструировании формы в Файл описания автоматически вносятся соответствующие изменения. 188
Open 3 a| aptf] |nraj | calcUrat.dfm I SBBOEffil Имя Файла Jki Файлов. | U rail dim | Odphi form (’dim j I Открыть^ I* ' , Отмёна*7| ‘. Справка' |-~ Рис. 3.6. Диалоговое окно открытия файла описания формы Чтобы отобразить содержание этого файла на экране, необходимо: предварительно сохранить файл формы на диске, например, с по- мощью команды File/Save (Файл/Сохранить) и закрыть окно формы с помощью команды File/Close (Файл/Закрыть); активизировать команду File/Open (Файл/Открыть); в диалоговом окне Open (Открыть) в списке типов файлов найти и установить маску *.dfm; выделить нужный файл и нажать на кнопку Open (Открыть) (рис. 3.6). Редактор кода и его содержимое будет доступно для просмотра и редактирования описания формы. Например, описание формы, приве- денной на рис. 3.3 и содержащей основные, наиболее часто используе- мые при проектировании приложений визуальные компоненты, выгля- дит следующим образом: object Forml: TForml // Параметры формы: Left = 162 // Координаты левого верхнего // угла формы относительно экрана Top = 99 Width = 440 // Ширина и высота Height = 341 Caption = ’Forml' // Заголовок формы Color = clBtnFace // Цвет фона Font.Charset = DEFAULT_CHARSET //Параметры шрифта Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False 189
PixelsPerlnch = 96 TextHeight = 13 object Memol: TMemo // Параметры компонента Memol // класса TMemo // Назначение компонента — // отображение и редактирование // текстов Left = 16 // Координаты левого верхнего // угла компоненты относительно // формы Тор = 48 Width = 153 Height = 193 Lines.Strings = ('Memol') // Текст содержимого окна // компонента TabOrder =0 // Номер компонента на форме // для прохода по компонентам // с помощью клавиши <ТаЬ> end object Editl: TEdit // Параметры компонента Editl // класса TEdit // Назначение компонента — // отображение и редактирование // одной строки текста Left = 16 Тор = 8 Width = 153 Height =21 TabOrder = 1 Text = 'Editl' // Текст содержимого // окна компонента end object ComboBoxl: TComboBox // Параметры компонента // ComboBoxl класса // TComboBox — раскрывающийся // список элементов Left = 248 Top = 8 Width = 145 Height = 21 ItemHeight = 13 TabOrder = 2 Text = 'ComboBoxl' end object GroupBoxl: TGroupBox // Параметры компонента // GroupBoxl класса TgroupBox // — группирующая рамка для // альтернативных кнопок- // переключателей 190
Left = 248 Top =48 Width = 153 Height = 129 Caption = 'GroupBoxl' TabOrder = 3 object RadioButtonl: TRadioButton // Параметры компонента RadioButtonl класса // TradioButton — радиокнопка для представления // варианта выбора Left = 16 Тор = 24 Width = 113 Height = 17 Caption = 'RadioButtonl' TabOrder = 0 end object RadioButton2: TRadioButton Left = 16 Top = 56 Width = 113 Height = 17 Caption = 'RadioButton2' TabOrder = 1 end object RadioButton3: TRadioButton Left = 16 Top = 88 Width = 113 Height = 17 Caption = 'RadioButton3' TabOrder = 2 end end object CheckBoxl: TCheckBox // Параметры компонента CheckBoxl // класса TCheckBox — независимая // помечаемая кнопка (или флаг), // служащая для формирования // значений «Да», «Нет» или // «Недоступно» Left = 264 Тор = 192 Width = 97 Height = 17 Caption = 'CheckBoxl' TabOrder = 4 end object CheckBox2: TCheckBox Left = 264 Top = 224 Width = 97 Height = 17 Caption = 'CheckBox2' 191
Taborder = 5 end object Buttonl: TButton // Параметры компонента Buttonl // класса TButton — управляющая // кнопка Left = 248 Top = 280 Width = 75 Height = 25 Caption = 'Buttonl' TabOrder = 6 end object Button2: TButton Left = 336 Top = 280 Width = 75 Height = 25 Caption = 'Button2* TabOrder = 7 end end Чтобы снова открыть окно формы, необходимо: закрыть файл описания формы в Редакторе кода с помощью коман- ды File/CIose; открыть форму для конструирования с помощью команды View/Forms и выбора в диалоговом окне нужной формы (рис. 3.7). Файл модуля формы (*.pas) создается автоматически при добавле- нии новой формы и содержит описание класса формы (состав и пове- дение компонентов и функционирование обработчиков событий). В процессе конструирования формы (при размещении на форме компонентов) в модуль формы вносятся соответствующие изменения, Рис. 3.7. Выбор формы для конструирования 192
причем изменения в описание класса вносятся автоматически, а про- цедуры обработки событий кодируются разработчиком. Открыть модуль формы можно либо с помощью команды меню File/Open, либо в диалоговом окне команды View/Units (Про- смотр/Модули), выбрав нужный модуль. Файл модуля формы (Unitl.pas), соответствующий представленно- му выше описанию формы, выглядит следующим образом: unit Unitl; // Заголовок модуля interface // Интерфейс модуля, т.е. описание констант, типов, // переменных, процедур и функций, которые могут быть // использованы главной программой при вызове этого // модуля uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; // Список подключаемых модулей type TForml = class(TForm) // Описание класса формы Memol: TMemo; Editl: TEdit; ComboBoxl: TComboBox; GroupBoxl: TGroupBox; RadioButtonl: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; CheckBoxl: TCheckBox; CheckBox2: TCheckBox; Buttonl: TButton; Button2: TButton; private { Private declarations } public { Public declarations } end; var Forml: TForml ; // Размещение объекта // описанного класса Implementation // Исполнительная часть модуля {$R *.DFM} // Подключение файла описания // формы end. 193
Тексты модулей форм отображаются в окне Редактора кода и ре- дактируются с его помощью. Файлы модулей Помимо файлов, создаваемых средой Delphi, в проект могут вклю- чаться файлы, не связанные с формами. Например, константы, перемен- ные, процедуры и функции, общие для нескольких модулей проекта, целесообразно оформить в виде отдельного модуля л подключать его по мере необходимости. Такие модули оформляются по правилам языка программирования Object Pascal и сохраняются в отдельных файлах с расширением .pas. Для того чтобы модуль в дальнейшем мог быть использован дру- гим модулем или проектом, его имя должно быть указано в разделе uses этого модуля или проекта как имя подключаемого модуля. Файл ресурсов При первом сохранении проекта автоматически создается файл ре- сурсов с именем, совпадающим с именем файла проекта, и расширени- ем .res. Файл ресурсов имеет иерархическую структуру, в которой ресурсы разбиты на группы, а каждый ресурс имеет уникальное в пределах группы имя. Имя ресурса задается при его создании и в последующем используется в приложении для доступа к этому ресурсу. В файле ресурсы разбиты на группы. Каждая группа имеет имя. Файл содержит следующие ресурсы: • пиктограммы; • растровые изображения; • курсоры. Первоначально файл ресурсов содержит пиктограмму проекта. Ее можно изменить, используя Редактор изображений (Image Editor). Вы- зывается Редактор командой Tools/Image Editor (Инструменты/ Редак- тор изображений). Image Editor позволяет обрабатывать файлы четырех видов. Три из них объединяют файлы, содержащие ресурсы, определенные выше: • растровые изображения (*.ВМР); • пиктограммы приложений (*.1СО); • курсоры (*.CUR). К последнему (четвертому) виду относятся файлы, имеющие формат откомпилированных файлов-ресурсов (файлы с расширением .res), кото- рые могут в свою очередь содержать ресурсы предыдущих трех видов. 194
File Edit Text View Icon Window Help X: -012 Y: 013 Рис. 3.8. Пиктограмма проекта в окне Редактора изображений На рис. 3.8 показано окно Редактора, в которое загружен файл, и выполняется редактирование пиктограммы приложения. Пиктограмма проекта находится в группе Icon и по умолчанию имеет имя MAINICON. Файл параметров проекта Файл параметров проекта содержит конфигурационные установки, использующиеся при компиляции приложения, такие как директории для поиска файлов проекта, текущие установки директив компиляции. Например, если конфигурационный файл содержит следующие строки: -$D+ -$1+ -U"C:\ DELPHI\UNITS" Это означает, что проект будет содержать коды отладчика (SD+), в про- ект будет включена автоматическая генерация результатов выполнения процедур ввода-вывода ($1+), и при построении приложения для поиска модулей будет использоваться директория С:\ DELPHI\UNITS. Файл параметров среды Файл параметров среды представляет собой текстовый файл, кото- рый содержит текущие установки для параметров проекта, таких как: 195
• настройки компилятора и компоновщика; • имена служебных каталогов; • директивы условий компиляции; • параметры командной строки. Для установки параметров проекта используется диалоговое окно, вызываемое с помощью команды меню Project / Options. Параметры разбиты на группы, каждая из которых располагается на соответствую- щей странице (рис. 3.9). После установки отдельных параметров Delphi автоматически вносит нужные изменения в файл параметров среды, представляя информацию в виде текстовых строк. Например, к проекту на стадии разработки имеет смысл подклю- чать отладочную информацию. Для этого необходимо установить оп- цию Debug Information на странице Compiler. Файл настроек рабочей области среды Файл содержит настройки рабочей области для текущего проекта, например информацию о том, какие окна открыты, где находится кур- сор и т.п. Такая информация позволяет восстановить состояние рабочей области при каждом новом открытии проекта в среде. Для того чтобы обеспечить автоматическое создание и сохранение файла настроек, необходимо: с помощью команды меню Tools/Environment Options открыть диалоговое окно; Directories/Conditionals 1 Version Info | Packages Forms { Application Compier | Compier Messages | Lriker r Code generation................. Г" jOptrozatiori Г Stack frames Г Pentium-safe FDIV | g j Record field afignment i i • Syntax options------------- - - = P Strict vac-strings i Г Complete boolean eval p Extended syntax Г Tj^ed @ operator ' $7 Open parameters < p Huge strings ; T~ Assignable typed constants " Runtime errors - " Г Range checking p fZO checking Г Overflow checking Debugging p Debug information p Local symbols p Reference info p Definitions only P Assertions Г Use Debug DCUs Г" Default I OKI Cancel I Help Рис. 3.9. Окно установки параметров проекта 196
Type Lbrary ] Environment Variables | Internet | Delphi Direct Preferences | Designer | Object Inspector | Palette | Library | Explorer Autosave options Г* Editor files 15* Project desktop Desktop contents • " (• desktop only C Desktop and symbols ••• Compiino and running I* Show fompSer progress Г~ Wgm on package rebuid Г” Minimize on run 1*7 Hide designers on run Г Docking i P Auto drag docking \ Pressing the Control key whte ; dragging w3l prevent window docking Shared repository Directory: | Browse... It- j] Cancel Рис. 3.10. Окно настроек среды на странице Preferences окна в разделе Autosave options установить параметр Project Desktop. Delphi обновляет файл настроек рабочей области всякий раз при закрытии проекта. Файл хранится в той же директории, что и главный файл проекта, и имеет то же имя. При новой загрузке проекта внешний вид среды восстанавливается, т.е. становится таким же, как при предыдущем закрытии проекта. При создании нового проекта опция автоматического сохранения настроек проекта по умолчанию всегда включена. Резервные файлы Среда Delphi может создавать резервные копии главного файла проекта и файлов модулей и описаний форм. Резервные копии файлов содержат в расширении тильду (~) в качестве первого символа и созда- ются при повторном сохранении проекта для тех файлов, в исходном коде которых были сделаны изменения: * -DP — резервная копия главного файла проекта. * .~РА — резервная копия модуля. * .~DF — резервная копия файла описания формы. 197
Jdikr PrupOHips f £oom to full screen 17 Sgrt popup pages menu Right margin: Gutter width: |a> 3 I30 -’J J General| Source Options Display | Key Mappings] Color | Code Insight | г Display and He options i Г' BRIEF cursor shaoes i 17 Create feackup file r Margin and gutter I |7 Visible right margin [ 17 Visible gutter Editor font: £ize: | Courier New - -g Sample: AaBbYyZz [[ OK ;| Cancel I Help Рис. 3.11. Установка параметров редактирования Задать режим сохранения резервных копий можно с помощью диа- логового окна команды меню Tools / Editor Options, установив на стра- нице Display опцию Create backup file (рис. 3.11). 3.3. Компиляция и выполнение проекта Компиляция проекта Компиляция может быть выполнена на любой стадии разработки проекта и позволяет проверить внешний вид интерфейсных окон и пра- вильность функционирования фрагментов создаваемого кода. Запуск процесса компиляции выполняется по команде меню Project /Compile <Project_Name>. В строке меню команды указано имя проекта, разработка которого выполняется в текущий момент. При со- хранении проекта под другим именем, соответственно, должно изме- ниться имя проекта в меню. При компиляции происходит следующее: • компиляция файлов всех модулей, содержимое которых измени- лось после последней компиляции. В результате создается файл с расширением *.DCU; 198
• перекомпиляция модуля, в который внесены изменения и модулей, использующих его с помощью директивы uses; • создание исполняемого файла — приложения *.ЕХЕ; в процессе компиляции проекта создается готовый к выполнению файл (*.ЕХЕ) или динамически загружаемая библиотека (*.DLL). Само приложение является автономным и не требует при своей ра- боте дополнительных файлов Delphi. Имя приложения совпадает с именем файла проекта. Ход процесса компилирования будет отображаться на экране, если установить опцию Show compiler progress в меню Tools/ Environment Options/ Preferences (рис. 3.12). Сборка проекта Сборка проекта выполняется командой Project/Build <Project_ Name> . При сборке перекомпилируются все модули, входящие в проект, независимо от того, были в них внесены изменения или нет. Запуск проекта Запускать проект можно из среды Delphi и из среды Windows. Запуск проекта из среды Delphi выполняется командой Run/Run. Созданное приложение начинает свою работу. Если в модули програм- мы были внесены изменения, то перед запуском проекта выполняется компиляция проекта. При запуске проекта в среде Delphi необходимо учитывать сле- дующие особенности: • нельзя запустить вторую копию приложения; • вносить изменения в модули (т.е. продолжить разработку проекта) можно только после завершения работы приложения; • в случае, если приложение не может нормально завершить работу, необходимо выполнить завершение средствами Delphi с помощью команды меню Run/Program Reset. Рис. 3.12. Отображение процесса компиляции 199
Запуск проекта из среды Windows осуществляется так же, как и запуск любого другого приложения. Отладка приложения Для отладки приложений в среде Delphi можно использовать встроенную систему отладки. Проект в этом случае должен быть от- компилирован с отладочной информацией. Подключение отладчика происходит через установку опции Debug Information в окне параметров проекта (см. рис. 3.9). Встроенный отладчик (Debugger) облегчает поиск и устранение ошибок в приложениях. Средства отладчика доступны: • с помощью команд пункта меню Run (Выполнение); • с помощью подменю View / Debug Windows (Просмотр/Окна отладки). Система отладки предусматривает следующие действия: • выполнение до указанного оператора (строки кода); • пошаговое выполнение приложения; • включение и выключение точек останова; • выполнение приложения до точки останова; • просмотр значений данных в окнах просмотра; • установку новых значений данных при выполнении приложения. Установка параметров отладчика выполняется с помощью команды Tools/Debugger Options (параметры отладчика) (рис. 3.13). Если, например, в окне параметров отладчика установлена опция Inspector stay on top (см. рис. 3.13), то окно Инспектора отладки будет видно всегда. На рис. 3.14 показано окно инспектора отладки со значе- нием переменной Arg2 в момент временного прекращения работы при- ложения в точке останова (строке текста, выделенного на рисунке жир- ной точкой). И 3.4. Разработка приложения В соответствии с порядком разработки приложений с графическим интерфейсом (см. п. 1.7) необходимы последовательно два взаимосвя- занных этапа: проектирование функционального интерфейса приложе- ния и программирование процедур обработки событий, возникающих при работе пользователя с приложением. 200
GeneraJ | Event Log | Language Exceptions | OS Exceptions | r G eneral —................... Г" Map TD32 keystrokes on run ; Г Mark buffers read only on run i I* jfopectorc on topi ' i Г Enable CQM cross-process support Г Inspect» Defaults : Show inherited Г Paths ! Debug Symbols Search Path: Г” Alow function calls in new watches Г” Rearrange editor local menu on run Г Qebug spawned processes Г Show fjdy quaffed names i Debug D£U Path: |$(DELPHI)\Lb\Debug R Integrated debugging Г ок J Cancel | Help | Рис. 3.13. Окно параметров отладчика Ffe ЕЛ Search view Protect Rw Component Database Toob Window Hefe W~— ........j >g *2 TForml a$ TOperabon VanafcteWCcnstar*» <var irinteger; begin R;= Argl+Arg2; R:s Argl*Arg2; R:= Argl/Arg2; end; str(R:10:4,Result) ; Рис. 3.14. Просмотр значения переменной в окне отладки 201
В процессе создания интерфейса приложения (первый этап разра- ботки приложения) происходит последовательное размещение в Окне Формы компонентов, наилучшим образом соответствующих функцио- нальному назначению приложения. Для установки параметров разме- щения компонента на форме используется окно Инспектора объектов. Чтобы поместить нужный компонент на форму, необходимо вы- брать его из Палитры компонентов и указать его местоположение на области формы. После размещения компонента на форме можно изме- нять с помощью мыши его положение и размеры. По умолчанию компоненты выравниваются на форме по линиям сетки, которая при проектировании отображается на поверхности формы. Выделение нескольких компонентов на форме выполняется с по- мощью мыши при нажатой клавише <Shift>. Редактировать размещение компонентов можно с помощью кон- текстного меню или группы команд меню Edit, например: Align — выравнивание группы компонентов; Bring to front— перевод компонента на передний план; Send to Back — перевод компонента на задний план; Size — установка новых размеров компонента. Следующий шаг — определение свойств компонентов при проек- тировании. По типам хранящихся в них данных свойства делятся на следую- щие группы: • простые — свойства, значения которых являются числами или строками (например, Caption, Name, Left, Top); • перечисляемые — свойства, которые могут принимать значения из предложенного набора (списка) (например, свойства Visible и Enabled могут принимать значение True или False)', • множества — свойства, значения которых представляют собой комбинацию значений из предлагаемого множества (например, свойство формы Bordericon); • объекты — свойства, значения которых представляют собой объ- екты. В области значения свойства-объекта в скобках указывается тип объекта (например, свойство Font типа TFont). Управлять свойствами компонента можно непосредственно в окне Конструктора формы или с помощью Инспектора объектов. Для доступа к свойствам компонента через окно Инспектора объек- тов необходимо сначала в раскрывающемся списке верхней части окна, где отображаются название компонента и его тип, выбрать нужный компонент. В левой части окна Инспектора объектов приводятся назва- ния всех свойств выбранного компонента, которые доступны на этапе разработки приложения. Для каждого свойства справа содержится зна- чение этого свойства. 202
Чтобы изменить значения свойств, необходимо выбрать изменяе- мое свойство и, в зависимости от типа данных свойства, выполнить сле- дующие действия: • в случае простого свойства — ввести в правой колонке Инспектора объектов новое значение; • в случае перечисляемого свойства — открыть список, появившийся в соответствующей ячейке правой колонки, и выбрать нужное зна- чение; • в случае множества — установить значение True на выбираемых элементах предлагаемого множества; • в случае объекта — заполнить отдельно значение каждого поля объекта или воспользоваться вспомогательными диалоговыми ок- нами задания значений. Утверждается изменение свойства нажатием клавиши <Enter>, от- меняется — нажатием клавиши <Esc>. Если для свойства введено не- правильное значение, то выдается предупреждающее сообщение. Результат изменения свойств компонента сразу отображается в ок- не Конструктора формы. На рис. 3.15 показано изменение простого свойства формы Caption (введено значение «Новая форма»), перечисляемого свойства Color и свойства типа множество — Bordericon. На рис. 3.16 пред- ставлено окно заполнения свойства типа объект — установка шрифта (свойство Font). При выполнении приложения свойства компонентов можно изме- нять с помощью операторов присваивания. Например, изменить заголо- вок формы можно программно путем оператора присваивания: Forml.Caption:= 'Новая форма'; Функциональная схема работы приложения определяется про- цедурами, которые выполняются при возникновении определенных событий, происходящих при взаимодействии пользователя с управ- ляющими элементами формы. Реакция на события присуща каждой форме и не зависит от назначения приложения и его особенностей. На втором этапе разработки приложения для каждого компонента, размещенного на форме, разработчик должен определить нужную реак- цию на те или иные действия пользователя (например, нажатие кнопки или выбор переключателя). Каждый компонент имеет свой набор событий, на которые он может реагировать. Вместе с тем, существуют две категории стан- дартных событий, определенных для всех визуальных компонентов. К первой категории относятся события, определенные для всех без ис- ключения визуальных компонентов, а ко второй — события, характерные только для оконных визуальных компонентов. В табл. 3.1 представлены 203
jobject li | Forml TF< [ DefauitMorior DockSde DragKnd Dra^dode ' Enabled'~ Fori ; Cheset Height Name Pitch LjasMe-. IsBokJ L "fslt*: Object Inspector |Form1: TForml Pioperties | Events | ,E Anchors JakLeft.akTop] ~-l ' AutoScrol True : AutoSrze False | BDWode _ _ ’bdLeflTcflight _ BBorderlconS- _ [biSys_temMenu.biMrwnze) bSystemMenu True — j biMmimize 'True । bMaxrnize ‘ .False *' biHelp ' t False" В orderS tyte _ bsSizeable 1 BorderWkfth 0 CfenWrith | Color ^Constraints Cursor tnFace_______ cHighightText I clBtnFace feT'Tv—"• ...M dBtnShadow ^htMen, j, c|GlavText Рис. 3.15. Изменение свойств формы 'Шрифт Шрифт MS Sans Serif Начертание: (TSizeConstra ~ crDefaul _ dnActrveForn ,d<Drag 'dmManual i|TFont| DEFAULT-CI ' cN/indow' 11ВМ1И11| .MS Sara Sen IpDefai* " MS Sent Tr MT Extra О Niagara Engraved О Niagara Solid О OCR A Extended % Old English Text M Г Зачеркнутый Г Подчеркнуть^ Цвет обычный жирный курсив Образец Набор символов (Кириллица . о Рис. 3.16. Изменение свойства Font события, общие для всех визуальных компонентов, а в табл. 3.2 — собы- тия оконных визуальных компонентов. Процедура, связанная с несколькими событиями для различных компонентов, называется общим обработчиком и вызывается при воз- никновении любого из связанных с ней событий. 204
Таблица 3.1 Событие Момент возникновения и параметры процедур OnClick Возникает при нажатии левой клавиши мыши в области компонента или при нажатии клавиши <Enter>, если на компонент установлен фокус OnDblClick Возникает при двойном нажатии левой клавиши мыши в области компонента OnDragDrop Возникает в случае, когда пользователь «отпустил» пере- таскиваемый объект. Параметр Source соответствующей процедуры-обработчика содержит указатель на объект, который был «отпущен», а параметр Sender — на объект, принявший «перетаскиваемый» OnDragOver Возникает в случае, когда пользователь «перетаскивает» объект для его размещения в рамках другого компонента OnEndDrag Возникает в конце процедуры «перетаскивания» объекта. Параметр Sender соответствующей процедуры-обработчика указывает на «перетаскиваемый» объект, а параметр Target — на его образ в рамках принимающего компонента OnMouseDown Возникает при нажатии любой клавиши мыши в области компонента. Параметр Button соответствующей процеду- ры-обработчика этого события позволяет определить, ка- кая клавиша была нажата, параметр Shift — были ли нажа- ты клавиши <Shift>, <Ctrl> или <Alt> вместе с клавишей мыши, а параметры X и Y содержат координаты курсора мыши в момент нажатия клавиши OnMouseMove Возникает при «буксировке» манипулятора «мышь» (т.е. при его перемещении при нажатой левой клавише). Пара- метр Shift соответствующей процедуры-обработчика опре- деляет, были ли нажаты клавиши <Shift>, <Ctrl> или <AIt> вместе с клавишей мыши, а параметры X и Y содержат координаты курсора мыши в момент нажатия клавиши OnMouseUp Парное для OnMouseDown. Возникает, когда ранее нажа- тая клавиша мыши отпущена. Имеет те же параметры, что и OnMouseDown Для создания процедуры обработки события нужно: выделить на форме компонент; перейти на страницу событий Инспектора объектов; выделить событие, для которого будет создаваться процедура- обработчик события; посредством двойного нажатия мыши в области значения события получить доступ в модуль формы, где Delphi автоматически соз- даст заготовку процедуры-обработчика; в месте, где будет установлен текстовый курсор, написать код, ко- торый должен выполняться при возникновении события. 205
Таблица 3.2 Событие Момент возникновения и параметры процедуры On Enter Возникает, когда компонент получает фокус ввода OnExit Возникает при потере фокуса компонентом OnKeyDown Возникает при нажатии любой клавиши на клавиатуре. Па- раметр Key соответствующей процедуры-обработчика имеет тип Word и может содержать коды виртуальных клавиш. Параметр Shift передает информацию о нажатии клавиш <Shift>, <Ctrl>, <Alt> и о нажатии клавиш мыши OnKeyPress Возникает при нажатии клавиши на клавиатуре. Параметр Key соответствующей процедуры-обработчика имеет тип Char и содержит ASCII-код нажатой клавиши. Для клавиш, которые не имеют ASCII-кодов (например, <Shift>, <Ctrl> или <Alt>), событие не возникает OnKeyUp Парное для OnKeyDown. Возникает, когда пользователь от- пускает нажатую ранее клавишу. Имеет те же параметры, что и OnKeyDown Итак, для обеспечения выполнения функций приложения необходимо: • задать в Инспекторе объектов значения свойств и процедур обработки событий; • написать программный код для заданных процедур обработки событий. Создание простейшего приложения Рассмотрим процесс создания простейшего приложения для иллю- страции последовательности действий при разработке. Пусть форма нашего приложения содержит окно для редактирования строки текста и кнопку, нажав на которую, пользователь должен получить в окне редактирования текст: «Ура!!! Приложение работает!!!». С помощью команды меню File/New Application создается глав- ный файл проекта (имя по умолчанию Projectl) и заготовка, содер- жащая главную форму приложения, для которой уже автоматически построено описание (Unitl.dfm) и модуль (Unitl.pas). Форма содер- жит основные элементы окна Windows: заголовок Forml, кнопки минимизации, максимизации и закрытия окна и кнопку вызова сис- темного меню. Разместим на форме компонент редактирования строки — Edit клас- са TEdit (рис. 3.17). Компонент находится на странице «Стандартные» (Standard) Палитры компонентов. Имя компонента по умолчанию Editl (следующий компонент этого же класса получит имя Edit2 и т.д.). Оставим имя компонента без изменения, а значение свойства компонента 206
| Rte Edt Search Mew Prelect Run Component Database . Took Window Het | |<None> ' »f ; 0 (7/ & ♦, ,-, e; t-3 4$, Stendaid' lAdrtewwTl Wrf2lT«temiTtfa'^»'>r^C^fcll2. Т^еНЙПТ7WI < ?j ]l ^ ~-BB°а и В 3u ig » Д}, »j Рис. 3.17. Размещение на форме компонента Edit 1 Text обнулим. Для этого необходимо очистить содержимое правой час- ти строки Инспектора объектов для свойства Text (по умолчанию зна- чение этого свойства совпадает со значением свойства Name — имя компонента). По умолчанию для текстовой строки, отображаемой в окне компо- нента Editl, установлен размер шрифта, равный 8 пунктам. Чтобы сде- лать буквы крупнее, изменим свойство Font.Size (размер шрифта), задав новое значение, равное 12. Следующий компонент приложения — кнопка. Разместим на форме кнопку класса TButton (страница «Стандартные» Палитры компонен- тов). На форме компонент получит имя Buttonl. Изменим свойство кнопки Caption (Заголовок), установив значение «Проверка работы приложения» (рис. 3.18). Построение интерфейса приложения завершено. Следующий шаг — определение функций приложения. Разрабатываемый пример должен про- демонстрировать реакцию приложения на нажатие кнопки. Значит необхо- димо разработать обработчик события OnClick для кнопки. Для этого пе- реключимся в Инспекторе объектов на страницу Events (События) для компонента Buttonl. С помощью двойного нажатия левой клавиши мыши на левой части строки события в Инспекторе объектов попадем в процедуру обработки события, заготовку для которой среда генерирует автоматиче- ски, и по положению текстового курсора введем оператор: 207
1ЖЖЯЖ1!Я!М!!^ММЯИИШ1НМИМММИМЙ8ИКМ^ШИВ|^МЙН ,||Ду^ДфМуа?ЭД - Ini х| । Fto Edt Search Vfew Project Run Component Database Tools Window Нй> j fe°™=>. д! <2 4! *j iJ - Q a C? JS Й, • <8j <Fl ' iS Ф ; S“n4®l |мл«м|| Wr32| Surtid OMAeceol Oala C<rtZiljL 'I gjj~^ W»-и I >^T~~ Я A PT, S * <? Я» • *j Рис. 3.18. Размещение на форме компонента Buttonl Editl.Text := 'Ура!!! Приложение работает!!!'; Оператор присваивания в данном случае заполнит свойство Text компонента Editl изнутри приложения при возникновении события нажатия на кнопку (рис. 3.19). Разработка приложения завершена. С помощью команды меню File/ Save Project As... сохраним результаты работы, присвоив главному файлу проекта имя Example.dpr, а модулю формы — имя Ex_Unit.pas. Код модуля формы с обработчиком события кнопки: unit Ex_Unit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForml = class(TForm) Editl: TEdit; Buttonl: TButton; procedure ButtonlClick(Sender: TObject); 208
Defpf» 7 - fwsffspfe j Ffe ЕЛ Search view' Project Rur, CMoanent Database .Walt WxtowTwp ][|<Noie> 3 I i * J'Q ft it ЛЛ- <Ь Ф1' Й ! AT*"11*5^1^21 Smewj Oal,teia| DwOrti] «Lilt [if Й51| 11.1 a ?T 'If^3IT а [Д =! jd в Й Й» • 1 |Р!ДЯЯВВ5Я^ИВИГ^- "i jeuttoni b'J-* 3J Propeites Events | ;AJi^_________________Z_± I OnCHButtonlCli^J J OnCcrtMPwJ । , j ~bnpt<tfg>3n?c> ?. ~ Г,-" I OnDtapOw___J_____..... л f OnEndDock T 1 OnEndbrap__J'CL - I " ' Orfrtn Qr£rt OnkeyOown ["bnKeyPfets | OnfejUp " | OnMuuseOown ~ v I QnMouseMove OnMouseUp ' OnSlatOot*___________ -| JAI shown ’ Forml: TForml; |implementation ' {$R *.DFM} ।procedure TForml.ButtonlClick(Sender: TObj ect); , begin | Edi-tl.Tex-t: = ’ypal i! Приложение работает! 11 I end; I ^iend. I ’ 31- 41 ^Mocfifted jlnsert '. [\CodeXDiagiam/ Рис. 3.19. Процедура обработки события OnCIick private { Private declarations } public { Public declarations } end; var Forml: TForml; implementation { $R *. DFM} procedure TForml.ButtonlClick(Sender: TObject); begin Editl.Text:='Ура!!! Приложение работает!!!'; end; end. Для удаления процедуры-обработчика в дальнейшем достаточно удалить код, внесенный разработчиком самостоятельно. После этого при сохранении или компиляции модуля обработчик будет удален ав- томатически из файлов (dfm и pas) формы. При изменении в дальней- шем с помощью Инспектора объектов имени компонента-кнопки или имени процедуры происходит автоматически переименование процеду- ры-обработчика в файлах формы. 209
J-rFormi |D| X| - j/pa111 Приложение работает111 Рис. 3.20. Результат работы приложения Example Следующий этап — компиляция и запуск приложения. По команде меню Run/Run сначала выполняется компиляция проекта, а затем, в случае отсутствия ошибок компиляции, проект запускается на исполне- ние. На рис. 3.20 представлен результат работы приложения. 3.5. Средства управления параметрами проекта и среды разработки Отображение и установка текущих параметров среды выполняются в диалоговом окне Environment Options (см. рис. 3.10), которое вызывается посредством команды меню Tools/Environment Options. Параметры раз- биты на группы, каждая группа размещена на отдельной странице окна. Возможна настройка следующих групп параметров: • страница Preferences — параметры конфигурации рабочего про- странства среды; • страница Designer— параметры Дизайнера Форм проекта; • страница Environment Variables — просмотр и установка системных переменных, создание, редактирование и удаление пользователь- ских переопределений; • страница Object Inspector — спецификация свойств Инспектора Объектов; • страница Library — спецификация директорий размещения и пара- метров компиляции и компоновки библиотек проекта; • страница Palette — параметры настройки содержимого страниц Палитры компонентов; • страница Explorer — параметры настройки обозревателя проекта {Project Browser) и навигатора проекта {Code Explorer)-, • страница Type Library — параметры настройки редактора Библио- теки типов (набора информации о типах объектов); 210
• страница Internet — типы файлов и параметры скриптов для при- ложений WebSnap; • страница Delphi Direct — параметры доступа по сети к последним новостям Delphi на сайте borland.com. Менеджер проекта (Project Manager) В состав интегрированной среды разработки приложений включен Менеджер проектов, управляющий одновременно несколькими проектами. Окно Менеджера открывается по команде меню View/Project Manager. Менеджер проектов работает с группой проектов, в которой может быть один или несколько проектов. Новый проект может быть добавлен в группу с помощью контекстного меню, когда выделена строка ProjectGroupl. Менеджер проектов отображает иерархическую структуру группы проектов, самих проектов, а так же все формы и модули, входящие в каждый проект. Для работы с проектами в группе можно использовать либо панель инструментов, либо контекстное меню, набор команд кото- рого зависит от того, какой объект выделен в группе. На рис. 3.21 пока- зано окно Менеджера проектов с контекстным меню управления при- ложением, позволяющим: добавить модуль в проект; удалить модуль из проекта; сохранить проект; изменить параметры проекта; откомпилировать проект и построить приложение и т.п. j EdtProject.exe Files I Path CAEXAMPLE CAEXAMPLE CAEXAMPLE а CALCexe i calcllnit : H) calcUnit.... CAEXAMPLE ' i s Forml CAEXAMPLE H ip EditProject.exe CAEXAMPLE g] EditUnit CAEXAMPLE \ Э EditUnit.p...CAEXAMPLE | '-fi| Forml CAEXAMPLE ! g) EditUnit | Add New Project... । Add Existing Project... I Save Project Group Save Project Group As... View Project Group soiree >-’ —.' J .л| Status Bar Stay on Top Рис. 3.21. Окно Менеджера проектов 211
Менеджер Проекта позволяет объединять проекты, которые рабо- тают вместе, в одну проектную группу. Группы проектов сохраняются в файлах с расширением .bpg (Borland Project Group). Обозреватель проекта (Project Browser) С помощью Обозревателя проекта разработчик приложения может про- сматривать перечень модулей, классов, типов, свойств, методов, пе- ременных и процедур, объявленных в проекте или используемых им (рис. 3.22). Окно Обозревателя проекта открывается по команде меню View/Browser. Перед использованием Обозревателя проект должен быть сохранен и откомпилирован. Окно Обозревателя проекта разделено на две части. Слева в иерар- хическом виде отображаются доступные объекты, а справа для выбран- ного объекта более детально отображаются его характеристики. Для просмотра в левой части окна доступны иерархии трех типов объектов, расположенные на различных страницах: • Globals (Глобальные объявления); • Classes (Классы объектов); • Units (Модули). В правой части окна доступны для просмотра следующие характе- ристики: • Scope — список идентификаторов, объявленных в классе или мо- дуле, выделенном в левой части окна; • Inheritance — иерархическое дерево выделенного в левой части класса; • References — имена файлов и номера строк программного кода те- кущего проекта, где есть ссылка на выделенный в левой части объ- ект. Рис. 3.22. Окно Обозревателя проекта 212
Для управления параметрами отображения объектов в Обозревате- ле используется команда меню Tools/Environment Options/Explorer. С помощью Обозревателя проекта можно перемещаться по списку используемых проектом модулей и просматривать символы в разделах interface или implementation; можно просматривать глобальные объяв- ления и переходить к ссылкам на них в исходном коде. Репозиторий объектов Репозиторий (хранилище) объектов используется для создания но- вых элементов любого типа: форм, модулей данных, библиотек, компо- нентов и др. Объекты Репозитория рассматриваются в качестве шабло- нов при разработке приложений. Окно Репозитория открывается по команде меню File/New... Объ- екты-шаблоны сгруппированы. Каждая группа объектов размещена на отдельной странице: • New — базовые объекты; • ActiveX — объекты ActiveX и OLE; • Multitier — объекты многопоточного приложения; • Projects — проекты; • Form—формы; • Dialogs — диалоги; • Data Modules — модули данных; • Business — мастера форм. Страница с названием Example (рис.3.23) содержит форму текущего проекта. При добавлении или удалении формы проекта ее шаблон соответ- ственно добавляется или исключается из Репозитория объектов. Разработанный и отлаженный проект тоже может быть помещен в Репозиторий. Для этого необходимо инициировать команду меню Project/Add to Repository и заполнить появившееся диалоговое окно. Точно так же, как и шаблоны проектов, можно добавлять в Репозиторий и шаблоны форм. Для этого необходимо выбрать форму, которую пред- стоит добавить в Репозиторий, и инициировать команду контекстного меню Add to Repository. Добавить в текущий проект объект из Репозитория можно одним из трех способов: • Сору — в проект добавляется копия выделенного объекта Репози- тория. Все изменения в объекте являются локальными и не затра- гивают оригинал; • Inherit — в проект добавляется новый объект, который наследует свойства выделенного объекта Репозитория. При каждой новой компиляции проекта все изменения, внесенные в шаблон Репозито- рия, будут отражены в проекте; 213
Рис. 3.23. Окно Репозитория объектов • Use — объект Репозитория становится частью проекта. Изменения, вносимые в объект в рамках разработки проекта, на самом деле вносятся непосредственно в шаблон Репозитория. Справочная система В состав справочной системы Delphi входят: • стандартная система справки; • справочная помощь через Internet; • контекстно-зависимая справочная помощь. Окно стандартной системы справки (вызывается с помощью ко- манд меню Help/Delphi Help, Help/Delphi Tools, Help/Windows SDK) состоит из трех страниц — Содержание, Предметный указатель и По- иск (рис. 3.24). Информация на странице Содержание представлена иерархией те- матических разделов и подразделов. Страница Предметный указатель обеспечивает доступ к справоч- ной информации через словарь ключевых терминов. Страница Поиск предназначена для проведения полнотекстового поиска в справочном массиве и отображения всех разделов справочной системы, в которых встречается указанная фраза или слово. Команды меню, поддерживающие справочную помощь через Internet (например, Help/Borland Home Page, Help/Delphi Home Page), обеспечи- вают доступ на соответствующий сайт средствами браузера Internet 214
Содержание | Предметный указатель ] Поиск | Выберите раздел и нажмите кнопку "Показать" или выберите другую вкладку, например, "Предметный указатель". What'sNew ф Jpgrade and compatibility issues (jpl Programming with Delphi )elphi programming fundamentals Understanding the component library j?! Using the object model Й1 What is an object? f?l Examining a Delphi object i?l Changing the name of a component !?1 Inheriting data and code from an object 71 Scope and qualifiers 123 Private, protected, public, and published declarations i?l Using object variables '*Й г» • • i?l ГдраИпп insfantialinn and rlestrnuinn rJiierls Печать.. | Отмена Рис. 3.24. Окно справки Контекстно-зависимая справочная помощь вызывается путем нажа- тия клавиши <F1> функциональной клавиатуры. Содержимое справоч- ной информации зависит от текущего состояния среды и связано с акти- визированным в текущий момент объектом. 3.6. Pascal и визуальная среда разработки приложений Delphi Среда разработки приложений Delphi и ее библиотеки базируются на языке программирования Object Pascal — разработанном фирмой Borland объектно-ориентированном расширении языка Pascal, основан- ном на введении понятия класса и объекта (см. п. 1.7). Объявление класса. Чтобы определить в Object Pascal новый класс данных, имеющий свои поля данных и свои методы, используется сле- дующая синтаксическая конструкция: 215
type <Имя_класса> = class (<Имя_предка>) <Список_членов_класса> end; где <Имя_класса> — любой идентификатор; необязательное <Имя_предка> — идентификатор класса, свойства и методы которого наследует объявляемый класс; <Список_членов_класса> — перечень полей данных, свойств и методов класса. Рассмотрим в качестве примера класс данных TOperation, описы- вающий выполнение арифметической операции над двумя аргументами: TOperation = class Argl : real; Arg2 ; real; Oper : char; LastOper : boolean; Procedure InitOperation; Procedure SetArgl(ArgString:string); Procedure SetArg2(ArgString:string); Procedure SetOper(InpOper:char); Function OperationResult:string; end; В приведенном описании поля Argl и Arg2 типа real служат для хранения значений аргументов операции, поле Орег содержит символь- ное изображение операции ('+' — сложение, — вычитание, — ум- ножение и 7' — деление), а логический флаг LastOper принимает значе- ние true в момент задания операции. Для класса определены следующие методы: InitOperation — инициализация объекта класса; SetArgl — установка значения первого аргумента; SetArg2 — установка значения второго аргумента; SetOper — установка знака операции; OperationResult — выполнение операции. Методы класса должны быть описаны в модуле программы. Чтобы подчеркнуть, что они являются частью класса TOperation, их названия предваряются именем класса (в качестве разделителя выступает точка): Procedure TOperation.InitOperation; begin Argl:=0; Arg2:=0; Oper:=#0; // Обнуление первого аргумента // Обнуление второго аргумента // Очистка знака операции //(# — признак символьного кода) LastOper:=false end; // Флаг операции — false 216
Procedure TOperation.SetArgl(ArgString:string); var i:integer; begin val(ArgString,Argl,i) // Стандартная процедура // преобразования типов: строка // ArgString преобразуется в // вещественное число Argl. // Параметр процедуры i — код // ошибки преобразования // (в целях упрощения алгоритма // код ошибки не обрабатывается) end; Procedure TOperation.SetArg2(ArgString:string); var i:integer; begin val(ArgString,Arg2,i) end; Procedure TOperation.SetOper(InpOper:char); begin Oper:= InpOper; // Заполнение поля знака // операции end; Function TOperation.OperationResult:string; var R:real; begin R:=0; // Оператор case в зависимости от знака операции // обеспечивает выполнение арифметического действия. // Локальная переменная R служит для временного // хранения значения результата. case Oper of '+': R:= Argl+Arg2; R:= Argl-Arg2; R:= Argl*Arg2; '/': R:= Argl/Arg2; end; str(R:10:4,Result); // Стандартная процедура преобразования типов. // Для примера используется назначение формата // представления вещественного числа с фиксированной // точкой: 10 — общее количество цифр числа; // 4 — количество цифр дробной части. end; 217
После того, как класс описан, можно создать объект и использовать его, например, таким образом: var Ope_Result:TOperation; Begin // Создание объекта — общий для // всех классов метод Create: Ope_Result:=TOperation.Create; // Использование объекта: Ope_Result.SetArgl(1500'); // аналогично действию: // Ope_Result.Argl:= 500; Ope_Result.SetArg2('200'); // аналогично действию: // Ope_Result.Arg2:= 200; Ope_Result.SetOper('+'); // аналогично действию: //Ope_Result. Oper:= ' + '; // Вывод в виде сообщения // результата выполнения операции ShowMessage('Результат выполнения операции = ' + Ope_Result.OperationResult); // Уничтожение объекта — общий // для всех классов метод Free: Ope_Result:=TOperation.Free ; end; Object Pascal при объявлении переменной, имеющей тип класса, не создает автоматически объект, а использует так называемую ссылочную модель объекта. Основная идея этой модели в том, что каждая пере- менная типа class (как, например, Ope_Result) содержит не значение объекта, а лишь ссылку (указатель) на область памяти, в которой раз- мещается объект. Поэтому, когда описывается переменная типа class, то тем самым только резервируется место под ссылку на объект, а экземп- ляр объекта при этом должен создаваться вручную. Исключение состав- ляют экземпляры компонентов графического интерфейса, которые по- мещаются на форму — они автоматически создаются средой Delphi. Для создания экземпляра объекта необходимо вызвать его метод Create — конструктор класса TObject, свойства и методы которого на- следуют все другие классы как стандартные, так и создаваемые само- стоятельно. Если объект создан, то после использования он должен быть унич- тожен. Эта операция выполняется при помощи метода Free, который также является методом наследуемого класса TObject. 218
Обработка исключительных ситуаций В Object Pascal реализован механизм обработки исключительных си- туаций, описанный вп. 1.5 настоящего пособия. Опишем синтаксис ис- пользования ключевых слов try, finally и exept в защищенном блоке и в блоках завершения и исключений. Обработка завершения'. try «Операторы защищенного блока> finally «Операторы блока завершения> end; Рассмотрим пример процедуры, выполняющей длительные вычис- ления. Чтобы предупредить пользователя о возможной задержке в рабо- те программы, курсор манипулятора «мышь» обычно устанавливают в состояние «песочные часы», а после окончания вычислений возвращают в нормальное состояние: procedure TForml.BtnClick (Sender: TObject); var I, J : integer; begin Screen.Cursor := crHourglass; //Установка курсора в состояние // «песочные часы» J := 0; for I := 100000 downto 0 do // Блок вычислений J := J*J + J div I; Screen.Cursor := crDefault; // Возврат курсора в // нормальное состояние end; В приведенном алгоритме есть ошибка — когда переменная I дос- тигнет значения 0, произойдет программное прерывание, выполнение процедуры завершится и курсор не будет возвращен в нормальное со- стояние. Во избежание подобных ошибок необходимо использовать блок завершений: procedure TForml.BtnClick (Sender: TObject); var I, J : integer; begin Screen.Cursor := crHourglass; //Установка курсора в состояние //«песочные часы» 219
J := 0; try // Защищенный блок for I := 100000 downto 0 do J : = J*J + J div I; finally // Блок завершения Screen.Cursor := crDefault; // Возврат курсора в нормальное // состояние end end; При использовании обработчика завершения курсор будет приве- ден в нормальное состояние независимо от того, произошла исключи- тельная ситуация или нет, но обрабатываться исключительная ситуация при этом не будет. Рассмотрим далее синтаксис обработки исключений. Обработка исключений: try <Операторы защищенного блока> except on <Тип исключительной ситуации> do <Оператор> end; Реализуем в приведенном выше алгоритме вычислений обработчик исключений: procedure TForml.BtnClick (Sender: TObject); var I, J : integer; begin Screen.Cursor := crHourglass; //Установка курсора в состояние //«песочные часы» J := 0; try // Защищенный блок for I := 100000 downto 0 do J := J*J + J div I; except // Блок исключений on EDivByZero do ShowMessage('Исключение — деление на O'); //Обработка исключений end; Screen.Cursor := crDefault; // Возврат курсора в нормальное // состояние end; 220
В этом варианте процедуры перехватывается определенная в Delphi исключительная ситуация EDivByZero (деление на 0). Если в процессе вычислений произойдет деление на 0, то исключение будет обработано. Если же произойдет непредусмотренная блоком исключений ситуация (например, переполнение разрядной сетки для типа данных integer), то управление будет передано в блок исключений, но никаких действий произведено не будет, так как такой тип исключения не представлен в фильтре исключений. Оператор возврата курсора в нормальное состоя- ние будет выполнен в любом случае. 3.7. Разработка приложения «Калькулятор» Рассмотрим задачу разработки простого приложения — «Калькуля- тор», реализующего четыре арифметических действия над веществен- ными числами. При реализации арифметических операций будем поль- зоваться новым классом TOperation, объявленным в п. 3.6. На первом этапе проектирования приложения необходимо разрабо- тать форму, которая будет являться окном приложения в среде графиче- ского интерфейса ОС Windows. Пусть на нашей форме будут размещены следующие компоненты: • окно для ввода значений аргументов и вывода результата; • кнопки-цифры для формирования числа в окне при вводе значений аргументов; • кнопки-знаки арифметических операций; • кнопка-«точка» для формирования вещественного числа с дробной частью; • кнопка-«равно» для получения конечного результата; • кнопка удаления последнего введенного знака; • кнопка очистки окна ввода значений аргументов и вывода результата. Предлагаемый внешний вид формы представлен на рис. 3.25. При создании формы окна приложения определяется тип класса новой формы (в нашем примере это класс TForml), который наследует свойства и методы класса TForm, и автоматически размещается объект Forml. Исправим некоторые значения свойств в инспекторе объектов для формы Forml (рис. 3.26): изменим свойство Caption — текст заголовка окна формы, задав значение «Пример-калькулятор»; исключим из системного меню окна возможности изменять внеш- ний вид окна: для свойств biMinimize и biMaximize установим значе- ния False, запретив тем самым сворачивать окно до пиктограммы и раз- ворачивать его на весь экран. 221
Рис. 3.25. Окно программы «Калькулятор» Окно для ввода значений аргументов и вывода результата пред- ставляет собой компонент класса ТМешо (страница «Стандартные» Палитры компонентов). Разместим компонент на области формы и из- меним его свойства (рис. 3.27): • установим в свойстве Name (имя объекта) значение ArgMemo; • зададим свойству Align (размещение на форме) значение alTop, тем самым разместив компонент в верхней части формы и сделав его равным по ширине горизонтальному размеру формы; [Object Inspector j Forml I Form' Properties | Events | j Action * j __ AchveConhol ‘ ____________ " Afign : alNone AlphaBlend " False' ~ I AfchaBfend^abe 255 ~ BAndns [akLeflakTop] — AutnSrioJ I rue AutoSize ,Fal$e_ _ _ | ™ BDMocfe .........MLeftfo^hT^ EBorderlcons [biSfsIemMenu] brSystenWenu hue ЫМгжтюе ___ False _ _ „ _ ____ __ ЬМайиге _ False I “biHelp” “ __ ‘iFabe ’ j BoidecS^te ’ testable..... _ ...... | ...i Caption |||.1!!!и.8!М„ЯЯВЯЯ | OentHeight _,246 _ _rj; 2 hidden___________________________________ Рис. 3.26. Свойства формы в окне Инспектора объектов 222
Object Inspector jArgMemo —BH*' TMemo Properties | Events ; Align Alignment E] Anchors El BevelEdges Bevellnner : BevelKind BevelOuter i BiDiMode BorderStyle J Color" ГЕ] Constraints Cursor DragCursor DragKmd DragMode i Enabled EJFont Height ; HelpContext । HelpKeyword HelpType HideS election * Hint ImeMode ImeName : Left ’ Lines '' M axLength I Name alTop laRighUustily [akLeft.akTop.akRight] [beLefLbeT op,beRight,beBo bvRaised bkNone bvLowered bdLeftT oRight bsS ingle □ clWindow (TSizeConstrantsl crD efault crDrag IdkDtag jdmManual ЖИМ» i(TFont) htContext................ True imDontCare |(TStrings) _ . .............- :ИЯВВ!Я1 ; 2 hidden Рис. 3.27. Свойства окна ввода в Инспекторе объектов • установим в свойстве Alignment значение taRightJustify, т.е. зада- дим принцип выравнивания введенного в окно значения по правой границе окна; • установим в свойстве Readonly значение True, запретив ввод зна- чений в окно с клавиатуры. Следующий шаг разработки формы — размещение кнопок. Все кноп- ки представляются объектами, принадлежащими классу TSpeedButton (страница «Дополнительные» — «Additional» Палитры компонентов). Для каждого объекта кнопки установим следующие свойства: 223
• свойство Name (имя объекта): SB_0, SB_9 — для кнопок- цифр; SB_point — для кнопки-«точки»; SB_plus, SB min, SB_dev, SB mul, SB_eq — для знаков операций и «равно»; SBdel — для кнопки удаления знака; SB_clear — для кнопки очистки значения компоненты ArgMemo; • свойство Caption — текст на кнопке — символ цифры, точки, знака операции, ‘С’ и ‘Del’. Каждый из объектов-кнопок будет служить источником событий, обработка которых и будет поддерживать процесс вычисления с помо- щью разрабатываемого калькулятора: • при нажатии на кнопку-цифру необходимо добавить к формируе- мому числу справа цифру, указанную на кнопке; • при нажатии на кнопку-«точку» необходимо добавить к формируе- мому числу справа точку, предварительно убедившись, что такого знака еще нет в формируемом числе; • при нажатии на кнопку-знак операции необходимо выполнить пре- дыдущую операцию (в случае вычисления выражения типа А + В - С), отобразить ее результат и установить знак очередной операции; • при нажатии на кнопку-«равно» необходимо вычислить и отобра- зить результат; • при нажатии на кнопку удаления знака необходимо удалить из формируемого числа последний знак; • при нажатии на кнопку очистки окна ввода необходимо очистить текстовое значение объекта ArgMemo. Приведем описание процедур обработки событий. Событие OnClick для кнопки-цифры вызывает выполнение проце- дуры TForml. SBDigitClick: Procedure TForml.SB_DigitClick(Sender: TObject); Begin // Если последним был введен знак операции, то область // ArgMemo предварительно очищается для ввода нового // значения и происходит сброс флага ввода операции: if Ope_Result.LastOper then begin ArgMemo.Text:=1'; Ope_Result.LastOper := false end; // Добавление цифры к числу справа ArgMemo.Text:=ArgMemo.Text+(Sender as TSpeedButton).Caption; end; Процедура обработки события для кнопки-«точки» (TForml.SB PointClick) отличается от процедуры SB DigitClick тем, 224
что, во-первых, обеспечивает ввод двух знаков ('О' и в случае, если начинается ввод нового числа, и, во-вторых, запрещает ввод второй точки в изображение числа с десятичной дробью: procedure TForml.SB_PointClick(Sender: TObject); begin if Ope_Result.LastOper then begin ArgMemo.Text:=''; Ope_Result.LastOper := false end; if ArgMemo.Text = '' then ArgMemo.Text:=ArgMemo.Text+'0.' else if posArgMemo.text)= 0 then ArgMemo.Text:=ArgMemo.Text+• . •; end; Для обработки события OnClick кнопок-знаков операций предло- жим следующий алгоритм. 1. Полю Arg2 объекта OpeResult присвоим значение, введенное в окне ArgMemo. 2. Проверим содержимое поля Орег. Если текущая операция — первая в выражении (т.е. Орег = #0), то перенесем содержимое Arg2 в Argl. В противном случае выполним операцию, знак которой находится в поле Орег, и результат занесем в Argl. Таким образом, после нажатия на кнопку-знак операции в объекте Ope Result устанавливается значе- ние первого аргумента. 3. Занесем новое значение в поле Орег и установим флаг ввода операции. Описанный алгоритм реализуется процедурой TForml.SB Oper- Click: procedure TForml.SB_OperClick(Sender: TObject); var s:shortstring; begin with Ope_Result do begin SetArg2(ArgMemo.Text); if Орег <> #0 then s: := OperationResult // выполнение операции и запись // результата в локальную // переменную s else s:=ArgMemo.Text; SetArgl(s); // Занесение значения первого // аргумента SetArg2('0'); // Обнуление второго аргумента ArgMemo.Text:= s; // Вывод значения первого // аргумента 225
s:= (Sender as TSpeedButton).Caption; SetOper(s[1]); // Установка знака операции LastOper:=true; //и флага ввода операции end; end; Процедура обработки события OnClick для знака «равно» разреша- ет выполнение операции только в случае, если поле Орег не пусто, ини- циализирует объект Ope Result и устанавливает флаг ввода операции: procedure TForml.SB_EqClick(Sender: TObject); begin with Ope_Result do begin if Орег <> #0 then begin SetArg2(ArgMemo.Text); ArgMemo.Text:=OperationResult; SetArgl(ArgMemo.Text); end; InitOperation; Las tOper:=true; end; end; Для поддержки событий нажатия на кнопки ‘С’ и ‘Del’ опишем процедуры TForml. SBClearClick и TForml.SBdelClick: procedure TForml.SB_delClick(Sender: TObject); var s:string; begin // Локальная переменная s используется для временного // хранения текстового значения окна ArgMemo s:= ArgMemo.text; SetLength (s, dec (Length (s) ) ; // Установка нового значения // длины строки (меньше на 1) ArgMemo.text:=s; end; procedure TForml.SB_ClearClick(Sender: TObject); begin ArgMemo.text; // Очистка текстового значения //окна ArgMemo Ope_Result.InitOperation; // Инициализация объекта II Ope_Result end; Ha рис. 3.28 представлено окно Инспектора объектов для компо- нента-кнопки «5». 226
Заметим, что алгоритмы обработки событий строятся на использо- вании объекта Ope Result спроектированного ранее класса TOperation, поэтому необходимо обеспечить размещение объекта в памяти и осво- бождение памяти при завершении работы приложения. Для этих целей удобно использовать события формы OnCreate (создание формы) и OnClose (закрытие формы) (см. рис. 3.29). Опишем обработчики событий следующим образом: Object Inspector [object Inspector JSB.J Aclon AJowAWp EAnchwS BBMode □Consttairts [akLeltakTopl txLeltToRqht (TSizeConshairts) ictDefaiA Action | OnCbck _ OnDbCfck......... ; OnMouseDown i OnMouseMove , OrMouseUp 1 PopupMenu ич fiirnUdHSI Enabled ' Flat IB Font GWi Grooplndex Height^ H eb Context HetpKejwotd HelpType Hint fTFonl) (None) 0 •HContext _ WG^phLert Al shown /gj Al shown Рис. 3.28. Свойства и события кнопки «5» в Инспекторе объектов I Ob je с t Inspector iFonnl ТгоптД J Properties Everts] '""Action "i——————q AciweContrd 1 — —— —- —• —• - — ObjectMendtem _ __ _ OnAcbvate______ ____ ______ OnCanResize _ __ ______ _ OnCbck 1__________ * j OrOoseOuery J_ ____ ОпСопйгапвЯе» _ * ___ _____ { OnContextPtw !" _____ j OnCterte" ' ' FoteCteale ] OnObCEck ‘i " .d 'Al shown Рис. 3.29. События формы в Инспекторе объектов 7 3 О 227
procedure TForml.FormCreate(Sender: TObj ect); begin Ope_Result:=TOperation.Create; // Создание объекта Ope_Result Ope_Result.InitOperation; // Инициализация объекта end; procedure TForml.FormClose(Sender: TObject; var Action: TCloseAction); begin Ope_Result.Free; // Уничтожение объекта // Ope_Result end; Ha этом разработку учебного приложения «Калькулятор» можно считать завершенной. Средой Delphi сгенерирован текст основной про- граммы и построен модуль calcUnit, содержащий описание класса TOperation и интерфейсной формы Forml: program calc; uses Forms, calcUnit in 'calcUnit.pas' {Forml}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TForml, Forml); Application.Run; end. unit calcUnit; interface uses Windows, Classes, Controls, Forms, Buttons, StdCtrls, ExtCtrls; type // Описание класса TForml TForml = class(TForm) SB_1: TSpeedButton; SB_2: TSpeedButton; SB_3: TSpeedButton; SB_4: TSpeedButton; 228
SB_5: TSpeedButton; SB_6: TSpeedButton; SB_7: TSpeedButton; SB_8: TSpeedButton; SB_9: TSpeedButton; SB_0: TSpeedButton; ArgMemo: TMemo; SB_Point: TSpeedButton; SB_dev: TSpeedButton; SB_mul: TSpeedButton; SB_min: TSpeedButton; SB_plus: TSpeedButton; SB_del: TSpeedButton; SB_Eq: TSpeedButton; SB_Clear: TSpeedButton; procedure SB_DigitClick(Sender: TObject); procedure SB_PointClick(Sender: TObject); procedure SB_OperClick(Sender: TObject); procedure SB_EqClick(Sender: TObject); procedure SB_delClick(Sender: TObject); procedure SB_ClearClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } public { Public declarations } end; // Описание класса TOperation TOperation = class Argl:Real; Arg2:real; Oper:Char; Las toper:boolean; Procedure InitOperation; Procedure SetArgl(ArgString:string); Procedure SetArg2(ArgString:string); Procedure SetOper(OperString:char); function OperationResult:string; end; var Forml: TForml ; Ope_Result:TOperation; implementation {$R *.DFM) // Методы класса TOperation 229
Procedure TOperation.InitOperation; begin Argl:=0; Arg2:=0; Oper:=#0; LastOper:=false end; Procedure TOperation.SetArgl(ArgString:string); var i:integer; begin val(ArgString,Argl,i) end; Procedure TOperation.SetArg2(ArgString:string); var i:integer; begin val(ArgString,Arg2, i) end; Procedure TOperation.SetOper(OperString:char); begin Oper:=OperS tring ; end; function TOperation.OperationResult:string; var R:real; begin R:=0; case Oper of '+': R:= Argl+Arg2; R:= Argl-Arg2; R:= Argl*Arg2; '/': R:= Argl/Arg2; end; str(R:10:4,Result) ; end; // Обработчики событий класса TForml procedure TForml.SB_DigitClick(Sender: TObject); begin if Ope_Result.LastOper then ArgMemo.Text; ArgMemo.Text:=ArgMemo.Text+(Sender as TSpeedButton).Caption; Ope_Result.LastOper := false; end; procedure TForml.SB_PointClick(Sender: TObject); begin if Ope_Result.LastOper then 230
begin ArgMemo.Text; Ope_Result.LastOper := false end; if ArgMemo.Text = '' then ArgMemo.Text:=ArgMemo.Text+'0 . ' else if pos.ArgMemo.text)= 0 then ArgMemo.Text:=ArgMemo.Text+' .' ; end; procedure TForml.SB_OperClick(Sender: TObject); var s:shortstring; begin with Ope_Result do begin SetArg2(ArgMemo.Text); if Орег <> #0 then s:= OperationResult else s:=ArgMemo.Text ; SetArgl(s); ArgMemo.Text:=s; s:= (Sender as TSpeedButton).Caption; SetOper(s[1]) ; LastOper:=true; end; end; procedure TForml.SB_EqClick(Sender: TObject); begin with Ope_Result do begin if Орег <> #0 then begin SetArg2(ArgMemo.Text); ArgMemo.Text:=OperationResult; SetArgl(ArgMemo.Text); end; InitOperation; LastOper:=true; end; end; procedure TForml.SB_delClick(Sender: TObject); var s:string; begin s:= ArgMemo.text; SetLength(s,Length(s)-1) ; ArgMemo.text:=s ; end; procedure TForml.SB_ClearClick(Sender: TObject); begin ArgMemo.text:='1; Ope_Result.InitOperation; end; 231
procedure TForml.FormCreate(Sender: TObject); begin Ope_Result:=TOperation.Create; Ope_Result.InitOperation; end; procedure TForml.FormClose(Sender: TObject; var Action: TCloseAction); begin Ope_Result.Free; end; end. И 3.8. Разработка приложения «Редактор текстов» На примере разработки приложения «Редактор текстов» рассмот- рим использование на форме невизуальных и неоконных компонентов, а также порядок проектирования главного меню, всплывающего меню и панели инструментов. Разрабатываемое приложение должно обеспечить: • отображение существующего текстового файла в окне редактиро- вания или создание нового файла; • выполнение простейших операций по редактированию: вставка- удаление символов, копирование и перемещение фрагментов текста с использованием Системного буфера обмена (ClipBoard); • сохранение результатов редактирования в том же или в новом файле. Рассмотрим последовательно этапы разработки приложения. Первый этап — проектирование интерфейса. Главное интерфейсное окно приложения будет содержать следующие визуальные компоненты: • главное меню, расположенное в верхней части окна; • панель инструментов, расположенную сразу под строкой Главного меню; • область для отображения и редактирования текстов, которая должна занимать всю оставшуюся часть главного интерфейсного окна; • «всплывающее» меню, которое должно появляться при нажатии на правую клавишу манипулятора «мышь» в области редактирования. Главное меню. Для организации выполнения перечисленных дей- ствий построим Главное меню приложения. Разобьем функции приложения на две группы — «Файл» и «Редак- тировать». Эти две группы и представят собой опции Главного меню. В группу «Файл» войдут функции «Создать», «Открыть», «Сохра- нить», «Сохранить как...» и «Выход». Опция меню «Файл» должна объ- 232
единять соответствующие пункты меню, представляющие собой коман- ды, вызывающие выполнение перечисленных функций. В группу «Редактировать» — функции «Вырезать», «Копировать» и «Вставить» (функции работы с фрагментами текста через посредство Системного буфера обмена). Дизайнер меню. Компонент Главного меню располагается на страни- це «Стандартные» («Standard») Палитры компонентов. Установим компо- нент на форме и вызовем Дизайнер меню. Вызвать Дизайнер меню можно либо через свойство Items компоненты Главного меню, либо с помощью контекстного меню, которое раскрывается по нажатию на правую клавишу манипулятора «мышь», когда компонент Главного меню выделен на форме. Каждый пункт меню вводится с помощью Дизайнера, а затем ото- бражается в строке меню на форме. При вводе нового пункта на самом деле создается новый компонент и добавляется в список компонентов Инспектора объектов (хотя визуально на форму ничего не добавляется). В свойствах компонента обязательно заполняются поля Caption — на- звание пункта, Name — имя компонента и, если необходимо, поля Shortcut — «горячая клавиша» для вызова функции с помощью кла- виатуры и Bitmap — для добавления пиктограммы к заголовку пункта. На рис. 3.30 показаны свойства компонента пункта меню «Сохранить», доступ к которым открыт через Дизайнер меню. Для удобства восприятия команды меню визуально разбиваются на группы с помощью добавления в меню специальных пунктов- разделителей. Чтобы ввести в меню такой пункт, достаточно в свойстве Caption установить знак «-» (дефис), как показано на рис. 3.31. Ниже приведен фрагмент текстового описания формы для компо- нента пункта меню: object MFile_Save: TMenuItem Bitmap.Data = { F6000000424DF600000000000000760000002800000010000000100000000100 04000000000080000000C40E0000C40E00001000000000000000000000000000 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333330070 7700333333330080880033333333008888003333333300000000000000000000 00000FFFFFFF0FFFFFF00FFFFFFF0FFFFFF00F00F0080CCC9CC00FFFFFFFF039 9933OFOOOOFOFO999993OFFFFFFFF9999999OFOOFOOOOO399933OFFFFOFFO339 99330F08F0F0337999330FFFF003999993330000003333333333} Caption = 'Сохранить' Shortcut = 16467 OnClick = MFile_SaveClick end Панель инструментов. Следующий этап проектирования формы — размещение Панели инструментов. Панель инструментов предназначена 233
File Ed* Search View Project Run Component Database Tods Window Help ; [ШёО •*! ; ft ; © & Г£=г Й ' I 5tant*afC* [Additional | Win321 Svsteml Data Access! Data Controls I dbExoressl D_1 f<5 JHR > - il a 2-iH £3 IF % A PT uU К » 10 H “'O » (MFie.Save I : Properbet | Events | ! Acbon ’ AutoCheck r AutoHotkeys a 3 False . _________imaParer* | AutabneReduetonimaParent ? ; . Bitmap _ (TB*map) :mbNone Сохранить False False True 0 0 Файл редактировать ' Создать Открыть ‘ Bredc ^Caption j Checked i Default j Enabled | Grouplndex I HelpContext Hr* * Imageindex i Name ! Radeltem | Shortcut ! Al shown iMRe^Save?: False iCtikS * Coxparert» > MFie.Blank Properties | Events | Action AutoCheck _ False AutoHolkeys _z _ tmaParer* ; _ AutoUneReducbon maParent Bitmap jfNone) Bteak mbNone Caption Checked _ Del«dt Enabled Grouplndex^ HelpContext Hr* _______ Imageindex _ Name RadWtem_ _ Shortcut Al shown I False False Tiue О “ о" MFfeJBIank False iM ~j3 Сохранить как... Закрыть Выход Рис. 3.30. Дизайнер меню Standard | Addtonal | Wn321 Svstem] Data Access] Data Сепией I dbExtxessl D < I Файл Редактировать [ Создать Открыть Сохранить Сохрагетгь как... Закрыть Ctrt+5 Выход Рис. 3.31. Ввод в меню пункта-разделителя 234
для дублирования наиболее часто использующихся команд меню гра- фическими кнопками, что обеспечивает более быстрый альтернативный способ вызова связанных с командами меню функций. Начиная с версии 3, в Delphi появился специальный компонент ToolBar (страница «Win32» Палитры компонентов), предоставляющий готовую панель инструментов со своими собственными кнопками. Ком- понент ToolBar инкапсулирует соответствующий стандартный элемент управления Windows. Разместим компонент на форме и зададим значения некоторым свойствам (помимо свойства Name). Значение alTop свойства Align определяет положение панели инструментов на форме — в верхней час- ти, сразу под строкой Главного меню. Установка свойства Flat в true задает особый стиль изображения кнопок панели — без прорисовки контура (контур появляется только у кнопки, на которую указывает курсор). Сначала размещенная на форме панель инструментов пуста. Добав- ление кнопок осуществляется с помощью всплывающего контекстного меню компонента: для добавления кнопки используется команда New Button, а для добавления разделителя — команда New Separator. При- мер формы с компонентом ToolBar во время проектирования показан на рис. 3.32. Компонент ToolBar содержит объекты класса TToolButton. Это внутренние объекты (аналогично объектам класса TMenuItem, которые являются внутренними для объекта MainMenu). Далее для каждой кнопки задается рисунок — пиктограмма, соот- ветствующая функциональному назначению кнопки. Определение ри- сунков для кнопок панели инструментов осуществляется с помощью не визуального компонента ImageList. Этот компонент не имеет своего места на форме и предназначен для создания коллекции рисунков, вы- бор которых происходит по индексу — номеру рисунка в коллекции. Имя компонента заносится в свойство Images Панели инструмен- тов и, таким образом, обеспечивается связь каждой отдельной кнопки панели с соответствующей картинкой. Коллекция рисунков ImageList. Для создания коллекции рисунков на форму добавляется компонент ImageList (страница «Win32» Палит- ры компонентов). Контекстное меню компонента позволяет вызвать Редактор ImageList, с помощью которого проводятся действия по до- бавлению и удалению рисунков в коллекции (см. рис. 3.33). Каждый добавленный рисунок получает в коллекции свой ин- декс и может быть в дальнейшем связан с некоторой кнопкой панели инструментов путем занесения значения индекса в свойство Imageindex кнопки. 235
? l.'22::2^222'' 2:2 2. ...л . I Не Edt Search Mew Prelect Run Correonent Database Tools Window Heb> i I |SJSES 'JJS 3 e-v j LiJ ’ Q & <ia : ‘ Ф 1 S'****'* I Addbonall Wm32| Svstem! Data Accessl Data Contrab | dbE«xess| DateJLLt- [ Г® 9 =Fi IbI > • II. г VI i k H В" А [ДГ SI -»«J |g 8 а» ч |TooBar * Г Properties | Events | □ "3 аГГор ЩеМкТорл* Fabe S 10 ". Ц 122 ________________' 25 Д | Caption iTooBas “ ' :. 2 Color 1ig g: f j Q dBtnFace S Сопйгаг>Ь___^ _ ; (TSeeConsfraint Cursor __ «Default Custoneabte Fake Dtsabledlmages DockSie , False DragCtrsor crDrag Dra^jnd idkDrag DragMode :dmManuai QEdgeGorders 'ileblwl ~ " D 1 hidden : 22Ш21212Ш222Ш222222;2^ AEgn S Anchors AutoSae _ _ BotdeAVidth_J < ButtoinHeqjht ButtorWidth Файл Редактировать в]в;ч »>.;« Й* Qt| New Button New Separator Edit ► Control ► 3 Posidon s’ » Ftp Chicken ► Tab Order... !§ CreatiOQOrder... Add to Repository... View as Text pr] TextDFM ARgnto grid ABgn... Sze... Scalft... Рис. 3.32. Проектирование компоненты ToolBar as-a-s© &£> Standard | Addkionai I Wm321 Svstem | Data Access I Data Controk | dbEeres* | DataSnauSLi. flmageUst ’ ’ ] Properties | Events) j Afcrfjji 4 ( BkCokx " iHcKone i BlendCofa J ’ OdNone ‘ 5s DrawmgStyte <• sdsNocmal •' _ 7 ~ Jl6~ J J “ " ImageT^pe iMmage Masked True Name ImageUst Shareimages _ False T«9 _ _ 0 WBh _ _ . . . 16 Al shown x-v Options I OK Cmcd Г Рис. 3.33. Редактор ImageList — создание коллекции рисунков 236
Рис. 3.34. «Всплывающая» подсказка На примере кнопки Панели инструментов продемонстрируем использование пары свойств ShowHint и Hint для формирования «всплывающих» подсказок. Установка свойства ShowHint в True обеспечивает включение аппарата «всплывающей» подсказки для компонента, т.е. появление в специфическом «всплывающем» окне текста, заданного в свойстве Hint, при задержке курсора в области компонента (рис. 3.34). Ниже приводится фрагмент текстового описания формы для панели инструментов: object ToolBar: TToolBar Left = 0 //Параметры размещения //компонента на форме Top = 0 Width = 493 Height = 25 Buttonwidth = 25 // Ширина кнопок (все кнопки // имеют одинаковые размеры) Caption = •ToolBar' Flat = True Images = ImageList // Имя присоединенной коллекции // рисунков TabOrder = О object TBFile_New: TToolButton Left = О Top = О Hint = 'Создать файл' 237
// Текст всплывающей подсказки Caption = 'Создать' Imageindex =4 // Номер рисунка, назначенного // для кнопки, в коллекции // рисунков ParentShowHint = False ShowHint = True// Показывать всплывающую // подсказку OnClick = MFile_NewClick // Имя процедуры обработки // события нажатия на кнопку end object TBFile_Open: TToolButton Left = 25 Top = 0 Hint = 'Открыть файл' Caption = 'Открыть' Imageindex = 5 ParentShowHint = False ShowHint = True OnClick = MFile_OpenClick end object TBFile_Save: TToolButton Left = 50 Top = 0 Hint = 'Сохранить' Caption = 'Сохранить' Imageindex = 6 ParentShowHint = False ShowHint = True OnClick = MFile_SaveClick end object TBFile_Close: TToolButton Left = 75 Top = 0 Hint = 'Закрыть файл' Caption = 'Закрыть' Imageindex = 3 ParentShowHint = False ShowHint = True end object ToolButton4: TToolButton Left = 100 Top = 0 Width = 8 Caption = 'ToolButton4' ImageIndex = 8 Style = tbsSeparator end object TBEdit_Cut: TToolButton Left = 108 Top = 0 Hint = 'Вырезать' Caption = 'Вырезать' 238
Imageindex = О ParentShowHint = False ShowHint = True OnClick = MEdit_CutClick end object TBEdit_Copy: TToolButton Left = 133 Top = 0 Hint = 'Копировать' Caption = 'Копировать' Imageindex = 1 ParentShowHint = False ShowHint = True OnClick = MEdit_CopyClick end object TBEdit_Paste: TToolButton Left = 158 Top = 0 Hint = 'Вставить' Caption = 'Вставить' Imageindex = 2 ParentShowHint = False ShowHint = True OnClick = MEdit_PasteClick end end Область для отображения и редактирования текстов. Для ото- бражения и редактирования текстовых файлов разместим на форме уже известный из предыдущего примера компонент Мето класса ТМето. Установим значение Client в свойстве Align компонента, обеспечив тем самым заполнение компонентом всей незанятой части главного интер- фейсного окна. Компонент Мето предназначен для ввода разделенных и не разде- ленных на строки текстов. При установке свойства Wordwrap в True происходит дополнительное разбиение текстов на строки по ширине компонента. Свойство ScrolIBars позволяет установить на области компонента стандартные полосы «прокрутки» текстов: ssVertical — только верти- кальная полоса, ccHorizontal — только горизонтальная полоса, ssBoth — обе полосы «прокрутки» (в рассматриваемом примере установлена вер- тикальная полоса «прокрутки»). Компонент автоматически поддерживает стандартные функции ре- дактирования — ввод, замену и удаление отдельных символов, а также выделение фрагментов текста. Выделение фрагмента происходит либо с помощью «буксировки» манипулятора «мышь», либо посредством кла- виатуры (с помощью клавиш перемещения курсора — «стрелка впра- во», «стрелка влево» и т.п. — при нажатой клавише <Shift>). 239
Приведем фрагмент текстового описания формы для компонента Мето (имя компонента — EditMemo): object EditMemo: TMemo Left = 0 Top = 25 Width = 493 Height =295 Align = alClient ScrollBars = ssVertical TabOrder = 1 end Контекстное меню. Кроме Главного меню обычно еще исполь- зуется похожий на него компонент PopupMenu (страница «Стан- дартные» Палитры компонентов). Такое меню отображается при на- жатии на правую клавишу манипулятора в области компонента, свойство PopupMenu которого указывает на данное меню. Это стан- дартный способ использования контекстного (локального, всплы- вающего) меню. Разместим на форме неоконный компонент PopupMenu и добавим в меню опции из группы команд «Редактирование» Главного меню. По- рядок проектирования контекстного меню полностью совпадает с по- рядком проектирования Главного меню и тоже предполагает использо- вание Дизайнера меню. Проектирование пунктов контекстного меню читателю предлагает- ся провести самостоятельно. Для привязки контекстного меню к компоненту Мето занесем в свойство PopupMenu компонента имя контекстного меню. Использование стандартных диалогов. Для организации рабо- ты с файлами (открытия файлов для редактирования и сохранения результатов) используются компоненты стандартных диалогов Windows, реализованные в Delphi. Разместим на форме приложения неоконные компоненты OpenDialog и SaveDialog (страница «Dialogs» — «Диалоги» — Палитры компонентов) и рассмотрим возможности их использования на примере компонента OpenDialog (SaveDialog работает похожим образом). Использование диалоговых компонентов не обеспечивает автома- тического открытия или сохранения файлов, но позволяет получить для дальнейшей обработки в программе имя выбранного пользователем файла (рис. 3.35). Рассмотрим некоторые свойства компонентов, позволяющие управ- лять видом и характером действия диалогового окна. 240
в в ей «»। at Рис. 3.35. Диалоговое окно «Открыть файл» Свойство FileName назначает имя файла по умолчанию (имя, кото- рое будет отображаться в окне «Имя файла» при открытии диалога), а затем (после закрытия диалогового окна) содержит имя выбранного пользователем файла. Свойство DefaultExt позволяет указать расширение имени файла, которое будет добавляться по умолчанию в том случае, если в окне «Имя файла» имя будет указано без расширения (в нашем примере ис- пользуется значение txt). Свойство Filter задает список файловых масок (файловых фильтров), из которого пользователь может выбирать для отображения в диалоговом окне только нужные типы файлов. Файловый фильтр представляет собой строку, состоящую из одной или более пар значений. Пара значений — это описание фильтра и сам фильтр (один или несколько, разделенных симво- лом «;»). Каждая пара значений задает один фильтр. Пары значений и зна- чения внутри одной пары разделяются символом«|» (вертикальная черта). Например, следующая строка задает два фильтра для диалога: MyOpenDialog.Filter := 'Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*' На рис. 3.36 показано окно для заполнения свойства Filter при про- ектировании формы. Второй этап разработки приложения — разработка процедур об- работки событий. Рассмотрим последовательно процедуры-обработчи- ки для пунктов Главного меню (обработчики события OnClick для каж- дого компонента класса TMenuItem). 241
। Ffe Edfc Search-View Project Run Component Database Tods Window Hcb'-ullSBISt t: £ © 38 £ Standard | Additional j Win32| Svstemj Data Access] Data Controls] dbExoress] DataSnaui-Li. a. t-B » i k 38“ % a isr si ju г» ® ". S ' > i________________1 ta lopenDialoc '.....................*| Properties | Events | ' DdadtExt _ tat ;i FieName | Fter Fierlndex | HelpContext f InfeaDr ’ Name .Ш Options . : SOpbonsEx I Tag Tide ~O В 9MS 1 >1 hdden :ce фаяяыг. 1 0 OpenDiaiog : [ofl-lfdeReadOnly.d fl 0 Открыть Файл Рис. 3.36. Свойства компоненты OpenDialog (заполнение свойства Filter) Пункт Главного меню Файл / Открыть предназначен для органи- зации диалога выбора файла на редактирование. Метод OpenDialog.Execute открывает диалоговое окно, а после нажатия на кнопку «Открыть» в диалоговом окне возвращает значение True и за- полняет свойство OpenDialog.FileName, помещая в него имя выбранно- го в окне диалога файла. Метод LoadFromFile объекта TStrings, представляющего свойство Lines компонента EditMemo, позволяет одновременно организовать от- крытие и чтение файла в область редактирования. Если процесс загрузки файла закончился удачно, изменяется заголовок окна приложения (в него помещается имя открытого файла) и заполняется глобальная переменная CurrentFileName, служащая для хранения имени текущего файла. В слу- чае возникновения ошибки при загрузке файла управление передается в блок обработки исключений и выдается диалоговое окно сообщения. Приведем текст процедуры: procedure TForml.MFile_OpenClick(Sender: TObject); begin if OpenDialog.Execute then try EditMemo.Lines.LoadFromFile(OpenDialog.FileName); Forml.Caption := Forml.Caption + ' — ' + OpenDialog.FileName; CurrentFileName := OpenDialog.FileName; 242
except on EFOpenError do MessageDlg('Ошибка при открытии файла’, mtError, [mbOk], 0) ; end; end; Пункт Главного меню Файл/Сохранить как... предназначен для сохранения результатов редактирования в файле, имя которому назначает пользователь. При организации процесса сохранения ре- зультатов необходимо учесть возможность существования файла с именем, указанным в окне диалога сохранения. В предлагаемой про- цедуре-обработчике используется стандартная функция FileExists для проверки существования файла и организуется запрос-диалог для принятия решения. Стандартная функция MessageDlg открывает диалоговое окно с тремя управляющими кнопками — [Yes], [No] и [Cancel] и возвращает константу — признак выполненного действия (нажатой кнопки). В зависимости от принятого пользователем решения выполняет- ся одна из трех последовательностей действий: сохранение результа- тов редактирования с помощью применения метода SaveToFile, симметричного методу LoadFromFile; повторяется диалог назначения имени файла или происходит выход из процедуры-обработчика: procedure TForml.MFile_SaveAsClick(Sender: TObject); label 1; var btn : word; fsave : boolean; begin if EditMemo.Text <> '' then begin 1: if SaveDialog.Execute then if FileExists(SaveDialog.FileName) then begin btn := MessageDlg(1 Файл существует. Перезаписать?', mtWarning, [mbYes, mbNo, mbCancel], 0); if btn = mrNo then goto 1; if btn = mrCancel then fsave := false; if btn = mrYes then fsave := true; end else fsave := true; if fsave then try EditMemo.Lines.SaveToFile(SaveDialog.FileName); CurrentFileName := SaveDialog.FileName; 243
Forml.Caption := ProgCaption + ' — ' + SaveDialog.FileName; except on ElnOutError do MessageDlg('Ошибка записи в файл', mtError, [mbOk], 0) ; end; end; Посредством пункта Главного меню Файл/Сохранить результаты редактирования сохраняются в текущем файле, если имя текущего фай- ла не пусто, или происходит вызов процедуры-обработчика для пункта меню Файл/Сохраннть как...: procedure TForml.MFile_SaveClick(Sender: TObject); label 1; var btn : word; fsave : boolean; begin if EditMemo.Text <> '' then if CurrentFileName = '' then MFile_SaveAs.Click else try EditMemo.Lines.SaveToFile(CurrentFileName); except on ElnOutError do MessageDlg('Ошибка записи в файл', mtError, [mbOk], 0); end; end; Обработчики событий пунктов Главного меню из группы «Редак- тировать» построены на использовании методов работы с Системным буфером обмена (Clipboard). Рассмотрим эти методы, общие для компо- нентов, поддерживающих процессы редактирования. Метод CopyToClipboard предназначен для копирования выделен- ного фрагмента текста в Системный буфер обмена. Метод CutToClipboard предназначен для удаления из текста выде- ленного фрагмента и переноса его в Системный буфер обмена. Метод PasteFromClipboard предназначен для вставки текста, со- держащегося в Системном буфере обмена, по месту текстового курсора. Использование этих методов позволяет организовать процедуры редактирования, связанные не только с перемещением фрагментов тек- ста внутри области редактирования, но и обеспечить взаимодействие 244
разрабатываемого приложения с другими, поддерживающими обработ- ку текстовой информации. Процедуры-обработчики, реализованные для пунктов Главного ме- ню, являются общими и подключаются к соответствующим кнопкам панели инструментов и соответствующим пунктам контекстного меню. Далее приводится полный текст файла EditProject.dpr (главного файла проекта) и текст модуля формы EditUnit.pas. Окно работающего приложения представлено на рис. 3.37. program EditProject; uses Forms, EditUnit in 'EditUnit.pas' {Forml}; {$R *.RES) begin Application.Initialize; Application.CreateForm(TForml, Forml); Application.Run; end. unit EditUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, ComCtrls, ToolWin, ImgList, StdCtrls; type TForml = class(TForm) MainMenu: TMainMenu; MFile: TMenuI tem,- MFile_Open: TMenuItem; MFile_Save: TMenuItem; MFile_Close: TMenuItem; MFile_Blank: TMenuItem; MExi t: TMenuItern; MEdi t: TMenuI tern ,- MEdi t_Copy: TMenuI tern ,- MEdi t_Pas te: TMenuItern; MEdit_Cut: TMenuItem; MFile_New: TMenuItern; ToolBar: TToolBar; TBFile_New: TToolButton; TBFile_Open: TToolButton; EditMemo: TMemo; ImageList: TImageList; TBFile_Save: TToolButton; TBFile_Close: TToolButton; 245
ToolButton4: TToolButton; TBEdit_Cut: TToolButton; TBEdit_Copy: TToolButton; TBEdit_Paste: TToolButton; OpenDialog: TOpenDialog; SaveDialog: TSaveDialog; MFile_SaveAs: TMenuItem; PopupMenu: TPopupMenu; PopCut: TMenuItern; PopCopy: TMenuItem; PopPaste: TMenuItern; N4: TMenuItem; PopSelectAll: TMenuItem; procedure MFile_NewClick(Sender: TObject); procedure MFile_OpenClick(Sender: TObject); procedure MEdit_CutClick(Sender: TObject) procedure MEdit_CopyClick(Sender: TObject); procedure MEdit_PasteClick(Sender: TObject); procedure MFile_SaveClick(Sender: TObject); procedure MFile_CloseClick(Sender: TObject); procedure MFile_SaveAsClick(Sender: TObject); procedure MExitClick(Sender: TObject); procedure PopSelectAllClick(Sender: TObject); private { Private declarations } public { Public declarations } end; const ProgCaption:string = 'Редактор'; 'Строковая константа для заголовка окна приложения var Forml: TForml; CurrentFileName: string; 'Имя текущего файла implementation {$R *.DFM} 'Обработчик события OnClick для пункта меню 'Файл/Соэдать procedure TForml.MFile_NewClick(Sender: TObject); begin MFile.Close.Click; Forml.Caption := ProgCaption + ' — Новый файл’; CurrentFileName := '•; end; 'Обработчик события OnClick для пункта меню 'Файл/Открыть procedure TForml.MFile_OpenClick(Sender: TObject); begin if OpenDialog.Execute then 246
try Edi tMemo.Lines.LoadFromFile(OpenDialog.Fi1 eName); Forml.Caption := Forml.Caption + ' - ' + OpenDialog.FileName; CurrentFileName := OpenDialog.FileName,- except on EFOpenError do MessageDlg('Ошибка при открытии файда', mtError, [mbOk], 0); end; end; 'Обработчик события OnClick для пункта меню 'Файл/Сохранить procedure TForml.MFile_SaveClick(Sender: TObject); label 1; var btn : word; fsave : boolean; begin if EditMemo.Text <> '' then if CurrentFileName = ’’ then MFile_SaveAs.Click else try EditMemo.Lines.SaveToFile(CurrentFileName),- except on ElnOutError do MessageDlgf'Ошибка записи в файл', mtError, [mbOk], 0); end; end; 'Обработчик события OnClick для пункта меню 'Файл/Сохранить как... procedure TForml .MFile_SaveAsClick(Sender: TObject) ,- label 1 ,- var btn : word; fsave : boolean; begin if EditMemo.Text <> '' then begin 1: if SaveDialog.Execute then if FileExists(SaveDialog.FileName) then begin btn := MessageDlg('Файл существует. Перезаписать?', mtWarning, [mbYes, mbNo, mbCancel], 0) ; if btn = mrNo then goto 1; if btn = mrCancel then fsave := false; if btn = mrYes then fsave := true; end else fsave := true; if fsave then try EditMemo.Lines.SaveToFile(SaveDialog.FileName); CurrentFileName := SaveDialog.FileName; 247
Forml.Caption := ProgCaption + ' — ' + SaveDi al og. Fi 1 eName ,- except on ElnOutError do MessageDlg('Ошибка записи в файл', mtError, [mbok], 0) ; end; end; end; 'Обработчик события OnClick для пункта меню 'Файл/Закрыть procedure TForml.MFile_CloseClick(Sender: TObject); var btn : word; begin if EditMemo.Text <> '' then begin btn := MessageDlg('Сохранить изменения?', mtWarning, [mbYes, mbNo, mbCancel], 0); if btn <> mrCancel then begin if btn = mrYes then MFile_Save.Click; EditMemo.Text := '; Forml.Caption := ProgCaption; end end end; 'Обработчик события OnClick для пункта меню 'Файл/Выход procedure TForml.MExitClick(Sender: TObject); begin if EditMemo.Text <> '' then MFile_Save.Click; Forml-Close; end; 'Обработчик события OnClick для пункта меню 'Редактировать/Вырезать procedure TForml.MEdit_CutClick(Sender: TObject); begin EditMemo-CutToClipboard; end; 'Обработчик события OnClick для пункта меню 'Редактировать/Копировать procedure TForml-MEdit_CopyClick(Sender: TObject); begin EditMemo.CopyToClipboard; end; 'Обработчик события OnClick для пункта меню * Редактировать/Вставить procedure TForml-MEdit_PasteClick(Sender: TObject); begin EditMemo.PasteFromClipboard; end; 248
'Обработчик события OnClick для пункта 'контекстного меню Выделить все procedure TForml.PopSelectAllClick(Sender: TObject); begin EditMemo.SelectAll; end; end. Vt'j i V'*.rnpir ixr Файл Ctrt+X p Вырезать Первая | возмо?^ Вставить изменило общество и культуру. Ctrt+V । связана с изобретением письменности. Появилась мй и сохранения их для передачи последующим поколениям етением книгопечатания, которое радикальным образом ЗЕловяенз открышам лринимпсезл 5Я5ЯЯ5ВВ8Я5лагодаря которому появились телеграф, телефон, радио, позволяющие оперативно передавать информацию. Четвертая революция (70-е годы >0< в] связана с созданием персональных компьютеров. В конце >0< века человечество вступило в новую стадию развития • стадию построения информационного общества. Информация стала важнейшим Фактором экономического роста, а уровень развития информационной деятельности и степень вовлеченности и влияния ее на глобальную информационную инфраструктуру превратились в важнейшее условие конкурентоспособности страны в мировой экономике. Понимание неизбежности прихода этого общества наступило значительно раньше. Австралийский экономист К. Кларк еще в 40-е годы говорил о наступлении общества информации и услуг, общества с ковьми технологическими и ® экономическими возможностями. Американский экономист Ф. Мах луп выдвинул предположение о наступлении информационной экономики и превращении жформации в важнейший товар в конце 50-х годов. В конце 60-х годов Д. Белл фиксировал превращение индустриального общества в информационное. Что касается стран, ранее входивших в СССР, то процессы информатизации в них развивались замедленными темпами. Информатика меняет всю систему общественного производства и взаимодействия культур. С наступлением информационного общества начинается новый этап не только научно-технической, Рис. 3.37. Приложение «Редактор» Упражнения 1. Модифицировать приложение «Калькулятор», добавив операцию «возведе- ние в квадрат». 2. Модифицировать приложение «Калькулятор», добавив операцию «извле- чение квадратного корня». 3. Модифицировать приложение «Калькулятор», добавив операцию (кнопку) возведения в целую степень. 4. Модифицировать приложение «Калькулятор», добавив операцию вычисле- ния Л! 5. Модифицировать приложение «Редактор», добавив возможность изменения через Главное меню шрифта текста в окне редактирования. 6. Модифицировать приложение «Редактор», добавив в меню сервисные функции: ликвидация лишних пробелов между словами; проверка правильности написания первого слова в предложении (обяза- тельно с заглавной буквы); ликвидация повторяющихся знаков препинания (две точки подряд, две за- пятые подряд и т.п.). 249
Глава 4. Язык программирования BASIC В прошлом веке один английский миссионер, желая облегчить кон- такт с туземным населением, выделил из английского языка самую про- стую и распространенную его часть, содержащую около 300 слов и поч- ти не имеющую грамматики. Это подмножество языка, названное BASIC ENGLISH, действительно оказалось весьма простым для усвое- ния и поэтому вскоре завоевало популярность не только среди туземцев, но и иммигрантов. Подобную цель создания средства для расширения и облегчения контактов, только не между различными группами людей, а между не- профессионалами и ЭВМ, поставили перед собой сотрудники Дартмут- ского колледжа Джон Кемени и Томас Курте. Разработанный ими в 1964 г. алгоритмический язык BASIC (Бейсик), как и всякий другой язык программирования, является формальной знаковой системой, ис- пользуемой для связи человека с ЭВМ и предназначенной для описания данных и алгоритмов их обработки на вычислительной машине. Назва- ние BASIC является аббревиатурой английской фразы «Beginner's All - purpose Symbolic Instruction Code», что в переводе означает «многоцеле- вой язык, символических команд для начинающих» (злые языки, одна- ко, считают, что его изобретатели сначала придумали название своему новому, простому в употреблении языку, а затем уже такую расшифровку). Вскоре как обучаемые, так и авторы программ обнаружили, что Basic может делать практически все то, что делает громоздкий ЯП Фор- тран. А так как языку Basic было легко обучиться и легко с ним рабо- тать, программы на нем писались быстрее, чем на Фортране. Basic был также доступен на персональных компьютерах, обычно он был встроен в ПЗУ. Так Basic завоевал популярность. Интересно, что и сегодня, спустя 40 лет после изобретения, Basic остается самым простым для освоения из десятков языков общецелевого программирования, имею- щихся в распоряжении программистов. Более того, он прекрасно справ- ляется с работой. Несмотря на наличие мощных профессиональных языков Си и Pascal, даже на их фоне Basic считается языком, снабженным мощными средствами решения специфических задач, которые обычно большинст- во пользователей решает при помощи небольших компьютеров, а имен- но, работая с файлами и выводя текстовое и графическое изображение на экран дисплея. 250
Для многих мини- и микроЭВМ Basic предназначался в качестве единственного языка программирования высокого уровня. Это обстоя- тельство привело к появлению различных его версий, включающих в себя многочисленные эффективные средства программирования из дру- гих алгоритмических языков. Таким образом, на сегодняшний день мы имеем не конкретный Basic, а целую группу однотипных диалоговых языков, называемых этим именем. 4.1. Примеры программ Рассмотрим вначале простейшую диалоговую программу (уже зна- комую из предшествующих разделов): Print("Здравствуйте! Как Вас зовут?") Input(Nam$) If Nam$ = "*” Then Exit Do If Nam$ <> "" Then Do Print("Добрый день, " + Nam$) Print("Как настроение?") Input(nastr$) If nastr$ = "*" Then Exit Do If nastr$ <> "" Then Print("У меня тоже " + nastr + ", " + Nam) End If Loop Loop Программа состоит из двух вложенных циклов Do...Loop. Выход из внешнего и внутреннего циклов происходит при условии ввода в строке NAM или NASTR символа " (звездочка) с помощью оператора Exit Do. При попытке ввести пустую строку в ответ на запрос, программа возвращается в заголовок, соответственно, внутреннего или внешнего цикла и повторяет запрос на ввод строки. Управление условными пере- ходами осуществляется условным оператором IF. Операторы Print и Input осуществляют соответственно вывод на эк- ран и ввод с клавиатуры строк текста. Строки ограничиваются симво- лами " (двойные кавычки). Пустая строка выглядит как "". Конкатена- ция (слияние) строк осуществляется операцией "+•. Условный оператор, представленный здесь, имеет структуру If ... End If. Логические условия, представленные в примере, образуются с помощью операций сравнения равно (=) и не равно (<>). 251
В программе создаются и используются две переменных строчного типа — nastr и пат. Как это видно из текста, Basic не различает строч- ные и заглавные символы, поэтому написания Nam и пат эквивалент- ны. Кроме того, в данной программе отсутствуют описания (объявле- ния, декларации) переменных. Это возможно, поскольку Basic, подобно Фортрану, позволяет определить тип в тексте программы непосредст- венно. В данном примере суффикс $ говорит, что данная переменная — строчная (суффикс %, наоборот, задает числовой тип). Рассмотрим далее программу сортировки элементов одномерного массива по возрастанию. Dim W_Array%(100), I as integer, J as integer, I_min as integer Input("Введите количество элементов массива (N<100)", N%) For I = 1 to N% 'Ввод элементов массива Input("Введите элемент массива №"+Str(I)+ W_Array[I]); Next I For I = 1 to N% ' Внешний цикл прохода по элементам массива I_min= I ' Начальное задание индекса ' минимального элемента For J = I + 1 to N% ' Внутренний цикл поиска минимального ’ элемента в пределах от I + 1 до N If W_Array%[J] < W_Array%[I_min] Then I_min = J Next J Swap(W_Array%[I_min], W_Array[I]) ' Перестановка элементов: I-го ' и минимального Next I For I = 1 to N% do ' Вывод отсортированного массива Print("Элемент массива №"+Str(I)+ •=’, W_Array[I]) Next I Структурно программа состоит из следующих компонентов: - цикла ввода исходного массива; - двух вложенных циклов сортировки массива; - цикла вывода результатов сортировки. В результате работы программы на экране может быть отображен следующий диалог: Введите количество элементов массива> 10 Введите элемент массива № 1>2 252
Введите элемент массива № 2>11 Введите элемент массива № 3>3 Введите элемент массива № 4>1 Введите элемент массива № 5>4 Введите элемент массива № 6>16 Введите элемент массива № 7>5 Введите элемент массива № 8>12 Введите элемент массива № 9>7 Введите элемент массива № 10>8 Элемент массива №1 = 1 Элемент массива №2 = 2 Элемент массива №3 = 3 Элемент массива №4 = 4 Элемент массива №5 = 5 Элемент массива №6 = 7 Элемент массива №7 = 8 Элемент массива №8 = 11 Элемент массива №9=12 Элемент массива № 10 = 16 Циклы в данном примере организуются вложенными операторами For 1=1 to N% ... Next I и For J=1 to N% ... Next J. В качестве условного оператора здесь используется упрощенная конструкция If (без End И), так как здесь при условии выполняется простой (а не составной, как в первом примере) оператор. В программе появились объявления переменных — массива целых чисел и двух целых переменных. Из текста программы ясно, что обра- щение к элементу массива осуществляется по индексу, заключенному в квадратные скобки. Комментарий начинается с символа ’ (одинарная кавычка). Перестановка элементов массива осуществляется функцией Swap, что избавляет от необходимости совершать последовательность пере- сылок. И 4.2. Лексика языка программирования Basic При записи текстов программ на языке Basic разрешается исполь- зовать заглавные и строчные буквы латинского алфавита (А В С D Е F GHIJKLNOPQRSTUVWXYZabcdefghijklnopqrstu v w х у z); знак «подчеркивание» (_); цифры (0 1 2 3 4 5 6 7 8 9) и огра- ничители. Ограничители представляют собой знаки пунктуации, знаки опера- ций, разделители и служебные (зарезервированные) слова. 253
Назначение знаков пунктуации: Знаки Назначение 1 Апостроф — признак комментария. Текст после апострофа до конца строки поясняет алгоритм и не является его частью Разделение списков значений, параметров процедуры и функции о Задание индексов массива, выделение части выражения, задание списков параметров - Знак оператора присваивания ! Обозначение типа данных Single, служебный знак символьного образца # Обозначение типа данных Double, служебный знак символьного образца @ Обозначение типа данных Currency $ Обозначение типа данных String % Обозначение типа данных Integer & Обозначение типа данных Long ? Служебный знак символьного образца * Служебный знак символьного образца [] Ограничители множества значений для символьного образца Признак переноса оператора на следующую строку Десятичная точка, разделитель целой и дробной части 3 Разделитель выражений в операторах ввода-вывода Разделитель операторов в одной строке Знаки операций представлены следующим набором ограничителей: + _/*Л = ><\ В качестве разделителя выступает знак пробела. Служебные (зарезервированные) слова Следующие слова могут быть использованы только по своему спе- циальному назначению: And Boolean Byte Call Case Choose Const Currency Date Deftype Dim Do Double Each Else End Eqv Exit Explicit For Function Get GoSub GoTo If Imp Integer Is Let Like Long Loop Mod Module New Next Not Object Option Or Private Property Public ReDim Return Select Set Single Static Stop String Sub Switch Then Variant WEnd While With Xor 254
При записи текста программы нет разницы между строчными и прописными буквами. И 4.3. Переменные и типы данных Перейдем далее к краткому рассмотрению ЯП Basic на примере его современной реализации — Visual Basic. Переменные В Visual Basic переменные именуются с помощью идентификато- ров, длина которых может достигать 255 символов (они должны начи- наться с буквы, за которой могут следовать другие буквы, цифры или символы подчеркивания). Регистр символов и наименований перемен- ной значения не имеет. Типы данных В языке Visual Basic, как и в языке Pascal, типы данных делятся на простые (или базовые) и структурированные. К простым (базовым) типам в языке Visual Basic относятся: • целый; • вещественный; • логический. К стандартным структурированным типам относятся: • дата; • массив; • строка; • тип, определяемый пользователем. Целый тип данных. В стандарте языка Basic определен единст- венный целый тип данных Integer. В реализации языка Visual Basic це- лый тип данных представлен множеством типов: Название типа Область измене- ния данных Занимаемый раз- мер в байтах Знак числа Byte 0..255 1 Целое без знака Integer -32768..32767 2 Целое со знаком Long -2J,..2JI-1 4 Целое со знаком Логический (булевский) тип данных. Данные логического типа (Boolean) в стандарте языка могут принимать одно из двух значений: 255
True или False. Переменная или константа логического типа занимает в Visual Basic 2 байта, в которые записывается 0, если переменная или константа имеет значение false, и любое целое, отличное от 0, в против- ном случае. Название типа Область изменения данных Занимаемый размер в байтах Boolean True..False 2 Когда прочие числовые типы данных преобразуются в тип Boolean, значение 0 воспринимается как False, а любое другое значение стано- вится значением True. Если значения типа Boolean преобразуются в значения других (числовых) типов, то значение False становится значе- нием 0, a True становится -1. Вещественный тип данных. В Visual Basic определены шесть стандартных вещественных типов. Каждый тип характеризуется своей областью изменения возможных значений. Выбор конкретного типа для переменной связан с требуемой точ- ностью вычислений. Название типа Размер памяти (в байтах) Область изменения Single 4 -3.402823Е38 .. -1.401298Е-45 для отрицательных значений; 1.401298Е-45 .. 3.402823Е38 для положительных значений Double 8 -1.79769313486232Е308 .. -4.94065645841247Е-324 для отрицательных значений; 4.94065645841247Е-324 .. 1.79769313486232Е308 для положительных значений Currency 8 -922 337 203 685 477.5808.. 922 337 203 685 477.5807 Decimal 14 -79 228 162 514 264 337 593 543 950 335 .. 79 228 162 514 264 337 593 543 950 335 — для це- лых чисел; -7.9228162514264337593543950335 .. 7.9228162514264337593543950335 — для чисел с фиксированной точкой (28 знаков после десятичной точки); Минимальное отличное от 0 число: +/-0.0000000000000000000000000001 256
Тип данных Variant. Данный тип добавлен в Visual Basic 5 из вер- сии 2.0. Переменная типа Variant может содержать данные любого типа. Если для переменной не объявлен тип данных, то по умолчанию ис- пользуется тип данных Variant. Тип информации, хранимой в переменной, при этом не имеет зна- чения, поскольку Variant может принять любой тип данных (численный, дата/время, строковый): Visual Basic автоматически производит необхо- димые преобразования данных. С другой стороны, можно использовать встроенные функции для проверки типа данных, хранящихся в пере- менной типа Variant. Использование такого типа данных, как Variant, замедляет работу программы, так как требуется время и ресурсы для выполнения опера- ций преобразования типов. Кроме того, использование автоматических преобразований типов данных приводит к неаккуратному виду про- грамм. Если в программе для некоторой переменной MyVar записаны опе- раторы MyVar = 5 MyVar = MyVar + 1 MyVar = "String value" MyVar = UCase(MyVar) то фактически тип переменной в каждом операторе будет определяться выражением в правой части. Тип данных Дата. Переменные типа Дата представляют собой 8- байтовые представления в форме с плавающей точкой календарных дат в интервале от1 января 100 года до 31 декабря 9999 года с составляю- щей времени в интервале от 0:00:00 до 23:59:59. Название типа Размер памяти (в байтах) Область изменения Date 8 January 1, 100.. December 31,9999 Константы типа Дата должны справа и слева ограничиваться зна- ком «#», например, #January 1, 2002#. Тип данных Строка (String). Строковый тип данных позволяет хранить последовательности символов — строки. Строки могут быть переменной и фиксированной длины. Теоретически такой тип данных позволяет хранить строковые переменные длиной до 2 миллиардов символов. Однако, на конкрет- ном компьютере это число может быть гораздо меньше из-за ограни- 257
ченных объемов оперативной памяти или ресурсов операционной системы. Название типа Размер памяти (в байтах) Область изменения String (переменной длины) 10 + длина строки От 0 до приблизительно 2 миллиардов символов String (фиксированной длины) Длина строки От 1 до приблизительно 65400 символов Строки фиксированной длины. Строки фиксированной длины представляют собой специальный тип строки, длина которой ограниче- на. Подобные переменные создаются при помощи оператора Dim. На- пример: Dim Shortstring As String * 10 Dim strShort As String * 10 MyStringVariable$="My string" Операции co строковыми типами. Visual Basic позволяет исполь- зовать знак операции «+» или «&» для объединения двух строковых операндов. Результатом операции S+T (или S&T), где S и Т имеют стро- ковый тип, будет конкатенация S и Т — новая строка, результат добав- ления строки Т в конец строки S. Операция Название Тип операнда Тип результата + ,& конкатенация Строковый строковый Стандартные функции для работы со строками Для работы с переменными строкового типа определены стандарт- ные функции. Некоторые из них (наиболее часто используемые) приве- дены в таблице: Имя Назначение Аргументы (параметры) Результат Left(S,m) LeftS(S,m) Оставляет в стро- ке m символов, начиная с первого S — строка, m — количество символов Строка, содержащая первые m символов строки S Right(S,m) Right$(S,m) Оставляет в строке m послед- них символов S — строка, m — количество символов Строка, содержащая последние m символов строки S 258
Продолжение табл. Имя Назначение Аргументы (параметры) Результат Mid(S,I [,m]) Mid$(S,I [,m]) Выделение подстроки Строка S; целые значения I и m Часть строки S, начи- ная с позиции I, дли- ной т. Если параметр m отсутствует или его значение больше дли- ны строки, то выдается часть строки S, начи- ная с позиции I до конца строки StrComp (sl,s2 [,cmp]) Сравнение двух строки Строка si, строка s2, стр — при- знак сравнения (0 — двоичное, 1 — символьное) 0, если si = s2; -1, если si < s2; 1, если si > s2; Null, если sl= Null или s2 = Null Len(S) Вычисление длины строки Строка S Целое значение длины строки LCase(S) LCase$(S) Преобразова- ние строки к нижнему реги- стру Строка S Новое значение строки S — все символы строчные InStr([I,]sl, s2[,cmp]) Вычисляет позицию нача- ла подстроки в строке Подстрока s2, строка si, целое значение I — по- зиция начала по- иска, стр — при- знак сравнения (0 •—двоичное, 1 — символьное) = 0, если s2 не содер- жится в si; целое >0 — позиция s2r в si. Если параметр I отсутствует, поиск ведется от начала строки Trim(S) Trim$(S) Удаляет пробе- лы в начале и в конце строки Строка S Новая строка LTrim(S) Ltrim$(S) Удаляет пробе- лы в начале строки Строка S Новая строка RTrim(S) Rtrim$(S) Удаляет пробе- лы в конце строки Строка S Новая строка UCase(S) UCase$(S) Преобразова- ние строки к верхнему реги- стру Строка S Новое значение строки S — все символы строчные 259
Продолжение табл. Имя Назначение Аргументы (параметры) Результат Val(S) Преобразова- ние строкового типа в число- вой Строка S Целое или веществен- ное значение, соответ- ствующее строковой записи S. Пробелы, знвки табуляции и конца строки пропус- каются, преобразова- ние ведется от начала строки до первого символа, который не может быть символом записи числа. Примеры использования строковых функций: выделение первого слова в предложении (разделитель слов — знак «пробел»). S_Sentence = Trim(S_Sentence) ' Удаление пробелов в начале строки i = InStr(S_Sentence, " ") ' Определение позиции первого пробела ' в предложении If i > О Then SWord = Left(S_Sentence, i-1) Else S_Word = S_Sentence End If ' В строковой переменной S_Word — ' значение первого слова удаление из строки всех цифр: L = Len(S_Sentence) ' Вычисление длины строки SDigit = "0123456789" ' Инициализация строковой переменной ' Sdigid, содержащей перечень всех ' цифр 1 = 1 While I <= L Ch = Mid(S_Sentence,I,1) 'Выделение очередного символа строки S_Sentence 260
J = InStr(SDigit, Ch) 'Проверка, является ли символ цифрой If J>0 Then S_Sentence = Left(S_Sentence, I — 1) + Right(S_Sentence, L — I) 'Удаление символа из строки L = L-1 'Уменьшение длины строки на 1, 'значение I не меняется Else I = 1 + 1 End If Wend • подсчет количества букв "W" в строке (независимо от регистра): N_w = 0 ' Обнуление счетчика букв S_Sentence = LCase(S_Sentence) 'Преобразование строки к нижнему ' регистру L = Len(SSentence) ' Подсчет длины строки For I = 1 То L If InStr(S_Sentence, "w")>0 Then N_w = N_w+1 'Увеличение счетчика букв на 1 Тип данных Массив Язык позволяет определить две разновидности массивов: статиче- ские и динамические. Границы статического массива устанавливаются на этапе разработки и могут изменяться только в новой версии про- граммы. Динамические массивы изменяют свои границы в ходе выпол- нения программы. С их помощью можно динамически задавать размер массива в соответствии с конкретными условиями. Для объявления статического массива используется оператор Dim с указанием в круглых скобках после имени массива его максимального индекса: Dim NameArray (100) As String В этом случае элементы переменной NameArray различают не по имени, а по индексу: NameArray(4) = "Иванов" Print NameArray(10) Статические массивы определяются только глобально — их нельзя определить локально внутри процедуры. В Visual Basic индексирование массива всегда начинается с нуля, т.е. индекс 0 обозначает первый элемент массива, индекс 1 — второй и т.д. 261
Оператор Option Base позволяет задать индексацию массива с 1: Option Base 1 Допустимыми значениями для Option Base являются только 0 и 1. Этот оператор служит для того, чтобы обеспечить совместимость Visual Basic с другими диалектами Basic, индексация в которых начинается с 1. Для установки других границ массива необходимо использовать следующий синтаксис: Dim Имя_переменной ( (Нижн__предел То] Верхн_предел) Указанием верхней и нижней границ можно задать любые диапазо- ны индекса. Это удобно, если индекс несет также определенную смы- словую нагрузку (дата, номер заказа, возраст и т.п.) Dim BirthDate (1980 То 2050) Visual Basic позволяет также создавать многомерные массивы. При объявлении многомерного массива верхние границы каждой размерно- сти разделяются запятыми: Dim NameArray(10, 25) As String Массив с именем NameArray может содержать 286 различных зна- чений (11’26 =286). Динамический массив объявляются в том случае, если его размер заранее неизвестен. Объявление массива как динамического позволяет изменять его размер или размерность во время выполнения программы. Динамический массив создается в два этапа. Сначала массив опре- деляют без указания размера: Dim DynArray() As Variant Затем с помощью оператора ReDim устанавливают фактический размер массива: ReDim DynArray (50, 10) Синтаксис оператора ReDim: ReDim Имя_переменной (Границы) [As Тип_данных] Операции с массивами Начиная с Visual Basic 6.0, в языке появилась возможность присво- ить содержимое одного массива другому так же, как обычно присваива- ется значение одной переменной другой. Например, задача копирования массива может быть решена традиционным путем (поэлементным копи- рованием): 262
B'or i = Lbound (oldCopy) To Ubound(oldCopy) newCopy(i) = oldCopy(i) Next В этом примере функция Lbound(oldCopy) определяет нижнюю границу индекса, a Lbound(oldCopy) — верхнюю границу индекса мас- сива oldCopy. Однако гораздо проще и привлекательней это выглядит, если присвоить один массив другому: newCopy = oldCopy Тип данных, определяемый пользователем Язык Basic дает возможность определять типы данных, представ- ляющие собой совокупность описания полей данных, аналогичную, на- пример, записи языка Pascal. Синтаксис определения пользовательского типа данных следующий: Туре <имя типа> <имя поля> As type [<ил/я поля> As type] End Type После описания типа данных необходимо разместить переменную заданного типа с помощью оператора Dim: Dim <имя переменной> As <имя типа> Например: Type StudentRecord ' Определяем тип данных FirstName As String * 20 LastName As String * 20 Address As String * 30 Phone As Long Birthday As Date End Type Dim MyRecord As StudentRecord ' Объявляем переменную ' Заполняем поля данных MyRecord.FirstName = "Лютиков" MyRecord.LastName = "Иван" MyRecord.Address = "г.Москва, ул.Профсоюзная, д.5 кв.10" MyRecord.Phone = 1205643 MyRecord.Birthday = #12.09.86# Идентификаторы типов данных При объявлении переменных тип данных можно не указывать. Для того чтобы переменная была отнесена к определенному типу, можно 263
использовать так называемые идентификаторы типов — специальные символы, добавляемые справа к идентификатору, задающему имя пере- менной Тип данных Знак Пример Integer % MyIntVar% = 5 Long & MyLongVar& = 317 689 654 Single t MySingleVar! = -3.40282 Double # MyDoubleVar# = 10Л(-12) Currency @ MyCurrencyVar® = 685 477.5807 String $ MyStringVarS = "My string" 4.4. Операции Операции подразделяются на несколько групп: • арифметические операции; • операции отношения; • логические операции; • операции с битами информации. Приоритет выполнения операций Если в выражении заданы операции более чем одной категории, то существует следующий порядок их выполнения: сначала выполняются арифметические операции, затем операции сравнения и последними выполняются логические операции. Операции сравнения имеют один уровень приоритета и выполняются в порядке следования слева направо. Арифметические и логические опера- ции выполняются в соответствии с порядком, приведенным в таблице: Арифметические операции Операции сравнения Логические операции Возведение в степень (л) Равенство (=) Not Признак отрицательного числа (-) Неравенство(о) And Умножение и деление (*, /) Меньше, чем (<) Or Деление нацело (\) Больше, чем (>) Xor Остаток от деления (Mod) Меньше либо равно (<=) Eqv Сложение и вычитание (+, -) Больше либо равно (>=) Imp Конкатенация строк (&) Like, Is 264
Операция конкатенации строк (&) не относится к арифметическим операциям, но по приоритету выполнения находится как раз между арифметическими операциями и операциями сравнения (т.е. выполняет- ся после арифметических операций, но перед операциями сравнения). Арифметические операции Арифметические операции могут применяться только к операндам целых и вещественных типов: Знак Операция Кол-во операн- дов Тип опе- рандов Тип ре- зультата Описание Возведение в степень 2 Целый Хотя бы один веще- ственный Целый Веществе нный хЛу — возведе- ние х в степень у. Значение х может быть от- рицательным только при це- лом у — Признак отрица- тельного числа 1 Целый Веществен- ный Целый Веществе нный Меняет значение операнда на зна- чение противо- положного знака + Сложение 2 Целый Целый Вещест- венный Результат — сумма двух чисел — Вычитание 2 Целый Хотя бы один веще- ственный Целый Веществе нный Результат — разность двух чисел * Умножение 2 Целый Хотя бы один веще- ственный Целый Веществе нный Результат — произведение двух чисел / Деление 2 Целый или веществен- ный Веществе нный Результат — частное от деле- ния двух чисел \ Деление целых чи- сел 2 Целый Целый Результат — целая часть от деления целых чисел: 25\6 = 4 265
Продолжение табл. Знак Операция Кол-во операн- дов Тип опе- рандов Тнп ре- зультата Описание mod Остаток от деления целых чи- сел 2 Целый или веществен- ный Целый Результат — остаток от деле- ния нацело: 25 mod 6 = 1 Если один из операн- дов — вещест- венный, перед выполнением операции проис- ходит округле- ние его до цело- го числа, напри- мер: 25 mod 6.7 = 4 В качестве операндов арифметических операций могут выступать стандартные арифметические (или тригонометрические) функции, т.е. функции с результатом целого или вещественного типа, аргументами которых являются выражения целого или вещественного типа. В табли- це приведены характеристики некоторых арифметических и тригоно- метрических функций. Функция Назначение Тнп аргумента Тип результата Abs(X) Модуль (абсолютная величина) аргумента Целый Вещественный Целый Вещественный Atn(X) Арктангенс аргумента Вещественный Вещественный (вычисляется в радианах) Cos(X) Косинус аргумента Вещественный Вещественный Abs(Cos(X))<=l Ехр(Х) Возведение числа е (основание натураль- ного логарифма) в степень X Вещественный, X <= 09.782712893; е считается равным 2.718282 Вещественный Fix(X) Целая часть числа. Для отрицательных чисел — минималь- ное целое число, большее или равное X Целый или вещест- венный Целый Например: Fix(-2.8) = -2 Fix(2.8) = 2 266
Продолжение табл. Функция Назначение Тип аргумента Тип результата Int(X) Целая часть числа. Для отрицательных чисел — минимальное целое число, меньшее или равное X Целый или вещест- венный Целый Например: Int(-2.8) = -3 Int(2.8) = 2 Log(X) Натуральный лога- рифм аргумента Целый или вещест- венный; е считает- ся равным 2.718282 Вещественный Rnd[(num ber)] Генератор случайных чисел Целый Вещественный, 0= Rnd <1 Sgn(X) Индикатор знака числа Целый или вещест- венный -1, если X < 0; 0, если X = 0; 1, если X > 0. Sin(X) Синус аргумента Вещественный Вещественный Abs(Sin(X))<=l Tan(X) Тангенс аргумента Вещественный Вещественный (в радианах) Sqr(X) Квадратный корень аргумента Вещественный, Х>=0 Вещественный Pi Число л Вещественный Перед вызовом функции Rnd необходимо инициировать генератор случайных чисел посредством использования оператора Randomize. Для того чтобы получить последовательность случайных чисел в заданном интервале [нижняя_граница, верхняя_граница], используют следующую формулу: \гЛ((<верхняя_граница> - <нижняя_граница> + 1) * Rnd + <нижняя_граница>) Например, чтобы сгенерировать случайные числа в интервале от 1 до 7, необходимо записать следующую последовательность действий: Randomize MyValue = Int((7-1+1) * Rnd) + 1) Результат тригонометрической функции Atn(X) представляет собой угол, измеряемый в радианах. Чтобы преобразовать радианы в градусы, необходимо умножить значение в радианах на константу 180/Pi. (180/л) Для обратного преобразования (градусов в радианы) необходимо умно- жить величину в градусах на константу Pi/180. Значение функции Fix(X) эквивалентно выражению: 267
Sgn(X) * Int(Abs(X)) Чтобы вычислить выражение вида logx у, можно воспользоваться выражением: Lg = Log(Y) / Log(X) Примеры записи арифметических выражений: (Int(X)+sqr(Z))*2 Fix(Tan(X)-1) Pi* R"2 (Cos(X)+Y)/Z Log(X)/Log(2)+X/Y РЛ0)/ИЛ(S+T) Операции сравнения В результате выполнения операций сравнения получается значение логического типа: true или false. Приведенная ниже таблица показывает для каждой операции, выполняемой над операндом 1 (01) и операндом 2 (02), значение результата в зависимости от значений операндов. Если хотя бы один из операндов при выполнении любой операции принимает стандартное значение Null, то результатом выполнения операции тоже будет величина, равная Null. Опе- рация Описание Тгие(нстнна), если Гаке(ложь), если < Меньше О1 < 02, например: 2 < 5 — истина О1 >= 02, например: 5 < 5 — ложь <= Меньше либо равно О1 <= 02, например: 2 <= 5 — истина О1 > 02, например: 5 <= 2 — ложь > Больше О1 > 02, например: 5 > 2 — истина О1 <= 02, например: 5 > 5 — ложь >= Больше либо равно О1 >=02, например: 5 >= 2 — истина О1 < 02, например: 2 >= 5 — ложь = Равно О1 = 02, например: 5 = 5 — истина О1 о 02, например: 2 = 5 — ложь о Не равно О1 о 02, например: 2 о 5 — истина О1 = 02, например: 5 о 5 — ложь При сравнении операндов следует учитывать, что правила сравне- ния чисел или символьных строк всегда корректно действуют только в том случае, если сравниваемые операнды однотипны, например, оба операнда — числовые значения или оба операнда — символьные стро- 268
ки. Если же операнды разного типа, или один из операндов имеет тип Variant, то справедливы следующие правила выполнения операций сравнения: • если один из операндов числового типа, а второй — типа Variant и является числом, то сравнение проводится по правилам сравнения чисел; • если один из операндов числового типа, а второй — типа Variant и является строкой, не конвертируемой в число, то будет получена ошибка несоответствия типов; • если один из операндов типа String, а второй — типа Variant и от- личен от Null, то сравнение проводится по правилам сравнения строк; • если один из операндов числового типа, а второй — Empty, то сравнение проводится по правилам сравнения чисел и значение Empty принимается равным 0; • если один из операндов типа String, а второй — Empty, то сравне- ние проводится по правилам сравнения строк и значение Empty принимается равным пустой строке ('"'). К операциям сравнения относят также и операции Is и Like, кото- рые имеют специфические области применения. Операция Is применяется для сравнения двух переменных А и В типа object. Если обе переменные ссылаются на один физический объ- ект в памяти, то в результате операции A Is В получается значение True. В противном случае результат равен False. Операция Like служит для выявления соответствия между значени- ем переменной строкового типа и так называемым образцом. Под об- разцом здесь понимается строка, содержащая специальные символы, трактующиеся в соответствии со следующими правилами: Символ в образце Соответствие в строке 7 Любой символ, например: "аВа" Like "а?а" возвращает True * Любое количество символов (или ни одного), напри- мер: "ВАТ!23456" Like "В?Т*" возвращает True # Любая цифра (0-9), например: "год 2002" Like "год ####" возвращает True [список_символов] Любой символ из списка, например: "X" Like "[A-Z]" возвращает True "X" Like "[A-WY-Z]" возвращает False [!список_символов] Любой символ, не находящийся в списке, например: ”9" Like "(!А-г]"возвращает True "X" Like "[1A-WY-Z]" возвращает False 269
Логические операции Логические операции применяются к операндам логического типа, т.е. в качестве операндов выступают выражения, при вычислении дающие результат логического типа. Результат выполнения логических опера- ций тоже логического типа. Вычисление логических выражений проис- ходит в соответствии с таблицами истинности логических операций. Таблицы истинности задают соответствие между значениями операндов и результатом выполнения операции (см. табл 1.10, 1.11). Помимо значений True и False в таблицы истинности операций до- бавлено стандартное значение Null. Результат выполнения логической операции со значением Null в одном или в обоих операндах также мо- жет быть равным значению Null. Примеры записи логических выражений. (Х>=0) and (Х<=1) (<Х>0) and (ХсО.5)) or (Х>3) (N mod 2=0) imp (N>0) При использовании логических выражений в качестве условий (на- пример, в условных операторах и операторах цикла) значение логиче- ского выражения Null приравнивается к значению False. Операции с битами информации (побитовые) Операции побитового сравнения выполняются для операндов чи- слового типа и в результате дают число, получающееся путем побито- вого выполнения операции над операндами в соответствии с правилами, приведенными в табл. 1.12. Рассмотрим примеры выполнения операций побитового сравнения. Пусть объявлены две переменных А и В типа Integer. В переменной А находится число 12, а в переменной В — число 9. Запишем значения переменных в двоичном побитовом представлении и применим опера- ции побитового сравнения: А= 12 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 В = 9 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 Not В = not 9 = -10 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 A and В = 12 and 9 = 8 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 АогВ = 12 or 9 = 13 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 A eqv В = 12 eqv 9 = -6 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 A imp В = 12 imp 9 = -5 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 А хог В = 12 хог 9 = 5 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 270
И 4.5. Операторы языка Оператор присваивания Предназначен для присваивания переменной значения. Синтаксис: [Let] «идентификатор» = «выражение» Тип данных переменной, имя которой стоит в левой части операто- ра присваивания, должен быть совместим с типом данных выражения. Если для переменной не задан тип данных, то оператор присваивания одновременно с занесением значения в переменную определяет и ее тип данных для дальнейшего использования. Тип данных в этом случае со- ответствует типу данных выражения в правой части оператора. Например: Let L = 2*Pi*R — присвоить переменной L значение длины окружно- сти радиуса R Аналогично: L = 2*Pi*R Let 1 = 1 + 5 — увеличить на 5 числовое значение, находящееся в переменной I Аналогично: 1 = 1 + 5 Оператор безусловного перехода Оператор GoTo позволяет изменить стандартный последователь- ный порядок выполнения операторов и передать управление заданному оператору, которому в этом случае должна предшествовать метка. Эта же метка должна быть указана при операторе goto. Метка представляет собой либо целое число в пределах от 0 до 9999, либо идентификатор. Синтаксис оператора безусловного перехода: GoTo «Метка» «Метка» представляет собой идентификатор или целое число — номер строки программного кода. Метка всегда должна стоять в начале строки и отделяется от первого оператора строки двоеточием. Значение метки должно быть уникально внутри модуля. Пример: 271
10: MyString = "Выполняется оператор строки номер 10" GoTo 30 ' Передача управления на строку номер 30. 20: MyString = "Выполняется оператор строки номер 20" 1 Оператор строки номер 20 не выполняется 30: MyString = "Выполняется оператор строки номер 30" Условный оператор В языке Visual Basic условный оператор реализован в двух формах: условный оператор If...Then...Else и условный оператор Select Case. Условный оператор If...Then...Else имеет следующий синтаксис: If «логическое выражение» Then [<оператор_1гие>] [Else <onepaTop_false>] Такая синтаксическая конструкция должна быть обязательно запи- сана в одной строке. Если в качестве оператора выступает последова- тельность операторов, то операторы при записи должны отделяться друг от друга двоеточием («:»). Рассмотрим пример. Пусть необходимо написать последовательность действий для ре- шения следующей задачи: присвоить переменной у значение sin х, если х > 0, и значение cos х, если х < 0. If х > 0 Then у = sin(x) Else у = cos(x) Допустим, что в условии задачи необходимо еще определить зна- чение переменной z: z = 1, если х > 0, и z - 0, если х < 0: If х > 0 Then у = sin(x) : z =1 Else у = cos(x) : z = 0 Оператор If...Then...EIse имеет и другую (так называемую блоч- ную) форму записи: If «логическое выражение» Then [<оператор_1гие>] [Elself «логическое выражение_1> Then [<оператор_1>] [Elself «логическое выражение_2> Then [<оператор_2>] [Elself Алогическое выражение_Ы> Then [<оператор_ N>] [Else[<onepaTop_felse»]J End If Обязательными частями записи условного оператора в блочной форме являются первая и последняя строки. Количество условных ветвей, начи- нающихся со служебного слова Elself, теоретически не ограничено. 272
Такую форму удобно использовать в том случае, если реализация алгоритма требует программирования вложенных условных операторов. Изменим, например, условие представленной выше задачи следующим образом: присвоить переменной у значение sin х, если х < 1, и значение cosx, если х > 2, т.е. значение переменной х рассматривается на трех интервалах: (-оо, 1), [1,2) и [2, +оо). Алгоритм решения задачи может быть описан с помощью блочной формы условного оператора: If х < 1 Then у = sin(x) Elself х >= 2 then у = cos(x) End If Далее рассмотрим запись алгоритма решения задачи, в которой значение у зависит от х следующим образом: У = sinx, х<-1 1 - sin х, -1 < х < О 1-cosx, 0<х<1 COSX, х>1 Условный оператор для реализации алгоритма может быть органи- зован так: If х < -1 Then у = sin(x) Elself (х >= -1) and (х < 0) then у = 1 — sin(x) Elself (x >= 0) and (x <= 1) then у = 1 — cos(x) Else у = cos(x) End If Условный оператор Select Case аналогичен по действию оператору Case в языке Pascal. Синтаксис оператора Select Case следующий: Select Case <выражение>[Са«е <константа_1> [<оператор_1>] [Case <константа_1Ч> [<onepaTop_N>][Case Else [<оператор>] End Select <Выражение> в заголовочной части оператора представляет собой арифметическое, логическое или строковое выражение, результат вы- числения которого последовательно сравнивается с константными зна- 273
чениями (числовыми или строковыми), заданными в Case-ветвях опера- тора. Если значение выражения совпало с одним из константных значе- ний, то выполняется оператор (или группа операторов), записанный после заголовка соответствующей Case-ветви. Если же константного значения, равного значению выражения, не найдено, то выполняется оператор (или группа операторов), записанный после Case Else. В случае использования в заголовочной части логического выраже- ния, Case-ветвей может быть три — с константными значениями True, False и Null. Между заголовками Case-ветвей может стоять любое количество строк операторов. Операторы, записанные в одной строке, должны от- деляться друг от друга двоеточием. При использовании оператора Select Case необходимо помнить о том, что значение выражения и константы должны быть одного типа. Рассмотрим запись оператора Select Case для решения задачи, предложенной в описании оператора Case языка Pascal: Присвоить строке S значение дня недели для заданного числа D при условии, что в месяце 31 день и 1-ое число — понедельник. Select Case D mod 7 Case 1 S = "понедельник" Case 2 S = "вторник" Case 3 S = "среда" Case 4 S = "четверг" Case 5 S = "пятница" Case 6 S = "суббота" Case 0 S = "воскресенье" End Select С введением ограничений на область возможных значений пере- менной D и использованием оператора Select Case в полной форме, по- лучим следующую запись алгоритма решения задачи: If (D>=1) and (D<=31) then Select Case D mod 7 Case 1 S = "понедельник" Case 2 S = "вторник" Case 3 274
S = "среда" Case 4 S = "четверг" Case 5 S = "пятница" Case 6 S = "суббота" Case Else S = "воскресенье" End Select End If Операторы цикла В языке Visual Basic реализовано три разновидности оператора цикла — операторы For...Next, Do...Loop и While...Wend. Оператор For...Next Синтаксис: For «переменная цикла> = <начало> То <конец> [Step <шаг>] [<тело цикла>] Next [«переменная цикла>] «Переменная цикла>, <начало>, <конец> и <шаг> должны принад- лежать числовому множеству значений. Общий алгоритм выполнения оператора совпадает с алгоритмом выполнения оператора For языка Pascal. Основное отличие состоит, во-первых, в возможности задать шаг изменения переменной цикла (необязательная конструкция Step <шаг>) и, во-вторых, — в возможности использования оператора Exit For внут- ри тела цикла. Использование конструкции Step <шаг> позволяет управлять изме- нением значений переменной цикла. Значение шага изменения может быть как положительным, так и отрицательным. В случае отсутствия конструкции в заголовке цикла по умолчанию принимается значение шага, равное 1. Рассмотрим действие конструкции Step <шаг> на при- мере простого цикла, увеличивающего значение переменной J (считаем, что начальное значение J равно 0): Оператор For Значения переменной цикла Значение J после выхода из цикла For I = 1 То 10 Step 2 J = J+l Next 1 1 = 1,3, 5,7,9 J = 5 For 1 = 10 To 1 Step -2 J = J+l Next I 1= 10, 8, 6, 4,2 J = 5 Fori = 1 To 10 J = J+1 Next I 1 = 1,2, 3, 4, 5, 6, 7, 8, 9, 10 J= 10 275
Для значений <начало> и <конец> должно выполняться следующее правило: • при отрицательном значении шага <начало> > <конец>; • при положительном значении шага <начало> < <конец>. Если указанные правила в заголовке цикла не выполняются, то не выполняются и операторы тела цикла. Использование в теле цикла оператора Exit For позволяет программи- ровать альтернативный путь для выхода из цикла. При его выполнении управление немедленно передается первому оператору, следующему за предложением Next, т.е. происходит выход из цикла. Например, в приве- денном ниже операторе выход из цикла произойдет, как только очередное вычисленное значение переменной J станет больше 5 (т.е. при J = 7): For I = 1 То 10 J = J*2+l If J > 5 then Exit For Next I Оператор For...Next можно использовать для программирования вложенных циклов, например, используя следующую конструкцию: For I = 1 То 10 For J = 1 То 10 For К = 1 То 10 Next К Next J Next I Любая другая последовательность применения операторов Next будет некорректной, например, вложенный оператор, приведенный ни- же, вызовет ошибку: For I = 1 То 10 For J = 1 То 10 For К = 1 То 10 Next J Next К Next I Если при этом в предложении Next не указывается переменная цикла, то очередное предложение Next считается относящемся к перво- му (по направлению вверх) предложению For ... То. Примеры применения оператора For ... То: 1. Вычислить значение функции у = N! (у = 1 х2хЗх ... xN) для N = 100. 276
у = 1 For i = 2 То 100 У = У*1 Next i 2. Вычислить значение функции у = xn: у = 1 For i = 1 То n у = у*х Next i 3. Найти максимальный делитель D натурального числа к (за ис- ключением самого к): For i = к \ 2 То 1 Step -1 if к mod i = 0 then D := I Exit For ' Выход из цикла, если делитель найден End If Next i Оператор Do...Loop Оператор предназначен для повторения выполнения тела цикла до тех пор, пока значение некоторого логического выражения истинно или до тех пор, пока это значение не станет истинным. Оператор может быть записан в двух формах: в форме проверки условия до выполнения тела цикла и в форме проверки условия после выполнения тела цикла Синтаксис оператора с проверкой условия до выполнения тела цикла: Do [{While | Until} <условие>][<тело цикла>]Ьоор Синтаксис оператора с проверкой условия после выполнения тела цикла: Во[<тело цикла>]Ьоор [{While | Until} <условие>] Алгоритм выполнения оператора совпадает с алгоритмом выполне- ния операторов While и Repeat языка Pascal: максимальное число шагов цикла заранее неизвестно, а изменение значения переменных, входящих в логическое выражение, должно программироваться внутри тела цикла. Необязательное <условие> выполнения цикла может быть задано со словом While, и тогда тело цикла выполняется до тех пор, пока зна- чение условия — истина, или со словом Until, и тогда тело цикла вы- 277
полняется до тех пор, пока значение условия — ложь. Необязательность задания условия выполнения цикла объясняется тем, что тело цикла (как и тело цикла оператора For...Next) может содержать оператор Exit Do, позволяющий завершить цикл принудительно. Рассмотрим опять в качестве примера задачу нахождения макси- мального делителя (пример 3 предыдущего пункта). Алгоритм решения задачи с использованием оператора Do.,.Loop выглядит следующим образом: С проверкой условия до выпол- нения тела цикла С проверкой условия после выполнения тела цикла С использова- нием оператора Exit Do Условие While Условие Until Условие While Условие Until D = 0 i =k\2 Do While D = 0 If k mod i = 0 Then D = i End If i = i- 1 Loop D = 0 i = k\2 Do Until D о 0 If k mod i = 0 Then D = i End If i = i- 1 Loop D = 0 i = k\2 Do If k mod i = 0 Then D = i End If i = i- 1 Loop While D = 0 D = 0 i = k\2 Do If k mod i = 0 Then D = i End If i = i- 1 Loop Until DoO D = 0 i = k\2 Do lfkmodi = 0 Then D = i: Exit Do End If i = i-l Loop Оператор While... Wend По результату действия оператор аналогичен оператору While язы- ка Pascal. Синтаксис оператора следующий: While <условие> [<тело цикла>] Wend Рассмотрим следующий пример. 1. Член ряда с номером п для п = 1,2,3,... определяется выражением — (х > 1). Написать последовательность операторов для вычисления пх суммы членов ряда от первого до (включительно) члена с наименьшим номером, не превосходящего КГ6. S = О N = 1 SN = 1 'Задание начального значения 'для члена ряда (больше, чем 10~6) 278
While SN > 10л(-6) SN = 1/ ЬГХ S = S + SN N = N + 1 Wend И 4.6. Процедуры и функции В языке Basic (так же как и в языке Pascal) существуют две разно- видности подпрограмм — процедуры и функции. Каждое объявление процедуры или функции содержит обязательный заголовок, за которым следует последовательность операторов и обязательный признак завер- шения подпрограммы. Передача управления в подпрограмму происходит с помощью опе- ратора процедуры или функции. В отличие от языка Pascal, процедуру (функцию) в языке Basic нельзя объявить внутри другой процедуры (функции). Объявление процедуры Обобщенный синтаксис объявления процедур в Basic следующий: [Private | Public] [Static] Sub имя_процедуры [(список_параметров)} [операторы} [Exit Sub] [операторы] End Sub Необязательные операторы Private и Public имеют смысл для вер- сий расширения Visual Basic: • оператор Public делает процедуру доступной для использования не только модулем, в котором она объявлена, но и всеми другим мо- дулями; • оператор Private служит для объявления процедуры, доступной для использования только текущему модулю (и его процедурам). Необязательный оператор Static позволяет сохранять значения ло- кальных переменных процедуры между ее вызовами. Если предложение Static не используется, значения локальных переменных не сохраняются. Рассмотрим простой пример объявления процедуры: Sub ExampleProc VariableA = VariableA + 1 Print VariableA End Sub 279
Каждый раз при вызове процедуры с помощью оператора ExampleProc будет выводиться на экран число 1. Если же модифициро- вать объявление процедуры следующим образом: Static Sub ExampleProc VariableA = VariableA + 1 Print VariableA End Sub то всякий раз при вызове процедуры на экран будет выводиться значе- ние, увеличенное на единицу (1, 2, 3 и т.д.). Оператор Exit Sub в теле процедуры может встречаться произволь- ное число раз и всякий раз вызывает немедленное завершение процедуры. Оператор вызова процедуры имеет следующий синтаксис: имяпроцедуры [список_параметров] Необязательный список фактических параметров (через запятую) приводится после имени процедуры без круглых скобок (в качестве раз- делителя имени и списка параметров выступает знак пробела): Proc Pari, Раг2, РагЗ Объявление функций Синтаксис объявления функции следующий: [Public | Private] [Static] Function имяфункции [(список_параметров)] [As тип] [операторы] [имяфункции = выражение] [Exit Function] [операторы] [имяфункции = выражение] End Function В отличие от синтаксиса объявления процедуры объявление функ- ции может содержать в заголовке явное указание типа возвращаемого значения (предложение As тип). Использование операторов Public, Private и Static аналогично ис- пользованию этих операторов при объявлении процедуры. Действие оператора Exit Function аналогично действию оператора Exit Sub. Возвращаемое функцией значение должно быть представлено вы- ражением в правой части оператора присваивания (имя_функции = вы- ражение) Приведем пример объявления функции вычисления квадратного корня действительного числа: 280
Function CalculateSquareRoot (Arg As Double) As Double If Arg < 0 Then ' Проверка параметра Exit Function ' Завершение функции, ' если параметр отрицательный Else CalculateSquareRoot = Sqr(Arg) ' Вычисление возвращаемого ' значения End If End Function Оператор вызова функции отличается от оператора вызова проце- дуры тем, что список фактических параметров при вызове функции за- ключается в круглые скобки: С = CalculateSquareRoot (Arg) Объявление параметров Процедуры и функции могут использовать параметры, список ко- торых (при необходимости с указанием типа), размещают в скобках по- сле имени подпрограммы, например: Sub Factorial (N As Integer) End Sub При необходимости в объявлении указывается тип данных для па- раметров. В приведенном выше примере параметр N имеет тип Integer. В версиях языка Visual Basic появилась возможность использовать обе категории вызова параметров: по значению и по наименованию. Обобщен- ный синтаксис объявления параметра в списке параметров следующий: [Optional] [ByVai | ByRef] имяпараметра [As тип параметра] [= значение_по_умол чанию] Передача параметров по значению (параметры-значения) Для передачи параметров-значений перед именем параметра в заго- ловке процедуры следует указывать ключевое слово ByVai. В этом случае процедуре передается копия этого значения. При передаче параметров- значений ключевое слово ByVai должно указываться обязательно'. Function Factorial (ByVai N As Integer) As Integer F = 1 For i = 2 to N F = F * i Next i Factorial = F End Function 281
Передача параметров по наименованию (параметры-переменные) Для того чтобы передать параметр-переменную, следует перед па- раметром в объявлении указать ключевое слово ByRef. По умолчанию (т.е. без указания способа передачи) параметры в Visual Basic передают- ся по наименованию. Если параметр передается по наименованию, то вызванная проце- дура получает физический адрес памяти передаваемой переменной и может изменять значение этого параметра. Запишем алгоритм вычисления факториала в виде процедуры: Sub Factorial (N As Integer, ByRef F As Integer) F = 1 For i = 2 to N F = F * i Next i End Sub После вызова и выполнения процедуры с помощью оператора про- цедуры: Factorial 5, F в переменной F будет значение 5!. Необязательные параметры Если при вызове процедуры в качестве фактических параметров указать не все формальные параметры, то последует сообщение об ошибке. Однако в Visual Basic существует возможность в заголовке подпрограммы объявить так называемые необязательные параметры (т.е. параметры, которые при вызове подпрограммы можно не указывать в списке фактических параметров). Для того чтобы параметр стал необязательным, перед его именем ставится ключевое слово Optional. Для параметров типа Optional (и только для них) можно указать значение по умолчанию (например, Optional Param = 5). После первого необязательного параметра все по- следующие должны быть также необязательными, например: Sub SomeProc (Pari, Optional Par2 = "Two", Optional РагЗ = "Three") Print Pari, Par2, РагЗ 'вывод значений параметров на экран End Sub Рассмотрим теперь следующие операторы вызова процедуры: 282
SomeProc "One" SomeProc "One","And" SomeProc "А","В","C" В результате выполнения первого оператора на экране появится значение " One Two Three", в результате второго — "One And Three" и в результате третьего — "А В С". 4.7. Организация ввода-вывода. Работа с файлами Для работы с файлами в Basic определено понятие канала ввода- вывода. При открытии файлу ставится в соответствие канал с опреде- ленным номером. Таким образом, каждый открытый файл имеет собст- венный канал, с помощью которого записываются или считываются данные. В операциях ввода и вывода данных для задания связи с физи- ческим файлом используется номер канала. В Basic реализованы три типа доступа к файлам: • последовательный (Sequential) — для чтения и записи тексто- вых файлов; • произвольный (Random) — для чтения и записи структуриро- ванных файлов с записями фиксированной длины (аналогично типизированным файлам в Pascal); • двоичный (Binary) — для чтения и записи неструктурирован- ных файлов (аналогично нетипизированным файлам в Pascal). При создании коммуникационных каналов система должна знать, какой тип доступа к каждому конкретному файлу нужно использовать и какова структура данных этого файла. Общий алгоритм работы с внешними файлами состоит из несколь- ких этапов: • определение номера канала ввода-вывода; • открытие файла; • чтение или запись данных; • закрытие файла. Определение номера канала ввода-вывода Номер свободного канала, который можно использовать для рабо- ты с файлом, вычисляется с помощью функции FreeFile: FreeFile [(область_изменения)] 283
Необязательный параметр область .изменения позволяет опреде- лить диапазон значений, из которого выбирается очередной свободный номер канала. Если его значение равно 0 (по умолчанию), то возвраща- ется номер канала из диапазона 1—255, если 1, то из диапазона 256-511, например: FInput = FreeFile(l) ’ В переменную FInput занесется ' номер свободного канала ' из диапазона 256-511 Открытие файла Связь внешнего файла с выбранным каналом осуществляется с по- мощью оператора открытия файла, общий синтаксис которого (для всех типов доступа) следующий: Open идфайла For тип [Access доступ] [блокировка] As [#]номер_канала [Ьеп=длина_записи] Здесь: ид_файла — полный идентификатор внешнего файла (например, C:\EXAMPLE\DATE.DAT); тип — ключевое слово, специфицирующее тип доступа к файлу (Append, Input, Output — для последовательного доступа, Binary — для двоичного и Random — для произвольного доступа); доступ -- необязательное указание уровня доступа к файлу (Read — только чтение, Write — только запись, Read Write — чтение-запись; по умолчанию устанавливается уровень Read Write); блокировка — необязательный параметр, определяющий возмож- ность совместного использования файла несколькими процессами (Shared — разделенный доступ; Lock Read, Lock Write, Lock Read Write — локальный доступ); номер_канала — номер выделенного канала ввода-вывода, знак «#» ставится при числовом константном значении (#1); длина_записи — необязательное числовое значение (меньше либо равное 32767 байтам), задающее длину записи для файлов произвольно- го (random) доступа или размер текстового буфера для текстовых фай- лов последовательного доступа (для файлов двоичного доступа пара- метр игнорируется). В случае работы с файлом последовательного доступа ключевое слово типа доступа одновременно задает и уровень доступа к данным: Input — только чтение из файла; Output — запись в файл; Append — добавление к файлу. При попытке открытия для чтения (input) несуществующего файла 284
выдается сообщение об ошибке. При открытии несуществующего файла для записи или добавления (Output или Append) создается новый файл. Если файл с указанным именем существует, то в режиме Output его со- держимое удаляется, а в режиме Append файл открывается для добавле- ния, например: Open "C:\EXAMPLE\USERS.TXT" For Append As #3 С помощью такого оператора канал ввода-вывода под номером 3 связывается с размещенным на диске файлом C:\EXAMPLE\USERS.TXT для добавления информации. Чтение (запись) данных Процессы чтения-записи при работе с файлами различного типа доступа управляются разными процедурами. Считывание данных из файла, открытого для последовательного или двоичного доступа, осуществляется с помощью оператора Input: Input #номер_канала, список_переменных Список переменных содержит разделенные запятой имена перемен- ных, в которые заносятся значения, последовательно считываемые из файла. В списке запрещено использовать переменные типа массив или объект. Элементы данных в файле должны быть записаны в том же поряд- ке, в котором запрашивается их чтение. Двойные кавычки (" ") внутри вводимых данных игнорируются. Считывание данных из текстового файла, открытого для последо- вательного доступа, может быть проведено также с помощью оператора Line Input: Line Input #номер_канала, строковая_переменная В строковую переменную заносится последовательность символов текстового файла до признака конца строки (код возврата каретки — Chr(13) или возврат каретки — перевод строки — Chr(13) + Chr(10)). Признаки конца строки пропускаются и следующее считывание из фай- ла осуществляется с начала следующей строки. Запись данных в файл последовательного доступа должна происхо- дить с использованием операторов Print и Write. Синтаксис оператора Print: Print #номер_канала, [списоквывода] Списоквывода — необязательный параметр, который задает выраже- 285
ние (или список выражений) для вывода в файл последовательного досту- па. Если параметр отсутствует, в файл выводится признак перевода строки. В список вывода, помимо выражений, могут входить следующие параметры: Spc(n) — задает вывод последовательности символов «пробел» длиной п; Tab(n) — задает позицию вывода следующих данных (п — позиция от начала строки; при использовании Tab без параметра очередной вы- вод начинается с начала следующей зоны). Для форматирования записываемой в файл информации следует по- разному разделять выражения в операторе Print. Если выводные данные в операторе разделять запятыми, то в файле они будут разделены символами табуляции. Если же в операторе для разделения данных использовать точку с запятой, то данные в файл записываются без разделителей: Данные, записанные в файл с помощью оператора Print, должны быть прочитаны из файла с помощью операторов Input или Line Input. Синтаксис оператора Write: Write #номер_канала, [список_вывода] Список_вывода — необязательный параметр, который задает вы- ражение (или список выражений) для вывода в файл последовательного доступа. Выражения в списке могут разделяться запятой, пробелом или двоеточием. Если параметр отсутствует, в файл выводится признак пе- ревода строки. Данные, записанные в файл с помощью оператора Write, должны быть прочитаны из файла с помощью оператора Input. Для считывания данных из файла произвольного или двоичного доступа используется оператор Get: Get [#\номер_канала, [количество_записей], имя переменной В соответствии с синтаксисом оператора из файла считывается ука- занное количество записей (при произвольном доступе) или байтов (при двоичном доступе) и помещается в переменную. Нумерация записей (байтов) внутри файла начинается с единицы. Если число записей (бай- тов) не указано, считывается одна запись (или один байт). Разделитель параметров при этом в операторе должен присутствовать: Get #2 , , RecordBuffer Запись данных в файл произвольного или двоичного доступа осуще- ствляется с помощью оператора Put, синтаксис которого сходен с син- таксисом оператора Get: 286
Put [#]номер_канала, [количествозаписей], имя_переменной Чтение данных из файла с помощью оператора Get предполагает, что запись данных в файл проводилась с использованием оператора Put, и наоборот: данные, записанные в файл посредством оператора Put, должны быть прочитаны оператором Get. Закрытие файла С закрытием файла связано освобождение канала ввода-вывода и, возможно, дальнейшее использование этого канала для связи с другим внешним файлом. Для закрытия файлов служат операторы Close и Reset: Close \список_номеров_каналов] Reset Оператор Close освобождает все перечисленные в списке каналы ввода-вывода. Если закрывается файл последовательного доступа, пред- варительно открытый на запись или добавление (Output или Append), то происходит запись информации и освобождение выделенного про- граммного буфера. Использование оператора Close без параметров при- водит к закрытию всех ранее открытых файлов. Действие оператора Reset аналогично вызову оператора Close без указания параметров. Для работы с клавиатурой как с устройством ввода данных и с эк- раном дисплея как с устройством вывода используются операторы Input и Print без указания первого параметра (номера канала), вследствие чего не требуется использования операторов Open и Close, например: Print "Hello!" На экран будет выведена текстовая строка "Hello!". Некоторые процедуры и функции для работы с файлами В следующей таблице приводятся некоторые дополнительные про- цедуры и функции работы с файлами (для Visual Basic): Наименование Назначение Dir [(образец[, атрибуты])] Возвращает текстовую строку — первое най- денное имя файла с указанными атрибутами, соответствующее образцу 287
Продолжение табл. Наименование Назначение (номер_канала) Возвращает значение True, если достигнут конец файла Fi\еРМх(номер _канала, тип значения) Возвращает тип доступа к файлу FileCopy источник, приемник Копирование файлов: файл с именем источник копируется в файл с именем приемник F i 1 е DateTi те(ид_файла) Возвращает дату создания или послед- ней модификации файла FileLen(ur) файла) Возвращает длину файла в байтах GetAttr(«d файла) Возвращает значение атрибутов файла Input(w, номер канала) Для файлов последовательного или дво- ичного доступа возвращает строку счи- танных символов длиной п. Kill ид файла Удаляет внешний файл с диска Ьос(номер_канала) Возвращает номер текущей позиции в открытом файле (для операций ввода- вывода) Random: номер последней про- читанной (записанной) записи;. SequentiakHOMep текущей позиции в файле (в байтах), деленный на 128; Binary: номер последнего прочитанного (записанного) байта. Lock [#]номер канала [, область записей} Блокирует доступ ко всему файлу или к его части, заданной областью записей ЦХ(номер канала) Возвращает длину открытого файла Name старое имя As новое имя Переименовывает внешний файл $еек(номер_канала) Seek [#}номер_ канала, позиция Возвращает (или устанавливает) номер текущей позиции в открытом файле (для операций ввода-вывода) Random: номер следующей записи для ввода-вывода; Binary, Output, Append, Input: номер байта для следующей операции ввода-вывода. SetAttr ид файла, атрибуты Устанавливает атрибуты файла Unlock [#}номер_ канала [, область_ записей} Снимает блокировку доступа ко всему файлу или к его части, заданной обла- стью записей Width # номер канала, п Задает длину строки для вывода в файл Запишем на ЯП Basic алгоритм решения задачи, рассмотренной в предыдущей главе: пусть на диске в текущей директории есть файл с именем filereal.txt, в котором в нескольких строках записаны последова- тельности действительных чисел в символьном представлении: 288
0.54 1.7 4.56 0.2 1.32 1.524 18 0.92 7.7 Необходимо вычислить сумму чисел и вывести результат на экран. Open "filereal.txt" For Input As #1 while not EoF(l) ' Цикл — до конца файла Input #1, R S=S+R Wend ' Подсчет суммы Close #1 Print "сумма=", S ' Вывод на экран ' значения суммы В результате работы программы на экране появится сообщение: сумма= 92.464 Рассмотрим пример работы с файлом прямого доступа. Пусть не- обходимо формировать и обрабатывать файл, состоящий из записей следующей структуры: TYPE StudentRecord Family AS STRING * 20 Mark AS SINGLE END TYPE Составим программу, позволяющую записывать данные в файл и затем читать содержимое произвольной записи по ее номеру: TYPE StudentRecord Family AS STRING * 20 Mark AS SINGLE END TYPE DIM MyRecord AS StudentRecord ' Объявляем буферную переменную для записи ' в файл ' Открываем (создаем) файл прямого доступа OPEN "STUDENTS.DAT" FOR RANDOM AS #1 LEN = LEN(MyRecord) b = ”Y" ' Флаг завершения ввода в файл WHILE b = "Y” INPUT "Введите фамилию : ", MyRecord.Family INPUT "Введите оценку : ", MyRecord.Mark PUT #1, 1, MyRecord 'Ввод очередной записи INPUT "Продолжить ввод (Y/N)? ", b b = UCASE(b) WEND 289
CLOSE #1 'Закрыть файл OPEN "STUDENTS.DAT" FOR RANDOM AS #1 LEN LEN(MyRecord) INPUT "Введите номер записи : ", 1% ' Ввод номера записи для чтения SEEK #1, 1%-1 ' Установка файлового указателя перед ' нужной записью GET #1, 1, MyRecord 'Чтение I -той записи PRINT "STUDENT:", MyRecord.Family PRINT "SCORE:", MyRecord.Mark CLOSE #1 Программная реализация алгоритмов главы 1 Рассмотрим программную реализацию некоторых алгоритмов, представленных в гл. 1, с использованием языка Basic. 1. Программа нахождения наибольшего общего делителя (НОД) двух целых чисел (алгоритм Евклида): INPUT "Введите первое число : ", А % INPUT "Введите второе число : : ", В% WHILE А% О В% IF А % < В% THEN SWAP А%, В% 'поменять местами А и В А% = А% - В% WEND PRINT "НОД = ", А % 2. Программа формирования ряда Фибоначчи для любого нату- рального N: INPUT "введите натуральное число N : ", N% F% = 1 p% = 0 WHILE F% <= N% PRINT F%; 'вывод очередного члена ряда R% = F% 'сохранение значения очередного 'члена ряда F% = F% + P% 'вычисление следующего значения P% = R% 'восстановление предыдущего 'значения WEND PRINT 290
3. Программа формирования и обработки двумерного массива (матрицы): INPUT "Введите размерность матрицы N : ", N% DIM A(N%, N%) FOR 1% = 1 TO N% 'Вложенные циклы формирования 'элементов матрицы FOR J% = 1 ТО N% A(I%, J%) = (1% + J%) Л 2 NEXT J% NEXT 1% FOR I% = 2 TO N% 'Вложенные циклы алгоритма 'зеркального отображения FOR J% = 1 ТО 1% - 1 A(J%, 1%) = A(I%, J%) NEXT J% NEXT 1% FOR 1% = 1 TO N% 'Цикл обработки элементов 'главной диагонали А(1%, 1%) = О NEXT 1% FOR 1% = 1 ТО N% 'Построчный вывод элементов 'матрицы FOR J% = 1 ТО N% PRINT A(I%, J%), NEXT J% PRINT 'Переход на начало следующей 'строки} NEXT 1% 4. Программа вычисления суммы двух целых чисел, значения кото- рых превышают максимально допустимые для типа INTEGER: INPUT " Введите первое число : ", Nmbl$ 'Ввод чисел как 'последовательностей символов INPUT " Введите второе число : ", Nmb2$ ' LI = LEN(Nmbl$) 'Вычисление длины первого числа L2 = LEN(Nmb2$) 'Вычисление длины второго числа IF LI > L2 THEN L = LI ELSE L = L2 L = L + 1 'Вычисление размерности массивов DIM A(L), B(L), C(L) 'Объявление массивов А, В, С} FOR 1=1 TO L 'Цикл обнуления элементов 'массивов A(I) = О 291
B(I) = о C(I) = о NEXT J = L 'Формирование двоично- 'десятичного представления 'для числа А FOR I = Ll ТО 1 STEP -1 A(J) = VAL(MID$(Nmbl$, I, 1)) - VAL("O") J = J — 1 NEXT J = L 'Формирование двоично- 'десятичного представления 'для числа В FOR I = L2 ТО 1 STEP -1 B(J) = VAL(MID$(Nmb2$, I, 1)) -VAL(”0") J = J — 1 NEXT FOR I = L TO 2 STEP -1 'Основной цикл вычисления 'результата C(I) = С(I) + A(I) + B(I) IF С(I) >= 10 THEN C(I) = C(I) - 10 C(I - 1) = 1 END IF NEXT IF C(l) = 0 THEN J = 2 ELSE J = 1 'Пропуск лидирующего нуля 'перед выводом FOR I = J ТО L Nmb3$ = Nmb3$ + LTRIM$(STR$(С(I) + VAL("0"))) 'Формирования строки- 'результата NEXT PRINT • Сумма = ’, №пЬЗ$ 'Вывод результата 5. Программа вычисления количества слов в последовательности символов: INPUT " Введите фразу : ", А$ N = LEN(A$) 'Вычисление длины фразы L = 0 'Обнуление длины слова NW = 0 'Обнуление счетчика слов FOR 1=1 ТО N 'Основной цикл обработки фразы IF MID$(A$, I, 1) = " " THEN 292
IF L > 0 THEN 'Увеличение счетчика слов и 'обнуление длины слова, 'если перед пробелом слово 'ненулевой длины NW = NW + 1 L = О END IF ELSE L = L + 1 END IF NEXT IF L > 0 THEN NW = NW + 1 'Увеличение счетчика слов, если 'фраза не заканчивается пробелом PRINT "Количество слов = ", NW 6. Программа подсчета количества слов в текстовом файле: 'Объявление функции подсчета 'количества слов в строке DECLARE FUNCTION NumberWords! (line$) INPUT " Введите имя файла : ", FlName$ 'Ввод имени файла OPEN FlName$ FOR INPUT AS #1 ’ NWords = 0 WHILE NOT EOF(l) 'Цикл обработки файла INPUT #1, Fline$ NWords = NWords + NumberWords(Fline$) WEND PRINT "Количество слов в файле = ", NWords CLOSE #1 ELSE L = L + 1 END IF NEXT IF L > 0 THEN NW = NW + 1 NumberWords = NW 'Результат функции — количество 'слов в строке END FUNCTION Некоторые алгоритмы обработки массивов и символьных строк Рассмотрим несколько алгоритмов обработки массивов, формируе- мых с помощью генератора случайных чисел: 1. Расположить элементы массива целых чисел по убыванию коли- чества делителей. В массиве должны быть числа от N до 3*N, где N — размерность массива 293
DIM A%(300), B%(300) 'описание исходного массива A% 'и делителей В% N% = 300 RANDOMIZE(1) FOR I%=1 TO N% 'генерация массива : X=RND A%(1%)=N%+N%*2*X NEXT 1% PRINT "BEFORE SORT" FOR I%=1 TO N% 'проход по элементам массива NDEL%=0 : A1%=A%(I%) FOR J%=2 TO INT(Al%/2)+l 'цикл подсчета количества 'делителей для элемента WK1%=INT(A1%/J%) : WK2%=WK1%*J% IF WK2%=A1% THEN NDEL%=NDEL%+1 NEXT J% B%(1%)=NDEL% NEXT 1% FOR I%=1 TO N% 'сортировка массива B% MIN%=B%(I%): D%=I% 'нахождение минимального 'элемента массива из оставшихся 'элементов FOR J%=I%+1 ТО N% IF B%(J%)<MIN% THEN MIN%=B%(J%) : D%=J% NEXT J% SWAP(A%(I%), A%(D%)) 'перестановка элементов 'массива A% SWAP(B%(I%), B%(D%)) NEXT 1% PRINT "AFTER SORT:" FOR I%=1 TO N% PRINT А%(1%), В%(1%) 'вывод упорядоченных массивов NEXT 1% END 2. В массиве целых чисел удалить те элементы, сумма цифр кото- рых не есть число, кратное 4. В массиве должны быть числа от N до 3*N, где N — размерность массива DIM А%(300) 'описание исходного массива А% N% = 300 RANDOMIZE(1) FOR I%=1 ТО N% X=RND 'генерация массива А% (1%)=N%+N%*2*X NEXT 1% 294
PRIZN%=1 FOR I%=1 TO N% S1$=STR$(A%(I%)) : LN1%=LEN(Sl$) : SUMN%=0 FOR K%=1 TO LN1% S2$=MID$(S1$,K%,1) : SOMN%=SOMN%+VAL(S2$) NEXT K% PRINT A%(I%) SOMN% WK1%=INT(SUMN%/4%) : WK2%=WK1%*4% IF WK2% = SOMN% THEN PRIZN%=0 ELSE A%(I%)=0 NEXT 1% IF PRIZN%=1 THEN PRINT "Нет таких чисел" END 3. Расположить элементы массива целых чисел по возрастанию суммы цифр. Массив сгенерировать с помощью генератора случайных чисел в диапазоне от N до 2*N, где N — размерность массива. INPUT "Введите количество элементов массива А: ", N% DIM А%( N%), В%( N%) 'Описание исходного массива А% 'и массива сумм цифр В% RANDOMIZE(1) FOR 1% = 1 ТО N% 'Генерация массива X = RND А%(1%) = N% + N%*X NEXT 1% FOR 1% = 1 TO N% 'Основной цикл обработки 'элементов массива А% SOMN% =0 : К = А%(1%) WHILE К > 0 'Цикл подсчета суммы цифр S = К MOD 10 : К = К \ 10 SOMN% = SOMN% + S WEND B% (I%)=SUMN% PRINT A% (1%),B%(1%) NEXT 1% FOR I%=1 TO N% ' Сортировка массива B% и А% по В% MIN%=B%(I%): D%=I% FOR J%=I%+1 TO N% 'Поиск минимального элемента 'массива В% из оставшихся IF B%(J%)<MIN% THEN MIN%=B%(J%) : D%=J% NEXT J% SWAP A%(I%), A%(D%) 'Перестановка элементов 'массивов SWAP B% (1%), B%(D%) NEXT 1% 295
PRINT "После сортировки:" 'Вывод упорядоченных массивов FOR I%=1 ТО N% PRINT А%(1%), В% (1%) NEXT 1% END 4. Проверить, можно ли из букв первых 3-х слов фразы (слова фра- зы разделены одним пробелом, а в конце фразы — точка) составить 2 последних слова. INPUT "Введите фразу : ", STR1$ Wl$ = "" IF INSTR(STR1$) = 0 THEN PRINT "Нет точки" : STOP LENS = LEN(STR1$): NW =0 FOR I =1 TO LENS 'Подсчет общего количества слов 'и выделение букв первых трех SYM$ = MID$(STR1$, I, 1) IF (SYM$ =" ") OR (SYM$ THEN NW = NW + 1 IF (NW < 3) AND (SYM$ > " ") THEN W1$=W1$+SYM$ NEXT I IF NW <= 3 THEN PRINT "Да" : STOP NW = 0 : I = LENS - 1 FLAG = 1 WHILE NW < 2 'Цикл выделения последних 'двух слов SYM$ = MID$(STR1$, I, 1) 'Выделение очередной буквы IF SYM$=" " THEN NW = NW + 1 'Если «пробел», то увеличение 'числа слов ELSE 'Если буква не найдена в строке 'Wl$, то установка флага в 0 'и установка признака конца 'цикла выделения слов IF INSTR(Wl$, SYM$) = 0 THEN FLAG = 0 : NW = 2 ELSE IF 1 = 1-1 WEND IF FLAG = 0 THEN PRINT "Нет" ELSE PRINT "Да" END 5. Удалить из строки, содержащей некоторую фразу (слова, разде- ленные одним пробелом, а в конце — точка) слова, содержащие менее двух гласных букв латинского алфавита. 296
INPUT "Введите фразу : NEWSTR$="" GLAS$="EYUIOAeyuioa" LENS = LEN(STR1$) IF INSTR(STR1$, ".") > Wl$="" NS = 0 FOR I%=1 ТО LENS SYM$=MID$(STR1$,1%,1) IF SYM$ > " " THEN IF INSTR(GLAS$, SYM$) W1$=W1$+SYM$ ELSE ", STR1$ 'Новая фраза 0 THEN LENS = LENS - 1 'Очередное слово 'Количество гласных букв в 'очередном слове 'Основной цикл обработки фразы 'Выделение очередной буквы 'Если не пробел: > О THEN NS = NS + 1 'Увеличение счетчика гласных 'Формирование очередного слова 'Если пробел IF NS >= 2 THEN NEWSTR$ = NEWSTR$ + Wl$+ " " 'Добавление слова в новую фразу NS = 0 : Wl$ = "" 'Обнуление счетчика гласных и 'текущего слова END IF NEXT 1% IF NS >= 2 THEN NEWSTR$ = NEWSTR$ + Wl$ + " " 'Проверка последнего слова фразы NEWSTR$ = RTRIM$(NEWSTR$) + «.» 'Точка в конце новой фразы PRINT «Новая фраза : « + NEWSTR$ END 6. Во фразе найти слово, содержащее максимальное количество со- гласных букв латинского алфавита и напечатать его заглавными симво- лами. INPUT "Введите фразу : ", STR1$ NEWSTR$="" 'Новая фраза SOGLAS$="QWRTPSDFGHJKLZXCVBNMqwrtpsdfghjklzxcvbnm" LENS = LEN(STR1$) IF INSTR(STR1$,".") > Wl$="" NS = 0 MAX = 0: MAXW$ = 0 THEN LENS = LENS - 1 'Очередное слово 'Количество согласных букв в 'очередном слове 'Переменные для поиска слова с 'максимальным количеством 'согласных 'Основной цикл обработки фразы 'Выделение очередной буквы 'Если не пробел: > О THEN NS = NS + 1 FOR I%=1 TO LENS SYM$=MID$(STR1$, 1%, 1) IF SYM$ > " " THEN IF INSTR(SOGLAS$, SYM$) 297
W1$=W1$+SYM$ ELSE IF NS > MAX THEN MAX = иск слова NS = 0 : Wl$ = "" 'Увеличение счетчика согласных 'Формирование очередного слова 'Если пробел NS: MAXW$ = Wl$ 'По- 'Обнуление счетчика согласных и 'текущего слова END IF NEXT 1% PRINT "Искомое слово : " + UCASE$(MAXW$) END Упражнения 1. Изменить простейшую диалоговую программу, приведенную в начале раз- дела таким образом, чтобы на сообщение «настроение плохое» она бы от- вечала: «а у меня — хорошее», и наоборот. При вводе других значений программа должна отвечать — «не поняла!» 2. Изменить программу п.1 таким образом, чтобы на любые другие значения nastr (кроме «хорошее» и «плохое») программа отвечала бы: «у меня то- же nastr». 3. Используя вызов таймера, изменить простейшую диалоговую программу, приведенную в начале раздела таким образом, чтобы она приветствовала пользователя словами доброе утро, добрый день, добрый вечер, соответст- венно с 8:00 до 12:00, с 12:00 до 17:00 и с 17:00 до 22:00. 4. Одномерный массив размерности N с помощью генератора случайных чи- сел заполняется числами из диапазона [10000, 99999]. Найти 5-значное число, которое в 45 раз больше произведения его цифр. Если в массиве нет таких чисел, напечатать сообщение. 5. Заданы 2 массива N*M и M*N. Поменять местами строки и столбцы в 1-м массиве и сложить элементы полученных массивов. 6. Задана текстовая строка, содержащая слова, разделенные пробелами. В конце каждой фразы стоит точка с пробелом. Можно ли из последних 3-х слов 1 -й фразы составить 3 первых слова 4-й фразы. 7. Задана текстовая строка, содержащая слова, разделенные пробелами. В конце каждой фразы стоит точка с пробелом. Найти самое длинное (корот- кое) слово 1 -й (2-й) фразы и заключить его в скобки (кавычки). 8. Задана текстовая строка, содержащая слова, разделенные пробелами. В конце каждой фразы стоит точка с пробелом. Найти слово, содержащее бо- лее всего (менее всего) гласных (согласных) букв и напечатать его заглав- ными символами. 298
Глава 5. Интегрированная среда разработки приложений VISUAL BASIC В данной главе дается краткое представление об основных возможностях и порядке проектирования приложений в среде Visual Basi<Sa3OBbiM языком среды является язык программирования Visual Basic (расширенная версия языка Basic). Такие понятия, как «Форма», «Компонент», «Свойства компонен- та», «Событие» и «Процедура-обработчик события» более подробно рассмотрены в гл. 3 и являются общими для представленных в пособии интегрированных сред разработки приложений. Гл. 3 и 5 имеют схожую структуру и предлагают в качестве приме- ра программу «Калькулятор» с одинаковыми возможностями. И 5.1. Интерфейс среды После запуска Visual Basic на экране появляется диалоговое окно, в котором можно выбрать тип создаваемого приложения (рис. 5.1). Далее в примерах и рисунках будет представлена пятая версия программного продукта — Visual Basic 5.0 Рис. 5.1. Диалоговое окно запуска Visual Basic 4.0 299
Рис. 5.2. Среда разработки Visual Basic За некоторыми пиктограммами диалогового окна стоят мастера (Wizards), сопровождающие разработчика при создании приложений и берущие на себя часть его работы (например, подключение базы данных или создание формы). Один из основных мастеров — мастер приложе- ния Visual Basic, с помощью которого можно создать основной «каркас» для обычных Windows-приложений. В процессе работы мастера создается почти готовое приложение с различными формами, соответствующей рабочей средой, меню, пане- лью инструментов и т.п. Это приложение можно потом совершенство- вать и настраивать. С помощью пиктограммы Standard EXE инициируется создание фор- мы, представляющей собой окно обычного Windows-приложения (рис. 5.2). Главное окно среды разработчика содержит несколько окон (начи- ная с версии Visual Basic 5.0, все окна подчиняются главному окну Visual Basic): • главное меню и панель инструментов (Toolbar); • панель элементов (Toolbox); • окно формы; • окно свойств (Properties); 300
окно проекта; окно кода. Главное меню и панель инструментов В верхней части экрана находится центр управления средой — Главное меню и панель инструментов (Toolbar). Состав панели инстру- ментов можно настраивать с помощью команды Главного меню View/Toolbars. Панель элементов Панель элементов (Toolbox) на рис. 5.2 расположена в левой части экрана и содержит пиктограммы элементов управления — кнопок, пе- реключателей, полей ввода и др. Для выбора элемента управления не- обходимо выбрать его пиктограмму на панели элементов (с помощью манипулятора-мыши) и затем установить его на области формы, задав позицию его размещения и размеры. После двойного щелчка левой кла- виши мыши на пиктограмме соответствующий элемент появляется в центре формы и имеет стандартный размер. Окно формы Окно формы, часто называемое просто «форма», является главным окном разрабатываемого приложения. Форма представляет собой кон- тейнер для размещения компонент, составляющих интерфейс приложения. При запуске Visual Basic открывающаяся на экране форма не со- держит компонентов и имеет стандартный заголовок Forml. Требуемый элемент управления помещается на форму с помощью панели элемен- тов: после щелчка на пиктограмме курсор мыши принимает форму кре- стика; далее необходимо указать в форме начальный угол элемента управления, нажать левую кнопку мыши и, не отпуская ее, установить размер элемента. На рис. 5.3 изображено окно формы с размещенными на нем окном ввода (Textl), переключателем (Optionl) и управляющей кнопкой (Command 1). Окно свойств (Properties) Окно служит для задания свойств компонент, размещенных на форме. В строке заголовка окна свойств рядом с текстом Properties ука- зывается имя формы, которой принадлежит компонент. Поле со спи- ском под строкой заголовка позволяет выбрать требуемый компонент. В списке, расположенном ниже, перечислены свойства этого компонента (в алфавитном порядке — Alphabetic либо по категориям — 301
Рис. 5.3. Окно формы с элементами управления Categorized). Набор свойств связан с классом, которому принадлежит компонент. Список свойств состоит из двух столбцов: в левом перечислены названия свойств, а в правом — их значения. Редактирование свой- ства осуществляется либо вручную (путем непосредственного ввода с клавиатуры значения свойства), либо посредством выбора соответ- ствующего значения из раскрывающегося списка, либо при помощи диалогового окна настройки свойства. На рис. 5.4 представлено окно свойств формы, в котором проведено редактирование свойств Name — имя формы (новое значение MyForm отображается в окне свойств и в заголовке главного окна), Caption — заголовок формы (новое значе- ние «Моя учебная форма» отображается в заголовке формы) и вы- звано диалоговое окно для изменения свойства Font (установка шрифта для формы). Окно проекта В окне проекта отображаются все составляющие разрабатываемого приложения (проекта): формы, модули, классы и т.п., сгруппированные по категориям. Все приложения Visual Basic строятся по модульному принципу, поэтому и объектный код состоит не из одного большого файла, а из нескольких частей. Несколько приложений также могут объ- единяться в группы. Через окно проекта осуществляется вставка-удаление составляю- щих элементов проекта и сохранение их в виде файлов. Чтобы добавить в проект новый элемент, необходимо вызвать одну из команд Project/Add.... Для удаления элемента нужно выделить его в окне проекта и затем выбрать команду меню Project/Remove <Имя элемента>. 302
Рис. 5.4. Редактирование свойств формы (Project - Project 1 ЁЗ] □ s ;[ь_ _________________________________ в projecltl~~ Й-^S Forms iMyForm (MyForrn) Modules I !--<Л MyModule (MyModule) Class Modules • MyClass (MyClass) й-з&Э Property Pages -•IB MyPropertyPage (MyPropertyPage) Рис. 5.5. Окно проекта Чтобы сохранить форму, модуль и др., нужно выделить соответст- вующую строку в списке окна проекта и выбрать команду Главного ме- ню File/Save <Имя элемента> или File/Save <Имя элемента> As. Все эти элементы сохраняются как отдельные и независимые файлы, что дает возможность использовать в проекте формы и коды, созданные, например, для других проектов. 303
Для записи всего проекта (включая все составляющие) служит ко- манда File/Save Project или File/Save Project As (для сохранения про- екта под другим именем). Содержимое окна проекта сохраняется в специальном файле. Он имеет расширение VBP и содержит список элементов, которые нужно загрузить в среду разработки. Если несколько проектов объединяются в группу, их имена сохраняются в файле с расширением VBG. Окно кода Окно кода служит для ввода и отображения программного кода — совокупности глобальных определений и процедур для каждого элемен- та проекта. В текущий момент в окне кода отображается программный код того элемента проекта, который выделен в окне проекта. Программ- ный код в окно кода загружается либо после двойного щелчка правой клавиши мыши на строке имени элемента проекта в окне проекта, либо посредством команды главного меню View/Code. В верхней части окна кода расположены два раскрывающегося списка — список объектов и список событий/процедур. В списке объ- ектов, расположенном слева, отображается имя составляющего элемен- та проекта, программный код которого находится в настоящее время в окне. Внутри списка (для формы) перечислены элементы управления формы. Список событий/процедур отображает либо перечень всех воз- можных событий элемента управления (в случае формы), либо перечень процедур (в случае модуля). На рис. 5.6 представлено окно кода для модуля класса. Приемы редактирования текста в окне кода такие же, как и при ре- дактировании текстов в приложениях Windows. Над вертикальной полосой прокрутки находится специальное поле (split window), которое можно перетаскивать с помощью мыши вниз для разделения окна на две части. Это дает возможность редактировать в одном окне две разные процедуры. Разделение отменяется, если разде- лительную линию переместить к самому краю окна или выполнить двойной щелчок на разделительной линии. 5.2. Характеристика проекта Проект Visual Basic в общем случае состоит из форм, модулей, классов и ресурсов. Все эти элементы объявляются в едином файле про- екта Visual Basic (файл с расширением vbp). 304
| Class Initialize n Private Sub Class_Initialize() plrgl = 0 Arg2 e 0 LastOper « False Oper - ”n End Sub Public Property Let SetArgl(ByVai vNewValue As String) Argl ® Val(vNewValue) End Property Public Property Let SetArgZ(ByVai vNewValue As String) Arg2 = Val(vNewValue) End Property End Property Public Property Let FillLastOper(ByVai vNewValue As Boolean) LastOper - vNewValue End Property Public Property Get SignOper() As String SignOper = Oper End Property ж Public Property Let SignOper(ByVai vNewValue As String) Oper = vNewValue End Property____ Рис. 5.6. Окно кода Приложения Visual Basic строятся по модульному принципу, т.е. могут состоять из различных элементов. Все составляющие проекта хранятся в памяти отдельно и независимо друг от друга. Информация о составляющих проекта и связях между ними хранится в файле проекта. Сам отдельный элемент (например, форма) не связан с другими элемен- тами проекта, поэтому может быть включен и в другие проекты. В состав проекта Visual Basic могут входить следующие группы файлов: • формы (файлы с расширением FRM, содержащие перечень компо- нентов формы с их свойствами и процедурами обработки событий); • модули классов (файлы с расширением CLS, содержащие описание пользовательских классов и наборов разработанных для них свойств и методов); • модули Basic (файлы с расширением BAS, содержащие наборы процедур на языке Basic); • файл проекта Visual Basic (файл с расширением VBP, содержащий описание состава проекта и параметров среды); 305
файл Группы проекта (файл с расширением VBG, содержащий описание группы проекта); справочный файл Windows (файл справки с расширением HLP, присоединяемый к проекту); библиотека динамической компоновки (файлы с расширением DLL); файлы дополнительных элементов управления (файлы с расшире- нием OCX, CTL); файлы рисунков и пиктограмм (файлы с расширением BMP и ICO) И др. 5.3. Компиляция и выполнение проекта Чтобы программа Visual Basic могла выполняться не только в среде Visual Basic, нужно ее откомпилировать. Для компиляции предназначе- на команда меню File/Make. После компиляции и сборки создается файл проекта с расширением EXE, который может быть запущен из среды Windows так же, как и любое другое приложение. В среде разра- ботки Visual Basic приложение запускается с помощью команды меню Run/Start, нажатием клавиши <F5> или кнопки [Start], Если во время выполнения программы, запущенной из среды раз- работки, возникла необрабатываемая ошибка, то среда предлагает пе- рейти в режим отладки приложения. При этом выдается сообщение об ошибке с возможностью выбора одного из вариантов: завершение вы- полнения или переход в режим отладки. Возможности среды по запуску приложения объединены в группу команд главного меню Run. Набор команд меню Run и назначение многих кнопок панели инст- рументов зависит от состояния среды разработки. В режиме проектиро- вания приложение можно только запустить, все же остальные возмож- ности недоступны. При запуске можно выбрать один из двух вариантов: • запуск без полной компиляции; • запуск с полной компиляцией всех процедур. Переход в режим отладки выполняется нажатием клавиш <Ctrl+Break> или щелчком на кнопке [Break]. В режиме отладки можно выбирать один из вариантов: • продолжать выполнение программы; • перейти в режим разработки. Продолжить выполнение можно, нажав повторно клавишу <F5> или щелкнув на кнопке [Continue] (в режиме отладки название «Continue» носит кнопка [Start]). 306
Название текущего режима отображается в квадратных скобках в строке заголовка Visual Basic. Инструменты поиска ошибок объединены в меню Debug. Восполь- зовавшись командой Toolbars меню View, можно отобразить панель инструментов Debug для доступа к основным средствам отладки. Система отладки предусматривает следующие действия: • включение и выключение точек останова; • выполнение приложения до точки останова; • задание следующей строки выполнения кода (следующий опера- тор); • пошаговое выполнение приложения; • выполнение программы до указанной строки кода; • отображение значений данных в окнах просмотра; • установка новых значений данных при выполнении приложения. Точка останова — это выделенная строка программы, на которой автоматически останавливается выполнение программы. По достиже- нии этой строки Visual Basic переходит в режим отладки. Строка с точ- кой останова выделяется красным цветом и жирной красной точкой на полосе индикатора (серая полоса в левой части окна программного ко- да) (рис. 5.7). Точки останова можно поместить в любой строке кода, Рис. 5.7. Точка останова и следующий оператор 307
включая заголовок процедуры (Sub/Function/Property) и строку End. Точки останова нельзя установить только в строках комментариев или пустых строках. На панели инструментов Debug находится кнопка [Toggle Breakpoint], позволяющая установить или удалить точку останова на текущей строке. Это можно сделать также нажатием клавиши <F9>. Следующий оператор В режиме отладки Visual Basic особым образом выделяет строку, которая должна выполняться следующей. Сама строка выделяется жел- тым цветом, а на полосе индикатора рядом с ней появляется желтая стрелка. Перемещая желтую стрелку, можно изменять последователь- ность выполнения строк кода. Текущая строка выполнения появляется в поле зрения в окне кода с помощью команды меню Debug/Show Next Statement. Пошаговое выполнение программы Пошаговое выполнение является важным средством поиска ошибок и отладки программы. В таком режиме можно непосредственно наблю- дать за результатами выполнения каждой строки. Существует несколько различных команд пошагового выполнения: • шаг с заходом (команда Step into) позволяет выполнить оператор или, в случае если текущий оператор является оператором вызова процедуры или функции не только выполнить соответствующий оператор, но и дает возможность перейти в эту процедуру; • шаг с обходом (команда Step Over) подобен шагу с заходом, но если при шаге с заходом осуществляется переход в вызываемую процедуру, то шаг с обходом выполняет вызов процедуры как еди- ничный оператор, т.е. без захода; • шаг с выходом (команда Step Out) позволяет выполнить остав- шуюся часть текущей процедуры и возвратиться в точку вызова. Команды пошагового выполнения можно вызвать из меню Debug либо из панели инструментов Debug. Выполнение до указанной строки кода Команда Run То Cursor меню Debug позволяет выполнить про- грамму от текущей выполняемой строки до строки с текстовым курсо- ром. Если текстовый курсор находится в выполняемой строке, то ре- зультат выполнения этой команды будет таким же, что и команды Continue. Для вызова команды Run То Cursor используется также ком- бинация клавиш <Ctrl+F8>. 308
End Sub Ф Project 1 - Microsoft Visual Basic [break] - [Forml (Code)] Private Sub SB_Eq_Click () With Ope_Resu.lt If .SignOper <> **" Then .SetArg2 » ArgMemo.Text |ArgMemo.Text ° "З.УЬНепю. Tqxt End If .SetArgl » 0 .SetArg2 “ 0 .SignOper ” nn .FillLastOper = True End With Expression Q: ArgMemo Conitnd Project 1 (VB_Cafc.vbp) Forms a-S Class Modules -Ш MyCtass (MyCass.ds); ObiecVTextBox Forml SB_Ec;_a<k Рис. 5.8. Значения переменной в режиме отладки |SB_Eq Г'|,ск ArgHemo.Text - .OperationResul Отображение значений Кроме контроля хода выполнения программы важной задачей ин- струментов отладки Visual Basic является проверка значений выраже- ний. Для реализации механизма просмотра (watch) Visual Basic предла- гает несколько способов: • использование окна Data Tips (для открытия этого окна достаточно установить курсор мыши на соответствующем выделенном выра- жении в окне кода или на имени переменной); • использование команды Quickinfo (команда группы Edit, которая позволяет отобразить синтаксис для переменной, функции, опера- тора, метода или процедуры, выбираемых в окне кода); • использование команды Parameter Info (команда группы Edit, ко- торая позволяет получить информацию о параметрах используемой функции или оператора); • использование окна Quick Watch (вызывается с помощью команды Quick Watch меню Debug и отображает значение переменной, внутри имени которой находится текстовый курсор); • использование диалогового окна Add Watch (вызывается с помо- щью команды Add Watch... меню Debug и позволяет не только до- бавить нужное выражение в окно просмотра, но и определить до- полнительные параметры просмотра и выполнения программы). 309
На рис. 5.8 демонстрируется отображение значения переменной ArgMemo.Text в окне Data Tips (под точкой останова) и в окне Watches (в нижней части рабочего окна среды) в результате использования диа- логового окна Add Watch. Редактирование контрольного значения Редактирование значений переменных проводится с помощью коман- ды Edit Watch... меню Debug. В результате выполнения этой команды ото- бражается диалоговое окно Edit Watch, похожее на окно Add Watch. Окна режима отладки Итак, среда разработки Visual Basic предоставляет разработчику три окна отладки программы: • Окно контрольного значения (Watch Window) отображает список контролируемых выражений и их текущие значения; • Окно отладки (ImmediateWindow) позволяет выполнять одностроч- ные операторы. • В окне локальных переменных (Locals Window) отображаются все объявленные переменные и их значения текущей процедуры. На рис. 5.9 изображены все три окна отладки. Результат немедлен- ного выполнения оператора присваивания в окне Immediate (ArgMemo.Text = "2.5"+"2") сразу же отображается в окне Watch ("2.52"). Рис. 5.9. Окна режима отладки 310
Важной особенностью режима отладки среды Visual Basic являет- ся возможность интерактивного внесения изменений в программный код проекта и продолжение выполнения программы с учетом внесенных изменений. 5.4. Разработка приложения Рассмотрим процесс создания простейшего приложения на приме- ре, приведенном в п. 3.2. Будем проектировать приложение, содержащее окно для редактирования строки текста и кнопку, по нажатию на кото- рую в качестве подтверждения работы приложения в окно редактирова- ния будет выведен текст: «Ура!!! Приложение работает!!!». Выбрав пиктограмму Standard EXE в диалоговом окне New Project (окно вызывается либо при старте среды, либо с помощью команды ме- ню File/New Project), приступим к самостоятельному проектированию интерфейса приложения. Разместим на форме элемент управления редактированием строки класса TextBox (рис. 5.10). По умолчанию элементу будет присвоено Рис. 5.10. Размещение на форме компонента Textl 311
имя Textl (следующий компонент этого же класса получит имя Text2 и т.д.). Изменим свойство Text в окне свойств, задав ему пустое значение, и установим с помощью диалогового окна «Шрифт» значение размера шрифта, равное 12. Следующий компонент приложения — кнопка. Разместим на фор- ме кнопку класса CommandButton. На форме компонент получит имя Command!. Изменим свойство кнопки Caption (Заголовок), установив значение «Проверка работы приложения». Размещение интерфейсных элементов управления на форме завер- шено. Далее следует приступить к разработке процедур обработки собы- тий. Для ввода программного кода необходимо перейти в Окно кода. Рас- сматриваемый пример должен продемонстрировать реакцию приложения на нажатие кнопки, следовательно, в Списке объектов Окна кода выбира- ем элемент управления Command!, а в Списке событий — событие Click (нажатие на кнопку). При этом среда генерирует программный код заго- ловка и завершения процедуры обработки события в соответствии с син- таксисом языка Visual Basic (рис. 5.11). Так же, как и в среде Delphi, оста- ется ввести по положению текстового курсора оператор присваивания нового значения свойству Text элемента управления Textl: Textl.Text = "Ура!!! Приложение работает!!!" Рис. 5.11. Программный код обработчика Click для Command! 312
Разработка приложения завершена. С помощью команды меню File/Save Project As... сохраним результаты работы, присвоив главному файлу проекта имя Example.vbp, а модулю формы -— имя ExForm.frm. Код модуля формы с обработчиком события кнопки: VERSION 5.00 Begin VB.Form Forml Caption = "Forml" ClientHeight = 3195 ClientLeft = 60 ClientTop = 345 Clientwidth = 7230 LinkTopic = "Forml" ScaleHeight = 3195 Scalewidth = 7230 Startupposition = 3 'Windows Default Begin VB.CommandButton Commandl Caption = "Проверка работы приложения" Height = 495 Left = 1680 Tabindex = 1 Top = 1680 Width = 3735 End Begin VB.TextBox Textl BeginProperty Font Name = "MS Sans Serif" Size = 12 Charset = 204 Weight = 400 Underline = 0 'False Italic = 0 'False Strikethrough = 0 'False EndProperty Height = 375 Left = 240 Tabindex = 0 Top = 240 Width = 6735 End End Attribute VB_Name = "Forml" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Private Sub Commandl__Click () Textl.Text = "Ура!!! Приложение работает!!!" End Sub 313
й Forml - !□! X| Рис. 5.12. Результат работы приложения Example Принцип удаления процедуры-обработчика общий со средой Delphi — достаточно удалить код, внесенный разработчиком самостоятельно. После этого при сохранении или компиляции модуля обработчик будет удален автоматически из файла frm формы. При изменении с помощью Окна свойств имени кнопки происходит автоматически переименование процедуры-обработчика в файлах формы. Следующий этап — компиляция и запуск приложения. По команде меню Run/Start (или с помощью функциональной клавиши <F5>) сна- чала выполняется компиляция проекта, а затем, в случае отсутствия ошибок компиляции, проект запускается на исполнение. На рис. 5.12 изображен результат работы приложения. 5.5. Средства управления параметрами проекта и среды разработки Для отображения и установки текущих параметров проекта и среды служат диалоговые окна Project Properties (вызывается по команде главного меню Project/Project Properties) и Options (вызывается по команде Tools/Options). В каждом из окон представлены параметры управления проектом и средой, которые можно устанавливать в соответствии с потребностями разработчика. Параметры разбиты на группы, каждая группа размещена на отдельной странице соответствующего окна. Рассмотрим диалоговое окно Options, предназначенное для на- стройки параметров среды (рис. 5.13). Параметры разбиты на следующие группы: 314
{Options P Г*Л' Ли . Tab Width [T 2d Editor | Edtor Format | Genera1 ] Docking | Environment | Advanced | r Code Settings -------------------- । P Auto Syntax Check 1 Г Require Variable Declaration ' P Auto List Members | P Auto Quick Info P Auto Data Tips Window Settings----- ------- P Drag and-Drop Text Edtng 1 P Default to FuB Modde View ' P Procedure Separator | Рис. 5.13. Окно установки параметров среды Страница Editor — параметры, управляющие редактированием в окне программного кода, например: поле Tab Width устанавливает число символов для отступа клавишей <ТаЬ>; при установленном флаге Auto Indent нажатие клавиши <Enter> помещает курсор ввода в колонку, с которой начиналась преды- дущая строка; установка флага Default to Full Module View позволяет про- сматривать в окне кода несколько процедур формы при установленном флаге Require Variable Declaration Visual Basic требует явного описания переменных и при использовании в коде необъявленной переменной выдаст сообщение об ошибке компиляции и т.п. Страница Editor Format — шрифтовые и цветовые характеристики окна кода, например, вид и размер шрифта, цвет фона. Страница General — параметры компиляции и обработки ошибок, например, разработчик может выбрать один из трех вариантов ре- акции среды разработки на ошибку: Break on All Errors — при возникновении ошибки среда разра- ботки всегда переходит в режим прерывания (отладки); Break in Class Module — возникновение любой необрабатывае- мой ошибки в модуле класса приводит к переключению среды 315
разработки в режим отладки и выделению строки кода, вызвав- шей ошибку; Break on Unhandled Errors — если процедура обработки оши- бок активна, то возникшая ошибка обрабатывается без переклю- чения среды в режим отладки. Если же такая процедура отсутст- вует, то происходит переключение среды в режим отладки. Страница Docking — установка списка интерфейсных окон, «привязанных» к главному окну. Страница Environment — параметры конфигурации рабочей сре- ды, например, при старте среда может либо всегда запрашивать имя проекта, который необходимо открыть для работы (опция Prompt for project), либо всегда открывать для разработки проект Standart EXE (опция Create default project) и т.п. Страница Advanced — параметры поддержки возможностей ран- них версий среды, например установка флага SDI Development Environment позволяет настроить вид среды разработки, сделав ее интерфейс более привычным для тех, кто работал с предыдущими версиями Visual Basic. Диалоговое окно Project/Properties служит для установки парамет- ров текущего проекта (рис. 5.14). Параметры проекта разделены на 4 группы: Project! - Project Properties Рис. 5.14. Окно установки свойств проекта 16
• General — общие характеристики проекта: стартовый объект (спи- сок Startup Object); название (поле Project Name) и описание про- екта (поле Project Description) и т.п. • Маке — опции приложения: управление номером версии (группа Version Number и опция автоматического увеличения номера вер- сии Auto Increment); название и пиктограмма приложения (Application: Title и Icon); параметры командной строки при за- пуске приложения (строка Command Line Arguments) и т.д. • Compile — условия компиляции проекта. • Component — параметры запуска проекта. Справочная система среды Visual Basic (рис. 5.15) организована по тому же принципу, что и справочная система Delphi и включает в себя стандартную систему справки (первая группа команд меню Help); справочную помощь, получаемую через Internet (группа команд Help/Microsoft on the \УеЬ)и контекстно-зависимую систему справок (клавиша <F1>). Рис. 5.15. Окно справки 317
И 5.6. Программа «Калькулятор» в среде Visual Basic Рассмотрим реализацию задачи «Калькулятор», сформулированной в п.3.6, в среде Visual Basic. Для решения задачи создадим класс MyClass, описывающий вы- полнение арифметической операции над двумя аргументами. Чтобы определить в Visual Basic новый класс данных, имеющий свои поля данных и свои методы, необходимо через Главное меню среды добавить модуль класса (рис. 5.16). — «ее & Forml (Forml .frm) □ n||Q I & Project! (vB_Cak.vbp) № ckfdtiuiittl > • I " " *•» । FlllLastOper(ByVai vNewValue As nOper() As String jMyLtass Afchabebc | Categorized | $. ДДДмуС1а?5 !;л° |au 1ч |»л> |м)сj[^r <r« Рис. 5.16. Добавление модуля класса cArgl(Byval vNewvalue As Strii etArg2(ByVal vNevValue As Serin FlllLastOper() As Boolean jRature thenameusedneodetoidertfy a form,i Далее для класса необходимо определить данные, свойства и мето- ды. Алгоритмы обработки аналогичны алгоритмам, примененным при решении подобной задачи в среде Delphi. Текст модуля класса со всеми разработанными для него свойствами приведен ниже: (General) (declarations) Dim Argl, Arg2 As Variant, LastOper As Boolean, Oper As String 318
Sub) Private Sub Class_Initialize() Argl = 0 Arg2 = 0 LastOper = False Oper = "" End Sub Public Property Let SetArgl(ByVai vNewValue As String) Argl = Vai(vNewValue) End Property Public Property Let SetArg2(ByVai vNewValue As String) Arg2 = Vai(vNewValue) End Property Public Property Get FillLastOper() As Boolean FillLastOper = LastOper End Property Public Property Let FillLastOper(ByVai vNewValue As Boolean) LastOper = vNewValue End Property Public Property Get SignOperO As String SignOper = Oper End Property Public Property Let SignOper(ByVai vNewValue As String) Oper = vNewValue End Property Public Function OperationResult() R = 0 ' Оператор Select Case в зависимости от знака операции ' обеспечивает выполнение арифметического действия. ' Локальная переменная R служит для временного хранения ' значения результата. Select Case Oper Case "+" R = Argl + Arg2 Case R = Argl - Arg2 Case R = Argl * Arg2 Case "/" 319
R = Argl / Arg2 Case Else R = 0 End Select OperationResult = Str(R) End Function Предлагаемый внешний вид формы приложения представлен на рис. 5.17. Рис. 5.17. Окно программы «Калькулятор» Исправим некоторые значения свойств в окне свойств формы Forml (рис. 5.18): изменим свойство Caption — текст заголовка окна формы, задав значение «Пример-калькулятор». Abhabebc | Categorized | "' ' ' , Forml *11 1 ftppearance 1 - 3D___________________ | ftutoRedraw : False__________________ BackCdor______□ 8H8000000F&_____________Г” I Borderstyle 2-Sizable" > f Caption_______; Пример-калькулятор_____ I decontrols True_________________________ _f / I ControBox jTrue___________________ | DrawMode _____13-Copy Pen________________ ! DrawStyte Jo-Sold__________ ____ _ _ ’-V: DrawWidth 1 Enabled True Рис. 5.18. Свойства формы в окне Инспектора объектов 320
£3 t*a b® Hew po|ttt Fgrrnat [£bug ВЦ' 1°*^ fidd-tne window Цф _ -tf • •В' сг Я X4aF.W “^~i >~I< iNff'Rgia'lj йо,1за> laS E General >9 за J^Projectl (Л1 i 4. vlf ) B-SForn —й j-onnl (Fwnl JrnQ a 15 Oa$$ ModiJes —£Э MyOass (MyCass.ds) ИИМ—i—E “““Й |SB_Dtgft(5) CornnantButtcn □ ««OOOOOOFB. Fake jMsb (None)_ (None) (None) 0; Manual True - {Appearance 4 BackColor Cancel S: Captan £ MaJt _ I' ItsabtedSrtire )o*mPicture ’ >agkon ?• DragMode EnaWed Font О <-> Рис. 5.19. Свойства кнопки — элемента массива Окно для ввода значений аргументов и вывода результата пред- ставляет собой визуальный компонент TextBox. Кнопки реализуются с помощью компонентов CommandButton. При разработке формы ис- пользовалась возможность создания массивов однотипных кнопок с одной процедурой обработки события Click (рис. 5.19). Приведем описание процедур обработки событий. Событие Click для кнопки-цифры вызывает выполнение процедуры SBDigitClick: Private Sub SB_Digit_Click(Index As Integer) If Ope_Result.FillLastOper Then ArgMemo.Text = "" Ope_Result.FillLastOper = False End If ' Добавление цифры к числу справа ArgMemo.Text = ArgMemo.Text + SB_Digit.Item(Index).Caption End Sub 321
Процедура обработки события для кнопки-«точки» (SBPointClick): Private Sub SB_Point_Click() If Ope_Result.FillLastOper Then ArgMemo.Text = "" Ope_Result.FillLastOper = False End If If ArgMemo.Text = Then ArgMemo.Text = ArgMemo.Text + "0." Elself InStr(ArgMemo.Text, = 0 Then ArgMemo.Text - ArgMemo.Text + End If End Sub Обработка события Click кнопок-знаков операций SB Oper Click: Private Sub SB_Oper_Click(Index As Integer) With Ope_Result •SetArg2 = ArgMemo.Text If .SignOper <> "" Then s = .OperationResult Else: s = ArgMemo.Text End If .SetArgl = s ' Занесение значения первого аргумента .SetArg2 = "0” 1 Обнуление второго аргумента ArgMemo.Text = s ' Вывод значения первого аргумента .SignOper = SB_Oper.Item(Index).Caption ' Установка знака операции и флага ввода операции .FillLastOper = True End With End Sub Процедура обработки события Click для знака «равно»: Private Sub SB_Eq_Click() With Ope_Result If .SignOper <> "" Then .SetArg2 = ArgMemo.Text ArgMemo.Text = .OperationResult .SetArgl = ArgMemo.Text End If .SetArgl = 0 .SetArg2 = 0 322
.SignOper = "" -FillLastOper = True End With End Sub Для поддержки событий нажатия на кнопки ‘С’ и ‘Del’ опишем процедуры SB_Clear_Click и SB del Click: Private Sub SB_Clear_Click() ArgMemo.Text = "" ' Очистка текстового значения окна ArgMemo With Ope_Result -SetArgl = 0 -SetArg2 = 0 .SignOper = "" -FillLastOper = False End With End Sub Private Sub SB_Del_Click() s = ArgMemo.Text 'Установка нового значения длины строки (меньше на 1) ArgMemo.Text = Left(s, Len(s) — 1) End Sub Алгоритмы обработки событий строятся на использовании объекта Ope Result, поэтому необходимо обеспечить размещение объекта в па- мяти и освобождение памяти при завершении работы приложения. Dim Оре_Result As MyClass Private Sub Form_Load() Set Ope_Result = New MyClass End Sub Private Sub Form_Unload(Cancel As Integer) Set Ope_Result = Nothing End Sub Средой Visual Basic сгенерирован текст основной программы и по- строен модуль MyClass, содержащий описание класса TOperation. Основной модуль: Dim Ope_Result As MyClass Private Sub Form_Load() Set Ope_Result = New MyClass End Sub 323
Private Sub FormUnload(Cancel As Integer) Set Ope_Result = Nothing End Sub Private Sub SB_Digit_Click(Index As Integer) If Ope_Result.FillLastOper Then ArgMemo.Text = "" Ope_Result.FillLastOper = False End If ArgMemo.Text = ArgMemo.Text + SB__Digit.Item(Index).Caption End Sub Private Sub SB_Oper_Click(Index As Integer) With Ope_Result -SetArg2 = ArgMemo.Text If .SignOper <> "" Then s = .OperationResult Else: s = ArgMemo.Text End If .SetArgl = s .SetArg2 = "0" ArgMemo.Text = s .SignOper = SB_Oper.Item(Index).Caption .FillLastOper = True End With End Sub Private Sub SB_Point_Click() If Ope_Result.FillLastOper Then ArgMemo.Text = "" Ope_Result.FillLastOper = False End If If ArgMemo.Text = "" Then ArgMemo.Text = ArgMemo.Text + "0." Elself InStr(ArgMemo.Text, ".") - 0 Then ArgMemo.Text = ArgMemo.Text + End If End Sub Private Sub SB_Eq_Click() With Ope_Result If .SignOper <> "" Then .SetArg2 = ArgMemo.Text ArgMemo.Text = .OperationResult .SetArgl = ArgMemo.Text End If 324
.SetArgl = О .SetArg2 = О .SignOper = .FillLastOper = True End With End Sub Private Sub SB_Clear_Click() ArgMemo.Text = "" With Ope_Result .SetArgl = 0 .SetArg2 = 0 -SignOper = "" -FillLastOper = False End With End Sub Private Sub SB_Del_Click() s = ArgMemo.Text ArgMemo.Text = Left(s, Len(s) — 1) End Sub Упражнения 1. Выполнить упражнения 1-4 гл. 3 для приложения «Калькулятор». 2. Разработать приложение «Редактор», окно которого содержит Главное ме- ню и область редактирования, с функциями, описанными в гл. 3. Для разра- ботки меню используйте Редактор меню, вызываемый с помощью команды Tools/Menn Editor. 3. Выполнить упражнения 5-6 гл. 3 для приложения «Редактор».
Глава 6. Язык программирования С (СИ) Язык Си был создан в начале 70-х годов Деннисом Ритчи, который работал в компании Bell Laboratories. Си был разработан как язык для программирования в новой по тем временам операционной системе (ОС) Unix. Вскоре Unix была переписана на языке Си, и в 1974-75 годах ОС Unix фирмы Bell Laboratories стала первым коммерческим продук- том, реализующим идею о том, что операционная система может быть успешно написана на языке высокого уровня, если этот язык является достаточно мощным и гибким. В 1978 г. Брайан Керниган и Деннис Ритчи написали книгу «Язык программирования Си» (издательство Prentice-Hall). Эта работа, которая в своем кругу называлась «белой книгой» и в остальном мире — «К & R», стала стандартом описания языка Си. На момент создания «К & R» существовали компиляторы языка Си для ЭВМ PDP-11, Interdata 8/32, Honeywell 6000 и IBM 370. В дальнейшем этот список был продолжен. После появления IBM PC стали появляться и компиляторы Си для этой ПЭВМ. Некоторые компиляторы были получены путем преобразо- вания соответствующих компиляторов для процессора 8080, другие бы- ли разработаны специально для IBM PC. В настоящее время на рынке представлены по меньшей мере семнадцать компиляторов языка Си для IBM PC. В 1983 г. Американский Институт Стандартов (ANSI) сформировал Технический Комитет ХЗЛ1, устав которого предусматривает создание стандарта языка Си. Стандартизация должна распространяться не толь- ко на язык, но и на программную среду компилятора, а также на биб- лиотеку стандартных функций. В работе комитета участвуют предста- вители основных фирм — поставщиков компиляторов Си, в том числе и для IBM PC, а также многие другие известные специалисты по языку Си. Усилия комитета X3J11 привлекли внимание средств массовой ин- формации. Проект стандарта был опубликован для того, чтобы все за- интересованные стороны могли ознакомиться с ним и внести свои пред- ложения. Поскольку большинство поставщиков компиляторов для IBM PC участвуют в работе комитета ХЗJ11, то разрабатываемые ими новые версии компиляторов будут в рамках этого стандарта. (Турбо Си, один из последних компиляторов для IBM PC, подчиняется большинству требований стандарта на язык и библиотеку). 326
Одним из наиболее часто упоминаемых достоинств языка Си явля- ется переносимость программ, написанных на этом языке. Если про- грамма на Си не использует расширения библиотеки, зависящие от кон- кретного компилятора, или машинно-зависимые операции, то она с меньшими усилиями, чем при использовании любого другого языка, может быть перенесена в другую программно-аппаратную среду, вклю- чая смену компилятора, операционной системы и ЭВМ. Программистам особенно нравится краткость выражений, которы- ми в Си кодируются алгоритмы. Большинство операторов (операторы присваивания, условные операторы, обращения к функциям или выра- жения) возвращают некоторые значения. Использование этой особенно- сти языка позволяет представлять выражения в краткой форме. Си — гибкий язык, позволяющий принимать в конкретных ситуа- циях самые разные решения. Тем не менее, Си налагает незначительные ограничения на такие, например, действия, как преобразование типов. Во многих случаях это является достоинством, однако программисты должны хорошо знать язык, чтобы понимать, как будут выполняться их программы. 6.1. Примеры программ Рассмотрим уже знакомую по предыдущим главам диалоговую программу общения с пользователем. Структурно программа состоит из двух вложенных циклов. Во внешнем вводится имя пользователя (пат), во внутреннем — настроение (nastr). Это строчные переменные, кото- рые в соответствии с особенностями строения ЯП Си объявляются как байтовые (символьные) массивы, размерность которые равна 100 байт (nam[100], nastr[100]). /* Простая диалоговая программа */ tinclude <stdio.h> tinclude <string.h> main () { char nam[100],nastr[100]; do { puts("ХпЗдравствуйте! Как Вас зовут?"); gets(nam); /* ввод строки */ if (nam[l]==' ') continue; if (nam[1]=='*') 327
break; do { puts ("КпДобрый день, ”); puts (nam); puts ("\Как настроение?"); gets (nastr); if (nastr[l]==' ') continue; if (nastr[1]=='*') break; puts ("\пУ меня тоже "); puts (nastr); puts(nam); } while (1) ; } while (1); Каждый из циклов имеет структуру do{ ... } while, т.е. является циклом с постусловием, которое здесь не используется, поскольку пре- дусмотрен выход из цикла при вводе пользователем определенной стро- ки. Операторные скобки {} (подобно begin ... end в языке Pascal) огра- ничивают тело каждого цикла. Кроме того, они же ограничивают в це- лом тело программы (процедуры) main() — основной процедуры данного исходного текстового файла. В начале текста находятся две директивы компилятору, которые требуют подключить (при компиляции) стандартные библиотеки ввода- вывода и обработки строчной информации. Далее идут объявления пе- ременных и начинается внешний цикл. В каждом из циклов присутст- вуют функции ввода и вывода строк puts и gets. Форматный символ, присутствующий в выводимых строках "\п" означает перевод на новую строку (new line). Далее в каждом из циклов расположены два условных операторов (if). Первый из них проверяет условие ввода непустой строки, и если это не так, инициирует возврат в начало цикла с выдачей повторного запро- са на ввод (continue). Второй оператор осуществляет при некотором условии выход во внешний цикл (из внутреннего) или из программы (во внешнем цикле) — оператор break. Этим условием, заложенным в текст данной программы, является ввод пользователем символа ("звездочка"). Из текста программы также ясно, что обращение к компонентам массива осуществляется по значению индекса (nam[l]) и операция срав- нения двух величин или выражений (РАВНО) кодируется как Кроме того, видно, что логической величине true (истина) здесь соот- ветствует арифметическая единица (while (1)). 328
Рассмотрим далее программу сортировки массива (целых) чисел. /* Программа сортировки массива V по возрастанию элементов*/ #include <stdio.h> tinclude <math.h> main () { int v[140],n,i,j,d=0; int minw,wl=0; n=10; for (i=0;i<n;i++) { printf("ХпВведите элемент массива>“); scanf("%d", &d); v[i]=d; } /* ввод массива */ printf ("Исходный массив\п"); for (i=0;i<n;i++) printf("%d\n",v[i]); for (i=0;i<n;i++) { minw=v[i]; d=i; for (j=i+l;j<n;j++) { if (v[j]<minw) {minw=v[j]; d=j;} } wl=v[i]; v[i]=v[d];v[d]=wl; } printf("ХпРезультаты сортировки"); for (i=0;i<n;i++) printf("\n%d",v[i]); } Структурно программа выглядит следующим образом: - цикл ввода исходного массива; - цикл распечатки введенных данных; - два вложенных цикла сортировки массива; - цикл вывода результатов сортировки. В результате работы программы на экране может быть отображен следующий диалог: Введите элемент массива>2 Введите элемент массива>11 Введите элемент массива>3 Введите элемент массива>1 Введите элемент массива>4 Введите элемент массива>16 329
Введите элемент массива>5 Введите элемент массива:» 12 Введите элемент массива>7 Введите элемент массива>8 Исходный массив 2 11 3 1 4 16 5 12 7 8 Результаты сортировки 1 2 3 4 5 7 8 11 12 16 Циклы в данном примере организуются оператором for (заголовок) { ... }. Заголовок цикла включает инициализацию управляющей пере- менной (i=0), условие продолжения (i<n), изменение управляющей пе- ременной при каждом проходе цикла (i++ — увеличение на 1 или ин- кремент). Новой особенностью является использование для ввода-вывода форматных функций printf и scanf. Параметрами функции являются управляющая строка ("%d") и собственно идентификатор вводимой- выводимой переменной. В данном примере вводятся (выводятся) деся- тичные числа, что отображается в управляющей строке как "%d". Осо- бенностью функции scanf в данном примере является использование в качестве параметра не идентификатора переменной (d), а указателя на адрес переменной (&d). И еще два важных отличия от предыдущего примера: • инициализация переменных при объявлении (присвоение в дан- ном случае нулевых значений); • использование скобок { ... } для создания составных операторов if (условие) {...}. 330
6.2. Лексика языка программирования Си Множество символов Си включает большие и малые буквы латин- ского алфавита и 10 десятичных арабских цифр: • большие (заглавные) буквы: ABCDEFGHIJKLMNOPQRSTUVWXYZ • малые (строчные) буквы: abcdefghij kl mnopqrstu vwxyz • десятичные цифры: 0123456789 Буквы и цифры используются при формировании констант, иден- тификаторов и ключевых слов. Компилятор Си рассматривает одну и ту же малую и большую буквы как различные символы. Пробельные символы. Пробел, табуляция, перевод строки, воз- врат каретки, новая страница, вертикальная табуляция и новая стро- ка — это символы, называемые пробельными, поскольку они имеют то же самое назначение, что и пробелы между словами и строками на пе- чатной странице. Эти символы разделяют объекты программы. Знаки пунктуации и специальные символы из множества симво- лов Си используются для различных целей, от организации текста про- граммы до определения заданий, которые будут выполнены компилято- ром или откомпилированной программой (табл. 6.1). Таблица 6.1. Знаки пунктуации и специальные символы Символ Наименование Символ Наименование Запятая ! Восклицательный знак Точка 1 Вертикальная черта 9 Точка с запятой / Наклонная черта вправо Двоеточие \ Наклонная черта влево Знак вопроса — Тильда » Одиночная кавычка Подчеркивание ( Левая круглая скобка # Знак номера ) Правая круглая скобка % Знак процента Левая фигурная скобка & Амперсанд } Правая фигурная скобка А "Крышка", Caret < Левая угловая скобка — Знак минус > Правая угловая скобка - Знак равно [ Левая квадратная скобка + Знак плюс 1 Правая квадратная скобка 331
ESC-последовательности (эскейп-последователыюсти) — это специальные символьные комбинации, которые представляют пробель- ные и неграфические символы в строках и символьных константах. Их типичное использование связано со спецификацией таких действий, как возврат каретки и табуляция, а также для задания символьных пред- ставлений некоторых кодов. ESC-последовательность состоит из на- клонной черты влево, за которой следует буква, знаки пунктуации или комбинация цифр. В табл. 6.2 приведен список ESC-последовательностей языка Си. Таблица 6.2. ESC- последовательности ESC- последовательность Наименование \п Новая строка \t Горизонтальная табуляция \v Вертикальная табуляция \b Пробел „ \r Возврат каретки \f Новая страница \a Звонок(сигнал) V Одиночная кавычка \" Двойная кавычка \\ Наклонная черта влево \ddd ASCII символ в восьмеричном представлении \xdd ASCII символ в шестнадцатеричном пред- ставлении Если наклонная черта влево предшествует символу, не включенно- му в этот список, то наклонная черта влево игнорируется. Например, изображение \с представляет символ "с" в символьной строке или кон- станте-символе. Последовательности \ddd и \xdd позволяют задать любой символ в ASCII как последовательность от одной до трех восьмеричных цифр или от одной до двух шестнадцатеричных цифр. Например, символ про- бела может быть задан как \010, \10, \х08 или \х8. Код ASCII "нуль" мо- жет быть задан как \0 или \х0. Операции. Операции — это специальные комбинации символов, задающие действия по пробразованию различных величин. В табл. 6.3 представлен список операций. Операции должны ис- пользоваться точно так, как они представлены в таблице: без пробель- ных символов между символами в тех операциях, которые представле- ны несколькими символами. 332
Таблица 6.3. Операции Опера- ция Наименование Опера- ция Наименование ! Логическое НЕ 1 Логическое ИЛИ Побитовое дополнение && Логическое И + Сложение ?: Операция условного выражения - Вычитание, арифмети- ческое отрицание ++ Инкремент * Умножение — Декремент / Деление = Простое присваивание % Остаток += Сложение с присваива- нием « Сдвиг влево _= Вычитание с присваи- ванием » Сдвиг вправо *= Умножение с присваи- ванием < Меньше /= Деление с присваиванием <= Меньше или равно %= Остаток с присваиванием > Больше »= Сдвиг вправо с при- сваиванием >= Больше или равно «= Сдвиг влево с присваи- ванием = Равно &= Побитовое И с присваи- ванием != Не равно 1= Побитовое включающее ИЛИ с присваиванием & Побитовое И Побитовое исключаю- щее ИЛИ с присваива- нием 1 Побитовое включающее ИЛИ Последовательное вы- полнение (запятая) Л Побитовое исключаю- щее ИЛИ Ключевые слова. Ключевые (зарезервированные) слова — это пред- определенные последовательности символов, которые имеют специаль- ное значение для компилятора Си. Их можно использовать только так, как они определены. Имена объектов программы не могут совпадать с названиями ключевых слов. 333
Список ключевых слов: auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof while do if static volatile Комментарии. Комментарий — это последовательность символов, которая воспринимается компилятором как отдельный пробельный символ или, другими словами, игнорируется. Комментарий имеет следующую форму представления: /*<символы>*/, где <символы> может быть любой комбинацией символов из множества представимых символов, включая символы новой строки, но исключая комбинацию */. Это означает, что комментарии могут занимать более одной строки, но не могут быть вложенными. Еще раз отметим, что компилятор Си рассматривает буквы верхне- го и нижнего регистров как различные символы. Поэтому можно соз- дать отдельные независимые идентификаторы, которые совпадают ор- фографически, но различаются большими и малыми буквами. Напри- мер, каждый из следующих идентификаторов является уникальным: add ADD Add aDD 6.3. Структура программы Рассмотрим структуру исходной программы на Си и опеределим термины, используемые при описании языка. Исходная программа — это совокупность следующих объектов: директив, указаний компилятору, объявлений и определений. Директивы задают действия препроцессора по преобразованию текста программы перед компиляцией. Указания компилятору — это команды, выполняемые компилято- ром во время процесса компиляции. 334
Объявления задают имена и атрибуты переменных, функций и ти- пов, используемых в программе. Определения — это объявления, определяющие переменные и функции. Определение переменной в дополнении к ее имени и типу задает начальное значение объявленной переменной. Кроме того, определение предполагает распределение памяти для переменной. Определение функции специфицирует ее структуру, которая пред- ставляет собой смесь из объявлений и операторов, которые образуют саму функцию. Определение функции также задает имя функции, ее формальные параметры и тип возвращаемой величины. Исходная программа может содержать любое число директив, ука- заний компилятору, объявлений и определений. Любой из объектов программы имеет определенный синтаксис, и каждая составляющая может появляться в любом порядке. В следующем примере иллюстрируется простая исходная прог- рамма на языке Си. int х = 1; /* Определение переменных */ int у = 2; extern int printf(char * / • . . .) /* Объявление функции */ main () /* Определение функции main */ { int z; /* Объявление переменных */ int w; z = у + x ; /* Операторы */ w = у — x; printf("z = %d \nw = ; %d \n", z, x); } Эта исходная программа определяет функцию с именем main и объявляет функцию printf. Переменные х и у задаются своими опре- делениями. Переменные z и w только объявляются. Каждая программа содержит главную программную функцию. В Си главная программная функция должна быть поименована как main. Функция main служит точкой старта при выполнении программы и обычно управляет выполнением программы, организуя вызовы других функций. Программа обычно завершает выполнение по окончанию функции main, хотя она может завершиться и в других точках, в зависи- мости от окружающей обстановки. Исходная программа обычно включает в себя несколько функций, каждая из которых предназначена для выполнения определенного зада- ния. Функция main может вызывать эти функции с тем, чтобы вы- полнить то или иное задание. Функция возвращает управление при вы- 335
полнении оператора return или по окончанию самой функции (выход на конец функции). Все функции, включая функцию main, могут быть объявлены с па- раметрами. Вызываемые функции получают значения параметров из вызывающих функций. Значения параметров функции main могут быть переданы из внешнего окружения. Например, они могут быть переданы из командной строки. Соглашение Си требует, чтобы первые два параметра функции main (если они необходимы) назывались argc и argv. Параметр argc определяет общее число аргументов, передаваемых функции main. Параметр argv объявляется как массив указателей, каждый элемент которого ссылается на строковое представление аргумента, пере- даваемого функции main. Третий параметр функции main (если он есть) традиционно задается с именем envp. Однако Си не требует этого имени. Параметр envp — это указатель на массив указателей строковых величин, которые определяют окружение, в котором выполняется программа. Операционная система поддерживает передачу значений для argc, argv, и envp параметров, а пользователь поддерживает задание значений фактических параметров для функции main. Соглашение о передаче параметров в большей степени определяется операционной системой, чем самим языком Си. И 6.4. Типы данных. Переменные и константы Типы данных. К базовым типам данных языка Си относятся: • целые; • вещественные; • перечисляемые. К структурированным типам данных относятся: • массивы, • структуры, • объединения, • типы, определяемые пользователем. Целые типы. В табл. 6.4 приведены типы данных языка Си, отно- сящиеся к целым. В этой таблице и следующих столбец «Сокращение» представляет альтернативное название типа. Тип char используется для запоминания буквы, цифры или символа из множества представимых символов. Значением объекта типа char является ASCII код, соответствующий данному символу. Так как тип char интерпретируется как однобайтовая целая величина с областью значений от -128 до 127, то только величины от 0 до 127 имеют сим- 336
вольные эквиваленты. Аналогично, тип unsigned char может запоминать величины с областью значений от 0 до 255. Таблица 6.4. Целые типы Название типа Сокращение Размер в бай- тах Область значе- ний signed char char 1 -128 до 127 signed int signed, int зависит от реализации signed short int short, signed short 2 -32768 до 32767 signed long int long, signed long 4 -2.147.483.648 до 2.147.483.647 unsigned char - 1 0 до 255 unsigned int unsigned зависит от реализации unsignet short int unsignet short 2 0 до 65535 unsigned long int unsignet long 4 0 до 4.294.967.295 Представление в памяти и область значений для типов int и unsigned int не определены в языке Си. По умолчанию размер int (со знаком и без знака) соответствует реальному размеру целого на данной машине. Например, на 16-разрядной машине тип int всегда 16 разрядов или 2 байта. На 32-разрядной машине тип int всегда 32 разряда или 4 байта. Таким образом, тип int эквивалентен типам short int или long int в зависимости от реализации. Аналогично, тип unsigned int эквивалентен типам unsigned short или unsigned long. Спецификаторы типов int и unsigned int широко исполь- зуются в программах на Си, поскольку они позволяют наиболее эффек- тивно манипулировать целыми величинами на конкретной машине. Вещественные типы. Перечень вещественных типов, определен- ных в языке Си, с принятыми сокращениями, размерами занимаемой памяти и областью значений приведен в табл. 6.5. Таблица 6.5. Вещественные типы Название типа Сокращение Размер в байтах Область значений float — 4 IEEE стандартное соглаше- ние long float double 8 IEEE стандартное соглаше- ние 337
Константы. Константа — это число, символ или строка символов. Константы используются в программе как неизменяемые величины. В языке Си различают четыре типа констант: целые константы, константы с плавающей точкой, константы-символы и строчные литералы. Целая константа — это десятичное, восьмеричное или шестнадцатеричное число, которое представляет целую величину. Десятичная константа записывается последовательностью десятич- ных цифр от 0 до 9. В записи восьмеричной константы присутствует лидирующий 0 а затем — одна или более восьмеричных цифр от 0 до 7. Запись ведущего нуля необходима. Шестнадцатеричная константа представляется лидирующими символами Ох (или ОХ) и одной или более шестнадцатеричных цифр (цифры от 0 до 9 и буквы (большие или малые) от А до F). В представ- лении константы допускается "смесь" больших и малых букв. Запись ведущего нуля и следующего за ним символа х или X необходима. Пробельные символы не допускаются между цифрами целой кон- станты. В табл. 6.6 иллюстрируются примеры целых констант. Таблица 6.6. Примеры констант Десятичные константы Восьмеричные константы Шести ад цатери ч и ые константы 10 012 Оха или ОхА 132 0204 0x84 32179 076663 0x7dB3 или 0x7DB3 Целые константы всегда специфицируют положительные величи- ны. Если требуется отрицательные величины, то необходимо сформи- ровать константное выражение из знака минус и следующей за ним кон- станты. Знак минус рассматривается как арифметическая операция. Каждая целая константа специфицируется типом, определяющим ее представление в памяти и область значений. Десятичные константы могут быть типа int или long. Восьмеричные и шестнадцатеричные константы в зависимости от размера могут быть типа int, unsigned int, long или unsigned long. Если константа может быть представлена как int, она специфицируется типом int. Если ее величина больше, чем максимальная положительная вели- чина, которая может быть представлена типом int, но меньше величины, которая представляется в том же самом числе бит как и int, она задается типом unsigned int. Наконец, константа, величина которой больше чем максимальная величина, представляемая типом unsigned int, задается типом long или unsigned long, если это необходимо. 338
Можно определить для любой целой константы тип long, приписав букву "1" или "L" в конец константы. В табл. 6.7 показаны примеры це- лых констант. Таблица 6.7. Примеры целых констант типа long Десятичные коистаиты Восьмеричные константы Шестнадцатеричные коистаиты 10L 012L OxaL или OxAL 791 01151 0x4fl или 0x4FI Константы с плавающей точкой — это вещественное десятичное положительное число. Величина действительного числа включает це- лую, дробную части и экспоненту. Константы с плавающей точкой имеют следующий формат представления: [<цифры>][.<цифры>][Е[-]<цифры>], где <цифры> — одна или более десятичных цифр (от 0 до 9), а Е или е — символ экспоненты. Целая или дробная части константы могут быть опушены, но не обе сразу. Десятичная точка может быть опущена толь- ко тогда, когда задана экспонента. Экспонента состоит из символа экспоненты, за которым следует целочисленная величина экспоненты, возможно отрицательная. Пробельные символы не могут разделять цифры или символы кон- станты. Константы с плавающей точкой всегда специфицируют положи- тельные величины. Если требуются отрицательные величины, то необ- ходимо сформировать константное выражение из знака минус и сле- дующей за ним константы. Знак минус рассматривается как арифмети- ческая операция. Примеры констант с плавающей точкой и константных выражений: 15.75 1.575Е1 1575е-2 -0.0025 -2.5е-3 Целая часть константы с плавающей точкой может быть опущена, например: .75 ,0075е2 -.125 -.175Е-2 339
Все константы с плавающей точкой имеют тип double. Константа-символ — это буква, цифра, знак пунктуации или ESC- последовательность, заключенные в одиночные кавычки (табл.6.8). Ве- личина константы-символа равна значению представляющего ее кода символа. Константы-символы имеют тип int. Чтобы использовать одиночную кавычку или наклонную черту влево в качестве константы-символа, необходимо вставить перед этими знаками наклонную черту влево. Чтобы представить символ новой строки, необходимо использовать запись '\п'. Таблица 6.8. Примеры констант-символов Константа Название величины ’а’ Малая буква а Знак вопроса w Знак пробела '0x1 В’ ASCII ESC-последовательность V Одиночная кавычка w Наклонная черта влево Строковые константы — это последовательность букв, цифр и символов, заключенная в двойные кавычки. Строковая константа рас- сматривается как массив символов, каждый элемент которого представ- ляет отдельный символ. Чтобы использовать двойные кавычки или наклонную черту влево внутри строкового литерала, нужно представить их с предшествующей наклонной чертой влево, как показано в следующем примере: "This is a string literal" "First \\ Second" "\"Yes, I do,\" she said." "The following line shows a null string:" ft II ESC-символы (такие как \\ и \") могут появляться в строковых ли- тералах. Каждый ESC-символ считается одним отдельным символом. Символы строки запоминаются в отдельных байтах памяти. Символ null (\0) является отметкой конца строки. Каждая строка в программе рас- сматривается как отдельный объект. Если в программе содержатся две идентичные строки, то каждая из них будет храниться в отдельном мес- те памяти. Строковые константы имеют тип char[]. Под этим подразумевается, что строка — это массив, элементы которого имеют тип char. Число элементов в массиве равно числу символов в строковой константе плюс 340
один, поскольку символ null (отметка конца строки) тоже считается элементом массива. Объявления переменных. При объявлении в языке Си выделяют следующие типы переменных: • простая переменная — переменная целого типа или типа с плавающей запятой; • переменная перечисления — простая переменная целого типа которая принимает значения из предопределенного набора зна- чений поименованных констант; • структура — переменная, которой соответствует композиция отдельных переменных различных типов (аналог записи в язы- ке Pascal); • объединение — переменная, которой соответствует компози- ция отдельных переменных, занимающих одно и то же про- странство памятиичем типы переменных композиции могут отличаться (аналог записи с вариантами в языке Pascal); • массив — переменная, представляющая собой набор элементов одного типа; • указатель — переменная, которая указывает на другую пере- менную (содержит местоположение другой переменной в форме адреса). Общий синтаксис объявлений переменных следующий: [<класс памяти>] «спецификатор типа> <описатель> [,«описатель»...], где «спецификатор типа> — задает тип данных, представляемых пере- менной, а <описатель> — это имя переменной, возможно модифи- цированное для объявления массива или указателя. В объявлении может быть задана более чем одна переменная путем задания множественного объявления, в котором описатели разделены запятыми. «Класс памяти> задает класс памяти переменной, которая опреде- ляет какой-либо объект, относится либо к глобальному, либо к локаль- ному классу памяти. Объект, относящийся к глобальному классу, существует и имеет значение на протяжении всей программы. Все функции относятся к глобальному классу — это означает запрет на вложенность функций. Переменные локального класса захватывают новую память при ка- ждом выполнении блока, в котором они определены. При выходе из блока, переменная теряет свое значение. Хотя Си определяет два типа классов памяти, но, тем не менее, имеется следующих четыре спецификатора классов памяти: auto register static extern 341
Объекты классов auto и register имеют локальный характер. Специ- фикаторы static и extern определяют глобальные объекты. Каждый из спецификаторов класса памяти имеет определенный смысл, который влияет на доступность функций и переменных в той же мере, как и сами классы памяти. В некоторых случаях переменные могут быть инициализированы (т.е. им могут быть присвоены некоторые начальные значения) при их определении. Простая переменная. Объявление простой переменной определяет имя переменной и ее тип; оно может также определять класс памяти переменной. Синтаксис: «спецификатор типа><идентификатор>[,<идентификатор>...]; Имя переменной — это идентификатор, заданный в объявлении. Спецификатор типа Спецификатор типа> задает имя определяемого типа данных. Можно определить несколько различных переменных в одном объ- явлении, задавая список идентификаторов, разделенных запятой. Каж- дый идентификатор списка именует переменную. Примеры: int х; /* Пример 1 */ unsigned long reply, flag; /* Пример 2 */ double order; /* Пример 3 */ В первом примере объявляется простая переменная х. Эта пе- ременная может принимать любое значение из множества значений, определяемых для типа int. Во втором примере объявлены две переменные: reply и flag. Обе переменные имеют тип unsigned long. В третьем примере объявлена переменная order, которая имеет тип double. Этой переменной могут быть присвоены величины с плавающей запятой. Перечисление. Объявление перечисления задает имя переменной перечисления и определяет список именованных констант, называемый списком перечисления. Значением каждого имени списка является це- лое число. Переменная перечисления принимает значение одной из именованных констант списка. Именованные константы списка имеют тип int. Таким образом, память, соответствующая переменной перечис- ления, — это память, необходимая для размещения отдельной целой величины. Синтаксис: 342
епиш[<тэг>]<список перечисления><идентификатор> [,<идснтификатор>...]; епит<тэг><идентификатор>[,<идентификатор>...]; Объявление перечисления начинается с ключевого слова enum и имеет две формы представления. В первой форме представления имена перечисления задаются в списке перечисления <список перечислениях Опция <тэг> — это идентификатор типа для объявляемого пере- числения. Переменную перечисления именует <идентификатор>. В объявле- нии может быть описана более чем одна переменная перечисления. Во второй форме используется тэг перечисления, который задает тип перечисления. В этой форме объявления список перечисления не представлен. Это означает, что тип перечисления должен быть опреде- лен ранее. <Список перечисления> заключается в фигурные скобки ({}) и имеет следующий синтаксис: <идентификатор>[=<константное выражение>][,<идентификатор> [=<константное выражение]]... Каждый идентификатор определяет отдельный элемент перечисле- ния. По умолчанию первому идентификатору соответствует значение О, следующий идентификатор ассоциируется со значением 1 и т. д. Имя константы перечисления эквивалентно ее значению. Запись =<константное выражение> переопределяет последователь- ность значений, заданных по умолчанию. Идентификатор, следующий перед записью =<константное выражение>, принимает значение, зада- ваемое этим константным выражением. Константное выражение имеет тип int и может быть отрицательным. Следующий идентификатор в спи- ске ассоциируется с величиной, равной <константное выражение>+1, если он явно не задается другой величиной. Перечисление может содержать повторяющиеся значения иденти- фикаторов, но каждый идентификатор должен быть уникальным. На- пример, двум различным идентификаторам null и zero может быть зада- но значение 0 в одном и том же перечислении. Примеры: enum day { /* Пример 1 */ Saturday, Sunday = О, monday, tuesday, Wednesday, thursday, friday } workday; 343
enum day today = Wednesday; /* Пример 2 */ В первом.примере определяется тип перечисления, поименованный day, и объявляется переменная workday этого типа перечисления. С Saturday по умолчанию ассоциируется значение 0. Идентификатор Sunday явно устанавливается в 0. Оставшиеся идентификаторы по умол- чанию принимают значение от 1 до 5. Во втором примере переменной today типа enum day присваивается значение из перечисления примера 1. Заметим, что для присваивания используется имя константы из перечисления. Так как тип перечисления day был предварительно объявлен, то достаточно сослаться только на тэг перечисления (day). Структура. Объявление структуры задает имя типа структуры и специфицирует последовательность переменных величин, называемых элементами структуры, которые могут иметь различные типы. Синтаксис: struct[<T3r>]<cnHC0K объявлений элементов><описатель>[,<описатель>...]; я!гис1<тэгхописатель> [,<описатель>...]; Объявление структуры начинается с ключевого слова struct и име- ет две формы представления, как показано выше. В первой форме пред- ставления типы и имена элементов структуры специфицируются в спи- ске объявлений элементов <список объявлений элементов>. <Тэг> — это идентификатор, который именует тип объявляемой структуры. Каждый <описатель> задает имя переменной типа структуры. Тип пе- ременной в описателе может быть модифицирован на указатель к структу- ре, на массив структур или на функцию, возвращающую структуру. Вторая синтаксическая форма использует <тэг> для ссылки на тип структуры, представленный со списком объявлений элементов ранее. <Список объявлений элементов> — это одно или более объявлений переменных. Каждая переменная, объявленная в этом списке, называет- ся элементом структурного типа. Объявления переменных списка име- ют тот же самый синтаксис, что и объявления переменных, за исключе- нием того, что объявления не могут содержать спецификаторов класса памяти или инициализаторов (начальных значений). Элементы струк- туры могут быть любого типа: базового, массивом, указателем, объеди- нением или структурой. Элемент не может иметь тип структуры, в которой он появляется. Однако элемент может быть объявлен, как указатель на тип структуры, в которую он входит, позволяя создавать списочные структуры. Идентификаторы элементов внутри объявляемой структуры долж- ны быть уникальными. Идентификаторы элементов внутри разных структур могут совпадать. 344
Переменные (элементы) структуры запоминаются последовательно в том же самом порядке, в котором они объявляются: первой пе- ременной соответствует самый младший адрес памяти, а последней — самый старший. Память каждой переменной начинается на границе, свойственной ее типу. Поэтому могут появляться неименованные уча- стки между соседними элементами. Примеры: struct { /* Пример 1 */ float х, у; ) complex; struct employee { /* Пример 2 */ char name[20]; int id; long class; } temp; struct employee student, faculty, staff; /* Пример 3 */ struct sample { /* Пример 4 */ char c; float *pf; struct sample ‘next; } x; В первом примере объявляется переменная с именем complex типа структура. Эта структура состоит из двух элементов х и у типа float. Тип структуры не поименован. Во втором примере объявляется переменная с именем temp типа структура. Структура состоит из трех элементов с именами name, id и class. Элемент с именем name — это массив из 20 элементов типа char, элементы с именами id и class — это простые переменные типа int и long соответственно. Идентификатор employee является тэгом структуры. В третьем примере объявлены три переменных типа структура с именами: student, faculty и staff. Каждая из структур состоит из трех элементов одной и той же конструкции. Элементы определены при объ- явлении типа структуры с тэгом employee в предыдущем примере. В четвертом примере объявляется переменная с именем х типа структура. Первые два элемента структуры представлены перемен- ной с типа char и указателем pf на величину типа float. Третий эле- мент с именем next объявляются как указатель на описываемую структуру sample. Объединение. Объявление объединения определяет имя перемен- ной объединения и специфицирует множество переменных, называемых элементами объединения, которые могут быть различных типов. Пере- 345
менная с типом объединения запоминает любую отдельную величину, определяемую набором элементов объединения. Синтаксис: шпоп[<тэг>]<список объявлений элементов><описатсль>[,<описатель>...]; ип1оп<тэг><описатель>[,<описатель>...]; Объявление объединения имеет тот же самый синтаксис, что и объ- явление структуры, за исключением того, что оно начинается с ключе- вого слова union вместо ключевого слова struct. Для объявления объе- динения и структуры действуют одни и те же правила. Память, которая соответствует переменной типа объединение, оп- ределяется величиной для размещения любого отдельного элемента объединения. Когда используется наименьший элемент объединения, то пере- менная типа объединения может содержать неиспользованное простран- ство. Все элементы объединения запоминаются в одном и том же про- странстве памяти переменной, начиная с одного и того же адреса. Со- храненные значения затираются каждый раз, когда пременной присваивается значение очередного элемента объединения. Примеры: union sign /* Пример 1 */ int svar; unsigned uvar; number; union /* Пример 2 */ char * a, b; float f[20]; jack; В первом примере объявляется переменная типа объединения, по- именованная number. Список элементов объединения состоит из двух объявлений переменных: svar типа int и uvar типа unsigned. Это объяв- ление позволяет запоминать текущее значение number в знаковом или беззнаковом виде. Тип объединения поименован идентификатором sign. Во втором примере объявляется переменная типа объединения с именем jack. Список элементов объявления состоит из трех объявлений: указателя а на величину типа char, переменной b типа char и массива f из 20 элементов типа float. Тип объединения не поименован. Память, распределенная для переменной jack, равна памяти, рас- пределенной под массив f, поскольку f самый большой элемент объеди- нения. Массив. Объявление массива определяет тип массива и тип каждо- го элемента. Оно может определять также число элементов в массиве. 346
Синтаксис: «спецификатор типа><описатель>[<константное выражение>]; «спецификатор типа>«описатель>[]; Здесь квадратные скобки — это специальные символы, участвую- щие в объявлении. Объявление массива может представляться в двух синтаксических формах, указанных выше. <Описатель> задает имя пе- ременной. Квадратные скобки, следующие за описателем, указывают на то, что объявляется массив. Константное выражение «константное вы- ражение^ заключенное в квадратные скобки, определяет число элемен- тов в массиве. Каждый элемент имеет тип, задаваемый спецификатором типа «спецификатор типа>, который может специфицировать любой тип, исключая тип функции. Во второй синтаксической форме опущено константное выражение в квадратных скобках. Эта форма может быть использована только то- гда, когда массив инициализируется, объявлен как формальный пара- метр или объявлен как ссылка на массив, явно определенный где-то в программе. Массив массивов (многомерный массив) определяется путем зада- ния списка константных выражений в квадратных скобках: «спецификатор типахописатель>[«константное выражение>] [«константное выражение>]... Каждое «константное выражение> в квадратных скобках определя- ет число элементов в данном измерении массива, так что объявление двумерного массива содержит два константных выражения, трехмерно- го — три и т.д. Типу массив соответствует память, которая требуется для разме- щения всех его элементов. Элементы массива с первого до последнего запоминаются в последовательных возрастающих адресах памяти. Эле- менты массива запоминаются друг за другом построчно. Чтобы со- слаться на отдельный элемент массива, нужно использовать индексное выражение. Примеры: int scores[10], game; /* Пример 1 */ float matrix[10] [15]; /* Пример 2 */ struct /* Пример 3 */ float x,у; complex[100]; char *name[20]; /* Пример 4 */ 347
В первом примере объявляется переменная типа массив с именем scores из 10 элементов типа int. Переменная с именем game объявлена как простая переменная целого типа. Во втором примере объявляется двумерный массив с именем matrix. Массив состоит из 150 элементов типа float. В третьем примере объявляется массив структур. Массив состоит из 100 объектов. Каждый объект массива представляет собой структуру, состоящую из двух элементов. В четвертом примере объявлен массив указателей. Массив состоит из 20 элементов, каждый из которых является указателем на величину типа char. Указатель. Объявление указателя определяет имя переменной ти- па указатель и тип объекта, на который указывает эта переменная. Синтаксис: «спецификатор типа> *<описатель>; <Описатель> определяет имя переменной с возможной модифика- цией ее типа. Спецификатор типа задает тип объекта, который может быть базового типа, типа структуры или объединения. Переменная типа указатель может указывать также на функции, массивы и другие указатели. Переменная, объявленная как указатель, хранит адрес памяти. Раз- мер памяти, требуемый для адреса, и смысл адреса зависит от данной конфигурации машины. Указатели на различные типы не обязательно имеют одну и ту же длину. Примеры: char ‘message; int *pointers[10]; int (*pointer)[10]; struct list ‘next, struct list { char ‘token; int count; struct list ‘next; } line; /* Пример 1 */ /* Пример 2 */ /* Пример 3 */ previous; /* Пример 4 */ /* Пример 5 */ В первом примере объявляется переменная-указатель, поимено- ванная message. Она указывает на величину типа char. Во втором примере объявлен массив указателей, поименованный pointers. Массив состоит из 10 элементов. Каждый элемент — это указа- тель на переменную типа int. В третьем примере объявлена переменная-указатель, поимено- ванная pointer. Она указывает на массив из 10 элементов. Каждый эле- мент в этом массиве имеет тип int. 348
В четвертом примере объявлены две переменных указателя, ко- торые ссылаются на величины структурного типа list (смотри следу- ющий пример). Определение типа с именем list должно находиться в пределах видимости объявления. В пятом примере объявляется переменная с именем line, структур- ного типа, поименованного list. Тип структуры с именем list определя- ется тремя элементами. Первый элемент — это указатель на величину типа char, второй — на величину типа int, а третий — это указатель на следующую структуру типа list. Функция. Объявление функции определяет имя, тип возврата функции и, возможно, типы и число ее аргументов. Синтаксис: [<спецификатор типа>]<описатель>([< Список типов аргументов]) [,<описатель>...]; Описатель объявляет имя функции, а спецификатор типа задает тип возврата. Если спецификатор типа опущен в объявлении функции, то предполагается, что функция возвращает величину типа int. Функции могут возвращать величины любого типа за исключением массивов и функций. Идентификатор функции может быть модифици- рован одной или несколькими звездочками (*), чтобы объявить возвра- щаемую величину типа указателя. Объявление функции может включать спецификаторы класса па- мяти extern или static. Список типов аргументов. <Список типов аргументов> определяет число и типы аргументов функции. Синтаксис списка аргументов сле- дующий: <Список имен типов>[,...] Список имен типов — это список из одного или более имен типов. Каждое имя типа отделяется от другого имени запятой. Первое имя типа задает тип первого аргумента, второе имя типа задает тип второго аргу- мента и т. д. Если список имен типов заканчивается запятой с многото- чием (,...), то это означает, что число аргументов функции переменно. Однако предполагается, что функция будет иметь не меньше аргумен- тов, чем имен типов, предшествующих многоточию. Если список типов аргументов содержит только многоточие (...), то число аргументов функции является переменным или равно нулю. Для того чтобы объявить функцию, не имеющую аргументов, мо- жет быть использовано специальное ключевое слово void на месте спи- ска типов аргументов. Компилятор вырабатывает предупреждающее сообщение, если в вызове такой функции будут специфицированы ар- гументы. 349
Еще одна специальная конструкция допускается в списке типов ар- гументов. Это фраза void *, которая специфицирует аргумент типа ука- затель. Эта фраза может быть использована в списке типов аргументов вместо имени типа. Список типов аргументов может быть опущен. В этом случае скоб- ки после идентификатора функции все же требуются, хотя они и пусты. В этом случае в объявлении функции не определяются ни типы, ни чис- ло аргументов в функции. Когда эта информация опускается, то компи- лятор не проверяет соответствия между формальными и фактическими параметрами при вызове функции. Примеры: int add(int, int) ; double calc() ; char *strfind(char *,...); void draf(void); /* Пример 1 */ /* Пример 2 */ /* Пример 3 */ /* Пример 4 */ В первом примере объявляется функция, поименованная add, кото- рая требует два аргумента типа int и возвращает величину типа int. Во втором примере объявляется функция, поименованная calc, ко- торая возвращает величину типа double. Список типов аргументов не задан. В третьем примере объявляется функция, поименованная strfind, которая возвращает указатель на величину типа char. Функция требует, по крайней мере один аргумент — указатель на величину типа char. Список типов аргументов заканчивается запятой с многоточием, обо- значающим, что функция может потребовать большее число аргумен- тов. В четвертом примере объявляется функция с типом возврата void (нет возвращаемой величины). Список типов аргументов также void, означающий отсутствие аргументов для этой функции. Вызовы функций. Синтаксис: <выражение>(<список выраженией>) Вызов функции состоит из выражения, за которым следует список выражений. Значению выражения соответствует адрес функции (напри- мер, значение идентификатора функции). Значение каждого выражения из списка выражений (выражения в списке разделены запятыми) соот- ветствует фактическому аргументу функции. Список выражений может быть пустым. Выражение вызова функции имеет значение и тип своего возв- рата. Если тип возврата функции- void, то и выражение вызова фун- кции имеет тип void. Если возврат из вызванной функции произошел 350
не в результате выполнения оператора return, то значение функции не определено. Объявления typedef. Объявления typedef являются аналогом объяв- ления переменной, за исключением того, что ключевое слово typedef заменяет спецификатор класса памяти. Синтаксис: typedef «спецификатор типа>«описатель>[,«описатель>...]; Объявление интерпретируется так же, как и объявления перемен- ной или функции, но <описатель>, вместо того, чтобы стать переменной типа, специфицированного объявлением, становится синонимом имени типа. Объявление typedef не создает типов. Оно создает синонимы для существующих имен типов, которые были специфицированы другим способом. Любой тип может быть объявлен с typedef, включая типы указателя, функции и массива. Примеры: typedef int WHOLE; /* Пример 1 */ typedef struct club { /* Пример 2 */ char name[30]; int sise, year; } GROUP; В первом примере объявляется WHOLE как синоним для int. Во втором примере объявляется GROUP как структурный тип с тремя элементами. Так как специфицирован также тэг club, то имя GROUP и тэг club могу быть использованы в объявлениях. Инициализация переменных. В объявлении переменной может быть присвоено начальное значение посредством инициализатора. Син- таксически записи инициализатора предшествует знак равно (=): =<инициализатор> Функции не инициализируются. Объявления, которые используют спецификатор класса памяти extern, не могут содержать инициализатора. Любая переменная, объявленная со спецификатором класса памяти static, может быть инициализирована константным выражением. Ини- циализация переменных класса static выполняется один раз во время компиляции. Если отсутствует явная инициализация, то переменные класса памяти static автоматически устанавливаются в нуль. Инициализация переменных auto и register выполняется каждый раз при входе в блок, в котором они объявлены. Если инициализатор опу- щен в объявлении переменной класса памяти auto или register, то на- 351
чальное значение переменной не определено. Инициализация составных типов auto (массив, структура, объединение) запрещена. Примеры: int х = 10; /★ Пример 1 */ int с = (3 * 1024); /* Пример 2 */ int *b = ’ &x; /* Пример 3 */ int p[4 ] [3] = { /* Пример 4 */ { 1, 1, 1 }, { 2, 2, 2}, {3, 3, 3, }, (4, 4, 4,), ); В первом примере х инициализируется константным выражением 10. Во втором примере используется константное выражение для ини- циализации с. В третьем примере инициализируется указатель b адресом другой переменной х. В четвертом примере объявляется массив р размерности 4 строки на 3 столбца. Элементы первой строки инициализируются 1, второй строки 2 и т. д. Заметим, что списки инициализаторов третьей и четвер- той строк заканчиваются запятой. Последний список инициализаторов 4, 4, 4, также заканчивается запятой. Эти дополнительные запятые до- пускаются, но не требуются. Требуются только те запятые, которые раз- деляют константные выражения и списки инициализации. Когда инициализируются составные переменные, то нужно поза- ботиться о том, чтобы правильно использовать фигурные скобки и спи- ски инициализаторов. В следующем примере иллюстрируется более детально интерпретация компилятором фигурных скобок. typedef struct { int nl, n2, n3; } triplet; triplet nlist[2] [3] = { {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, /* Line 1 */ {10,11,12}, {13,14,15}, {15,16,17}} /* Line 2 */ }; В примере nlist объявляется как массив структур, состоящий из двух строк и трех столбцов. Каждая структура состоит из трех элемен- тов. Первая строка инициализации назначает величины первой строке массива nlist следующим образом: 1. Первая левая фигурная скобка Line 1 информирует компилятор о том, что это начало инициализации первой строки массива nlist(nlist[O]). 352
2. Вторая левая фигурная скобка означает то, что начинается ини- циализация первого элемента первой строки массива ( nlist[0] [0]). 3. Первая правая фигурная скобка сообщает об окончании ини- циализации первого элемента — структуры nlist[0] [0]. Следующая ле- вая фигурная скобка сообщает о начале инициализации второго элемен- та первой строки nlist[0] [1]. 4. Процесс продолжается до конца Line 1 и заканчивается по по- следней правой фигурной скобке. Аналогично, Line 2 назначает величины второй строке массива nlist. Массив может быть инициализирован строчным литералом, например: char code[ ] = "abc"; инициализирует code как массив символов из четырех элементов. Чет- вертым элементом является символ \0, который завершает все строко- вые литералы. Если специфицируется размер массива, а строка больше чем спе- цифицированный размер, то лишние символы отбрасываются. Следу- ющее объявление инициализирует переменную code, как трехэлемент- ный массив символов: char code[3] = "abed" В примере только три первые символа инициализатора назнача- ются для массива code. Символ d и сивол нуль отбрасываются. Если строка короче, чем специфицированный размер массива, то оставшиеся элементы массива инициализируются нулем (символом \0). 6.5. Выражения и операции Операнд в Си — это константа, переменная, вызов функции. Вы- ражение представляет собой комбинацию операндов и операций. Часть выражения может заключаться в скобки. Любое выражение, которое имеет константное значение, называется константным выражением. Каждый операнд имеет тип. Операнд может быть преобразован из исходного типа к другому типу посредством операции преобразования типов. Преобразование типа может быть использовано в качестве опе- ранда выражения. Рассмотрим подробнее правила формирования индексных выраже- ний и выражений выбора структурного элемента. Индексные выражения. Индексное выражение обычно использу- ется для ссылок на элементы массива. 353
Синтаксис: <выражение 1 >[<выражение2>] Индексное выражение вычисляется путем сложения целой вели- чины <выражение2> со значением указателя <выражение1> с последу- ющим применением к результату операции "перейти по адресу" ("взять значение, расположенное по данному адресу") — В соответствии с правилами преобразования типов для операции сложения, целочисленная величина преобразуется к адресному пред- ставлению путем умножения ее на размер типа, адресуемого указате- лем. Например, предположим, что идентификатор line ссылается на массив величин типа int. Чтобы вычислить выражение linefi], целая ве- личина i умножается на размер типа int. Преобразованное значение i представляет i позиций типа int. Это преобразованное значение склады- вается с начальным значением указателя line, что дает адрес, который расположен на i позиций типа int от line. Последним шагом вычисления индексного выражения является операция перехода по адресу, применяемая к полученному адресу. Ре- зультатом является значение элемента массива, который позиционирован. Индексное выражение line[0] представляет значение первого эле- мента массива, так как отсчет смещения ведется от нуля. Следователь- но, такое выражение, как line[5], ссылается на шестой элемент массива. Выражение выбора структурного элемента. Выражение выбора структурного элемента ссылается на элементы структур или объедине- ний. Выражение выбора имеет значение и тип выбранного элемен- та.Синтаксис: <выражение>.<идентификатор> <выражение> —> <идентификатор> В первой синтаксической форме <выражение>.<идентификатор> выражение представляет величину типа struct или union, а идентифика- тор именует элемент структуры или объединения. Во второй синтакси- ческой форме <выражение> —> <идентификатор> выражение пред- ставляет указатель на структуру или объединение, а идентификатор именует элемент специфицированной структуры. Обе синтаксические формы выражений выбора элемента дают оди- наковый результат. Действительно, запись, включающая операцию ад- ресного выбора —>, является сокращенной версией записи с точкой, для случая, когда выражению, стоящему перед точкой, предшествует операция перехода по адресу Поэтому запись <выражеиие> —> <идентификатор> эквивалентна записи 354
(* <выражсние>). «идентификатор» Примеры: struct pair { int а; int b; struct pair *sp; }item, list[10]; item.sp - &item /* Пример 1 */ (item.sp) -> a = 24 /* Пример 2 */ list[8].b = 12 /* Пример 3 */ В первом примере адрес структуры item присваивается элементу sp этой структуры. Это имеет смысл того, что item содержит указатель на саму себя. Во втором примере используется адресное выражение item.sp с операцией выбора —> , чтобы присвоить значение элементу а. В третьем примере показано, каким образом из массива структур выбрать отдельный элемент структуры. Операции. Большинство операций Си выполняют преобразование типов, чтобы привести операнды выражений к общему типу, или чтобы расширить короткие величины до размера целых величин, используе- мых в машинных операциях. Преобразования, выполняемые операция- ми Си, зависят от специфики операций и от типа операнда или операн- дов. Тем не менее, многие операции выполняют похожие преобразова- ния целых типов и типов с плавающей запятой. Эти преобразования известны как арифметические преобразования, поскольку они приме- няются к типам величин, обычно используемых в арифметике. Арифметические преобразования, приведенные ниже, называются «обычными арифметическими преобразованиями». Обычные арифме- тические преобразования осуществляются следующим образом. 1. Операнды типа float преобразуются к типу double. 2. Если один операнд типа double, то второй операнд преобразуется к типу double. 3. Любые операнды типов char или short преобразуются к int. 4. Любые операнды типов unsigned char или unsigned short преобра- зуются к типу unsigned int. 5. Если один операнд типа unsigned long, то второй операнд преоб- разуется к типу unsigned long. 6. Если один операнд типа long, то второй операнд преобразуется к типу long. 7. Если один операнд типа unsigned int, то второй операнд преобра- зуется к unsigned int. 355
Рассмотрим следующие группы операций: • арифметические операции; • логические операции; • операции отношения; • операции с битами информации; • операции адресации; • операции присваивания. Операции ЯП Си требуют один операнд (унарные операции), два операнда (бинарные операции) или три операнда (тернарная операция). Операции присваивания — это унарные или бинарные операции. Унарными операциями Си являются следующие: Символ Наименование - ~ ! операции дополнения (арифметическое дополнение, двоичное отрицание, логическое отрицание) *& операции перехода по адресу и адресации (взять адрес) sizeof size-операция Бинарными операциями являются следующие: Символ Наименование * / %- + арифметические операции <><=;>==== 1 = операции отношений &|А«» операции с битами &&|| логические операции 5 операция последовательных вычислений В Си имеется одна тернарная операция — это операция условия Она интерпретируется справа налево. Арифметические операции Знак Операция Примеры - Арифметиче- ское отрица- ние short х = 987; х = -х; значение х является отрицанием 987, т.е. -987 ♦ Умножение int i = 10; double х = 2.0; у = х * i; х умножается на i, в результате получается 20.0. Результат имеет тип double. 356
Продолжение табл. Знак Операция Примеры / Деление int i = 10, j = 3; n = i/j; 10 делится на 3. Результат усекается до 3 и имеет тип int. % Остаток от деления int i = 10, j = 3; n = i % j; n присваивается остаток от деления 10 на 3, т.е. 1. + Сложение int i = 4; float х [ ]; float * px; px = & x [4] + i; Целочисленный операнд i складывается с адресом пятого элемента массива х. Значение i умножается на длину типа float и складывается с & х [4]. Значе- ние результирующего указателя представляет адрес х f 8] элемента массива. Вычитание int i = 4, j; float x [ ]; j = & x [i] - & x [i—2J; Адрес третьего элемента x (задается как х [i-2]) вычитается из адреса пятого элемента х (задается как х [i]). Полученная разность делится на длину типа float. В результате получается целая величина 2. Операндами операции % должны быть целые числа. Операции сложения, и вычитания, умножения и деления выполняются над целыми операндами и операндами с плавающей запятой. Типы первого и второ- го операндов могут отличаться. Арифметические операции выполняют обычные арифметические преобразования операндов. Типом результата является тип операндов после преобразования. В операциях сложения и вычитания один операнд может быть ука- зателем, а другой — целой величиной. Когда целая величина складыва- ется (или вычитается) с указателем, то она (например, i) преобразуется путем умножения ее на размер памяти, занимаемый величиной, адре- суемой указателем. После преобразования целая величина представляет i позиций памяти, где каждая позиция имеет длину, специфицирован- ную адресным типом. Когда преобразованная целая величина складыва- ется (вычитается) с величиной указателя, то результатом является ука- затель, адресующий память, расположенную на i позиций дальше (бли- же) от исходного адреса. Новый указатель адресует тот же самый тип данных, что и исходный указатель. 357
Логические операции Знак Операция Примеры 1 Логическое НЕ unsigned short у, х; if(!(x<y)); Если х больше или равно у, то результат выражения равен 1 (true). Если х меньше у, то результат равен 0 (false). && Логическое И int х,у; if (х < у && у < z) printf ("х is less than z/n"); Если x меньше чем у и у меньше чем z, вызывается функция printf для печати сообщения. Если х больше чем у, то второй операнд (y<z) не вычисляется и печа- ти не происходит. II Логическое ИЛИ int х,у; if (х = = у || х = = z) printf ("х is equal to either у or z/n"); Сообщение печатается, если х равен у или z. Если х равен у то второй операнд не вычисляется. Операция логического не "!" вырабатывает значение 0, если операнд есть true и значение 1, если операнд есть false. Результат имеет тип int. Опе- ранд должен быть целого, адресного типа или типа с плавающей запятой. Операнды логических операций И (&&) и ИЛИ (||) могут быть целого, адресного типа или типа с плавающей запятой. Типы первого и второго операндов могут быть различными. Операнды логических выражений вы- числяются слева направо. Если значения первого операнда достаточно, чтобы определить результат операции, то второй операнд не вычисляется. Логические операции не выполняют стандартные арифметические преобразования. Вместо этого они вычисляют каждый операнд с точки зрения его эквивалентности нулю. Указатель имеет значение 0, если это значение явно установлено путем присваивания или инициализации. Результатом логической операции является 0 или 1. Тип результата есть int. Операции отношения Бинарные операции отношений сравнивают первый операнд со вторым и вырабатывают значение l(true) и 0 (false). Типом результата является int. Знак Отношение Примеры < Первый операнд меньше, чем второй операнд int х = 0, у = 0; х<у Выражение имеет значение 0 358
Продолжение табл. Знак Отношение Примеры > Первый операнд больше, чем второй операнд int х = 0, у = 0; у >х Выражение имеет значение 0 <== Первый операнд меньше или равен второму операнду int х = 0, у = 0; х<=у Выражение имеет значение 1 >= Первый операнд больше или равен второму операнду int х = 0, у = 0; х >—у Выражение имеет значение 1 — Первый операнд равен вто- рому операнду int х = 0, у = 0; х = = у Выражение имеет значение 1 I = Первый операнд не равен второму операнду int х = 0, у = 0; х != у Выражение имеет значение 0 Операнды могут быть целого, вещественного или адресного типов. Типы первого и второго операндов могут различаться. Преобразования типов выполняются над целыми и вещественными операндами. Первый или второй операнды операций равенства "= —' или не- равенства "! =" могут быть типа enum. Величина типа enum преобра- зуется точно так же , как и величины типа int. Операндами любой операции отношения могут быть два указателя одного и того же типа. Для операций равенства или неравенства резуль- тат сравнения показывает, равны ли оба адреса в указателях. Результат сравнения указателей для других операций (<,>,<=,>=) отражает отно- сительное положение двух адресов памяти. Операции с битами информации Знак Операция Примеры Двоичное дополнение (вырабатывает двоич- ное дополнение своего операнда) unsigned short у = ОхАААА; у = ~у; у присваивается значение 0x5555 Сдвиг первого операн- да влево на число би- тов, заданное вторым операндом. unsigned int x,z; х = ОхООаа; z = х « 8; х сдвигается влево на 8 позиций и z при- сваивается величина ОхааОО 359
Продолжение табл. Знак Операция Примеры Сдвиг первого операн- да вправо на число битов, заданное вторым операндом. unsigned int y,z; у = 0x5500; z = у » 8; у сдвигается вправо на 8 позиций и z присваивается величина 0x0055 & Побитовое И short i = OxABOO; short j = OxABCD, n; n = i&j; n присвоится результат равный значению i (AB00) 1 Побитовое ИЛИ short i = OxABOO; short j = OxABCD, n; n = i(j; результатом будет величина ABCD Побитовое исключаю- щее ИЛИ short i = OxABOO; short j = OxABCD, n; n = iAj; результатом будет значение CD В операции двоичного дополнения операнд должен быть целого типа. Результат имеет тип преобразованного операнда. В операциях сдвига оба операнда должны быть целыми величина- ми. Тип результата — это тип левого операнда после преобразования. При сдвиге влево правые освобождающиеся биты устанавливаются в нуль. При сдвиге вправо метод заполнения освобождающихся левых битов зависит от типа, полученного после преобразования первого опе- ранда. Если тип unsigned, то свободные левые биты устанавливаются в нуль.В противном случае они заполняются копией знакового бита. Ре- зультат операции сдвига не определен, если второй операнд отрица- тельный. Операнды побитовых операций И (&), ИЛИ (|) и исключающего ИЛИ (л) должны быть целого типа, но их типы могут быть отличными. Тип результата определяется типом операндов после преобразования типов операндов. Если значение указателя в операции перехода по адресу равно ну- лю, то результат непредсказуем. Результат операции адресации является указателем на операнд. Тип, адресуемый указателем, является типом операнда. Операция адресации не может применяться к идентификаторам класса памяти register. 360
Операции адресации и перехода по адресу Знак Операция Примеры ♦ Переход по адресу через указатель. Результатом операции является величи- на, на которую указывает операнд int *ра, х; х = *ра; операция перехода по адресу исполь- зуется для доступа к величине типа int, адрес которой сохранен в ра. Зна- чение результата присваивается це- лочисленной переменной х. & Адресация, вырабатывает адрес своего операнда int *ра; ра = &а[5]; операция адресации вырабатывает адрес шестого элемента массива а. Результат записывается в адресную переменную (указатель) ра. Операции присваивания Операции присваивания в Си могут вычислять и присваивать зна- чения в одной операции. Используя составные операции присваивания вместо двух отдельных операций, можно сократить код программы и улучшить ее эффективность. Знак Операция Примеры Унарный инкремент (увеличение значения операнда на 1) if (pos++ > 0) *ptt = *qtt; Переменная pos сначала сравнивается с 0, а затем инкрементируется. Унарный декремент (уменьшение значения операнда на I) if (line [—i] ! = Vi') return; Переменная i декрементируется перед ее использованием в качестве индекса line. Простое присваивание double х; int у; х = у; Значение у преобразуется к типу double и присваивается х. ♦= Умножение с при- сваиванием double х; int у; х *= у; Операция аналогична простому присваи- ванию х = х * у /= Деление с присваива- нием double х; int у; х /= у; Операция аналогична простому присваи- ванию х = х / у 361
Продолжение табл. Знак Операция Примеры %= Остаток от деления с присваиванием double х; int у; х %= у; Операция аналогична простому присва- иванию х = х % у Сложение с присваи- ванием double х; int у; х += у; Операция аналогична простому присва- иванию х = х + у Вычитание с присваиванием double х; int у; х-= у; Операция аналогична простому присва- иванию х = х - у Сдвиг влево с при- сваиванием double х; int у; х «= у; Операция аналогична простому присва- иванию х = х « у Сдвиг вправо с при- сваиванием double х; int у; х »= у; Операция аналогична простому присва- иванию х = х » у &= Побитовое И с при- сваиванием double х; int у; х &= у; Операция аналогична простому присва- иванию х = х & у 1= Побитовое включаю- щее ИЛИ с присваи- ванием double х; int у; х|=у; Операция аналогична простому присва- иванию х - х | у Побитовое исклю- чающее ИЛИ с при- сваиванием double х; int у; х л= у; Операция аналогична простому присва- иванию х = х Л у При присваивании тип правого операнда преобразуется к типу ле- вого операнда. Унарная операция присваивания (++ и —) инкрементирует или декрементирует свой операнд. Операнд должен быть целого, адресного типа или типа с плавающей запятой. Операнды целого или веществен- ного типа преобразуются путем сложения или вычитания целой 1. Тип результата соответствует типу операнда. Операнд адресного типа ин- крементируется или декрементируется размером объекта, который он адресует. Инкрементированный указатель адресует следующий объект, а декрементированный указатель — предыдущий. 362
Операции инкремента (++) или декремента (—) могут появляться перед или после своего операнда. Когда операция появляется перед сво- им операндом, то величина операнда инкрементируется или декре- ментируется и становится результатом вычисления выражения. Когда операция стоит после своего операнда, то увеличение или уменьшение значения операнда происходит после вычисления всего выражения и использования результата. Операция составного присваивания состоит из простой операции присваивания, скомбинированной с другой бинарной операцией. В со- ставном присваивании вначале выполняется операция, специфициро- ванная аддитивным оператором, а затем результат присваивается левому операнду. Выражение составного присваивания, например, имеет вид: <выражение 1> += <выражение 2> и может быть понято как: <выражение 1> = <выражение 1> + <выражение 2> Операция последовательного вычисления Операция последовательного вычисления вычисляет два своих операнда последовательно слева направо. Результат операции имеет значение и тип второго операнда. Примеры: for ( i = j = 1; i+j< 20; i += i, j —); /* Пример 1 */ funk_one (x, y+2, z) ; /* Пример 2 */ funk_two ( (x—, y+2), z); В первом примере каждый операнд третьего выражения оператора for вычисляется независимо. Левый операнд i += i вычисляется первым, затем вычисляется j—. Во втором примере показано, что символ "запятая" используется как разделитель в двух контекстах. В вызове функции funk one три ар- гумента, разделенных запятыми, посылаются в вызываемую функцию: х, y+2, z. Использование симовола "запятая" как разделителя не нужно путать с ее использованием как операции. Оба использования совер- шенно различны. В вызове функции funk_two скобки вынуждают компилятор ин- терпретировать первую запятую как операцию последовательного вы- числения. Этот вызов функции посылает два аргумента к funk_two. Первый аргумент — это результат операции последовательного вычисления (х—, у+2), имеющий значение и тип выражения у+2 . Второй аргумент есть z. 363
Условная операция В Си есть одна тернарная операция — это условная операция Она имеет следующее синтаксическое представление: <операнд 1>?<операнд 2>:<операнд 3> Выражение <операнд 1 > вычисляется с точки зрения его равенства нулю. Оно может быть целого, вещественного или адресного типа. Если <операнд 1> имеет ненулевое значение, то вычисляется <операнд 2> и ре- зультатом условной операции является значение выражения <операнд 2>. Если <операнд 1> равен нулю, то вычисляется <операнд 3> и результа- том является значение выражения <операнд 3>. Пример: j = (КО) ? (-i) : (i); В примере j присваивается абсолютное значение i. Если i меньше нуля, то j присваивается -i. Если i больше или равно нулю, то j присваи- вается i. Старшинство и порядок выполнения Старшинство операций имеет смысл только при наличии несколь- ких операций, имеющих разные приоритеты. Выражения с более при- оритетными операциями вычисляются первыми. В табл. 6.9 приведены старшинство и порядок выполнения операций в Си. Старшинство опе- раций уменьшается сверху вниз. Операции, расположенные в одной строке таблицы или объединенные в группу, имеют одинаковое стар- шинство и одинаковый порядок выполнения. Таблица 6.9. Старшинство и порядок выполнения операций в Си Операции Порядок вы- полнения Операции Порядок вы- полнения ()[]-> Слева направо А Слева направо —!*&++— Справа налево Слева направо ♦/% Слева направо && Слева направо + - Слева направо II Слева направо « » Слева направо ?: Справа налево о <= >= Слева направо 1? А 11 ft * 11 II v Тг v I и £ „ и + и п 7 li Справа налево = != Слева направо Слева направо & Слева направо 364
Следует помнить, что логические операции вычисляют ми- нимальное число операндов, необходимое для определения результата выражения. Таким образом, некоторые операнды выражения могут быть не вычислены. Например, в выражении х && у ++ второй операнд у ++ вычисляется только тогда, когда х есть true (не нуль). Так что у не ин- крементируется, когда х есть false (нуль). В нижеследующих примерах показано группирование по умолча- нию для различных выражений. Выражение Группирование по умолчанию а & b || с (а & Ь) || с а = b || с а = (Ь || с) q && г || s— (q && г) || s— В первом примере побитовая операция И (&) имеет большее стар- шинство, чем логическая операции ИЛИ (||), поэтому выражение а & b является первым операндом логической операции ИЛИ. Во втором примере логическая операция ИЛИ (||) имеет большее старшинство, чем операция простого присваивания, поэтому выражение b || с представляет правый операнд в присваивании. Заметим, что значе- ние , присваиваемое а, есть нуль или единица. В третьем примере показано корректно оформленное выражение, которое может выработать неожиданный результат. Логическая опера- ция И (&&) имеет более высокое старшинство, чем логическая операция ИЛИ (||), поэтому запись q && г группируется в операнд. Так как логи- ческие операции обеспечивают вычисление операндов слева направо, то выражение q && г вычисляется раньше, чем s—. Однако, если q && г выработает ненулевое значение, то s— не будет вычисляться и s не декрементируется. Чтобы корректно решить эту проблему, необходимо чтобы s-- появилось в качестве первого операнда выражения, либо дек- рементировано отдельной операцией. В следующем примере показано неверное выражение, которое вы- рабатывает программную ошибку. Неверное выражение Группирование по умолчанию р == 0 ? р += 1 : р += 2 (р = 0 ? р += 1 : р) += 2 В этом примере операция эквивалентности (=) имеет наибольшее старшинство, поэтому р = 0 группируется в качестве операнда. Тер- нарный оператор (?:) имеет следующее старшинство. Его первым опе- рандом является выражение р ==0, вторым операндом является вы- ражение р +=1. Однако, последним операндом тернарной операции бу- 365
дет рассмотрен р, а не р += 2, так как в данном случае р по старшинству операций связан более тесно с тернарной операцией, а не с сотавной операцией присваивания. В результате будет выработана синтаксиче- ская ошибка, поскольку += 2 не имеет левого операнда (Lvalue- выражения). Чтобы предупредить ошибки подобного рода и сделать программу более наглядной, рекомендуется использовать скобки. Предыдущий пример может быть корректно оформлен следующим образом: (р == 0) ? (р+= 1): (р +=2) Преобразования типов Преобразование типов имеет место, когда тип значения, которое присваивается переменной, отличается от типа переменной. Пре- образование типов выполняется, когда операция перед вычислением преобразует тип своего операнда или операндов и когда преобразуется значение, посылаемое как аргумент функции. Преобразование типов при присваивании. В операциях присваива- ния тип значения, которое присваивается, преобразуется к типу пере- менной, получающей это значение. В Си допускаются преобразования при присваивании между целыми и вещественными типами, даже в слу- чаях, когда преобразование влечет за собой потерю информации. Преобразование знаковых целых типов представлено в табл. 6.10. В таблице предполагается, что типы char являются знаковыми по умолча- нию. Преобразования типов unsigned char даны в табл. 6.11. Таблица 6.10. Преобразование знаковых целых типов Из В Метод char short дополнение знаком char long дополнение знаком char unsigned char сохранение битов; старший бит теряет функцию зна- кового бита char unsigned short дополнение знаком до short, преобразование short в unsigned short char usigned long дополнение знаком до long; преобразование long в unsigned long char float дополнение знаком до long; преобразование long в float char double дополнение знаком до long; преобразование long в double 366
Продолжение табл. Из В Метод short char сохранение младшего байта short long размножение знака short unsigned char сохранение битов; старший бит теряет функцию знакового бита short unsigned long дополнение знаком до long; преобразование long в unsigned long short float дополнение знаком до long; преобразование long к float short double дополнение знаком до long; преобразование long в double long char сохранение младшего байта long short сохранение младшего слова long unsigned char сохранение младшего байта long unsigned short сохранение младшего слова long unsigned long сохранение всех битов; старший бит теряет функ- цию знакового бита long float представляется как float; если long не может быть представлено точно, то происходит некоторая поте- ря точности long double представляется как double; если long не может быть представлено точно как double происходит некото- рая потеря точности Замечание. Тип int эквивалентен или short типу или типу long в зависимости от оборудования. Преобразование значений int производится как для short или long, в зависимости от того, что подходит. Преобразование беззнаковых целых типов. Беззнаковое целое пре- образуется к короткому беззнаковому или знаковому целому путем усе- чения старших битов. Беззнаковое целое преобразуется к длинному без- знаковому или знаковому целому путем размножения нуля. Беззнако- вые целые преобразуются к вещественным величинам путем преобразования к ближайшему знаковому целому того же самого раз- мера, а затем преобразования этой знаковой величины к величине с пла- вающей точкой. Когда беззнаковое целое преобразуется к знаковому целому того же размера, то состояние битов не меняется. Однако значение этого представления изменится, если был установлен знаковый бит. Преобразование беззнаковых целых типов представлено в табл. 6.11. 367
Таблица 6.11. Преобразование беззнаковых целых типов Из В Метод unsigned char char сохраняются все биты; старший бит стано- вится знаковым unsigned char short дополнение нулем unsigned char long дополнение нулем unsigned char unsigned short дополнение нулем unsigned char unsigned long дополнение нулем unsigned char float преобразование к long, преобразование long в float unsigned char double преобразование к long, преобразование long к double unsigned short char сохранение младшего байта unsigned short short сохранение всех битов, старший бит стано- вится знаковым unsigned short long дополнение нулем unsigned short unsigned char сохранение младшего байта unsigned short unsigned long дополнение нулем unsigned short float преобразование к long, преобразование long в float unsigned short double преобразование к long, преобразование long к double unsigned long char сохранение младшего байта unsigned long short сохранение младшего слова unsigned long long сохранение всех битов; старший бит стано- вится знаковым unsigned long unsigned char сохранение младшего байта unsigned long unigned short сохранение младшего слова unsigned long float преобразование к long, преобразование long к float unsigned long double преобразование к long, преобразование long к double Замечание. Тип unsigned int эквивалентен или unsigned short, или unsigned long типам в зависимости от оборудования. Преобразование из unsigned int производятся как для unsigned short или unsigned long в зависимости от того, что подходит. Преобразование вещественных типов. Величины float преобразу- ются к double, не меняясь в значении. Величины double, преобразован- ные к float, представляются точно, если возможно. Если значение слиш- ком велико для float, то точность теряется. Вещественные величины преобразуются к целым типа long. Преоб- 368
разование к другим целым типам выполняется как для long. Дробная часть величины с плавающей точкой отбрасывается при преобразовании к long; если результат слишком велик для long, то результат преобразо- вания нео пределен. Преобразования вещественных типов сведены в табл. 6.12. Таблица 6.12. Преобразования вещественных типов Из В Метод float char преобразуется к long, long преобразуется к char float short преобразуется к long, long преобразуется в short float long усечение дробной части; результат неопределен, если ои слишком велик для представления в long float unsigned short преобразуется к long, long преобразуется к unsigned short float unsigned long преобразуется к long, long преобразуется к unsigned long float double изменение внутреннего представления double char преобразование к float, преобразование float к char double short преобразование к float, преобразование float к short double long усечение дробной части; результат неопределен, если он слишком велик для представления в long double unsigned short преобразование к long, преобразование long к unsigned short double unsigned long преобразование к long, преобразование long к unsigned long double float представляется как float; если значение double ие может быть точно представлено как float, то точ- ность теряется; если значение слишком велико для представления как float, то результат неопре- делен 6.6. Операторы языка Операторы Си управляют процессом выполнения программы. В Си, как и в других языках программирования, имеются условные опе- раторы, операторы цикла, выбора, передачи управления и т.д. Оператор-выражение. Синтаксис: <Выражение>; 369
В Си присваивания являются выражениями. <Выражение> вычис- ляется в соответствии с правилами выражения и присваивания. Примеры: х= (у+3) ; — х присваивается значение у+3. х++; — х инкрементируется (увеличивается на 1). f (х) ; — выражение функционального вызова. Значением выражения является значение, возвращаемое функцией. Если функция возвращает значение, то обычно оператор-выражение содержит опера- цию присваивания, чтобы запомнить значение возврата вызванной функции. Если возвращаемая величина не используется, как в данном примере, вызов функции выполняется, но возвращаемая величина (если она есть) не используется. Оператор GOTO и помеченные операторы Синтаксис: goto <метка>; <метка>: <оператор> Оператор goto передает управление непосредственно на оператор, помеченный меткой <метка>. Метка — это простой идентификатор, каждая метка должна быть отлична от других меток в той же самой функции. Помеченный оператор выполняется сразу после выполнения опера- тора goto. Если оператор с данной меткой отсутствует или существует более одного оператора, помеченных одной и той же меткой, то это приводит к ошибочному результату. Пример: if (errorcode>0) goto exit; exitcreturn (errorcode); В примере оператор goto передает управление на оператор, поме- ченный меткой exit, когда происходит ошибка. Оператор if Синтаксис: 370
if (<выражение>) «оператор 1> [else «оператор 2>] Тело оператора if выполняется селективно, в зависимости от значе- ния выражения. Сначала вычисляется выражение. Если значение выра- жения истина (не нуль), то выполняется оператор «оператор 1>. Если выражение ложно, то выполняется оператор «оператор 2», непосредст- венно следующий за ключевым словом else. Если <выражение> ложно и предложение else опущено, то управление передается на выполнение оператора, следующего за оператором if. Пример: if (i>0) у=х/i; else { x=i; y=f(X); } В примере выполняется оператор y=x/i, если i больше нуля. Если i меньше или равно нулю, то значение i присваивается переменной х и возврат функции f(x) присваивается переменной у. Оператор switch Синтаксис: switch («выражение») { case «константное выражение»: [<оператор>] [case «константное выражением [«оператор»]] [default: «оператор»] } Оператор switch передает управление одному из операторов «опе- ратор» своего тела. Оператор, получающий управление, — это тот опе- ратор, для которого case-константное выражение равно значению switch-выражения в круглых скобках. 371
Выполнение тела оператора начинается с выбранного оператора и продолжается до конца тела или до тех пор, пока очередной оператор не передает управление за пределы тела. Оператор default выполнится, если case-константное выражение не равно значению switch-выражения <выражение>. Если default-оператор опущен, а соответствующий case не найден, то выполняемый оператор в теле switch отсутствует. Switch-выражение — это целая величина размера int или короче. Оно может быть также величиной типа enum. Если <выражение> короче чем int, оно расширяется до int. Каждое case-константное выражение преобразуется к типу switch- выражения. Значение каждого case-константного выражения должно быть уникальным внутри тела оператора. Case и default метки в теле оператора switch существенны только при начальной проверке, когда определяется стартовая точка для вы- полнения тела оператора. Все операторы, появляющиеся между старто- вым оператором и концом тела, выполняются независимо от их меток. В заголовке составного оператора, формирующего тело оператора switch, могут появиться объявления, но инициализаторы, включенные в объявления, не будут выполнены. Назначение оператора switch состоит в том, чтобы передать управление непосредственно на выполняемый оператор внутри тела, обойдя строки, которые содержат инициализацию. Примеры: switch (с) { case 'А': сара++; case 'а': lettera++; default: total++; } Все три оператора в теле switch выполняются, если с равно 'А'. Пе- редача управления осуществляется на первый оператор сара++, далее операторы выполняются в порядке их следования в теле. Если с равно 'а', то переменные lettera и total инкрементируются. Наконец, если с не равно ни 'А', ни 'а', то инкрементируется только пе- ременная total. switch <i) { case -1: n++; break; 372
case 0: z++; break; case 1: p++; break; В теле switch после каждого оператора следует оператор break. Оператор break осуществляет принудительный выход из switch. По- следний оператор break не является обязательным, поскольку без него управление было бы передано из тела на конец составного оператора. Множественные метки. Оператор тела switch может быть помечен множественными метками, как показано в нижеследующем примере: case 'а': case 'b': case 'с': case 'd': case 'е': case 'f: C++; Оператор do Синтаксис: do <оператор> while (<выражение>); Тело оператора do выполняется один или несколько раз (цикл с по- стусловием), пока выражение <выражение> истинно (равно единице). Вначале выполняется оператор <оператор> тела, затем вычисляется вы- ражение <выражение>. Если выражение ложно, то оператор do завер- шается и управление передается следующему оператору в программе. Если выражение истинно (не равно нулю), то тело оператора выполня- ется снова и снова проверяется выражение. Выполнение тела оператора продолжается до тех пор, пока выражение не станет ложным. Оператор do может также завершить выполнение при выполнении операторов break, goto или return внутри тела оператора do. Пример: do { y=f(х); х—; } while (х>0); 373
Вначале выполняются два оператора y=f(x) и х—; не обращая вни- мания на начальное значение х. Затем вычисляется выражение х>0. Ес- ли х>0, то тело оператора выполняется снова, и снова перевычисляется выражение х>0. Тело оператора выполняется до тех пор, пока х не ста- нет меньше или равным нулю. Оператор for Синтаксис: for ([<инициализация>];[<условие>];[<выражение цикла>]) <оператор> Тело оператора for (цикл с предусловием) выполняется нуль и более раз до тех пор, пока условное выражение <условие> не станет ложным. Выражения инициализации и цикла могут быть использованы для инициа- лизации и модификации величин во время выполнения оператора for. Первым шагом при выполнении оператора for является вычисление выражения инициализации, если оно имеется, затем — вычисление ус- ловного выражения с тремя возможными результатами. 1. Если условное выражение истинно (не равно нулю), то выполня- ется тело оператора. Затем вычисляется выражение цикла (если оно есть). Процесс повторяется снова с вычисления условного выражения. 2. Если условное выражение опущено, то его значение принимается за истину, и процесс выполнения продолжается, как показано выше. В этом случае оператор for может завершиться только при выполнении в теле оператора операторов break, goto, return. 3. Если условное выражение ложно, то выполнение оператора for заканчивается и управление передается следующему оператору в про- грамме. Оператор for может завершиться при выполнении операторов break, return, goto в теле оператора. Пример: for (i=space=tab=O; 1<МАХ; i++) { if (line [i] == '\x20') space++; if (line [i] == '\t') { tab++; line [i] = '\x20'; } } В приведенном примере подсчитывается количество символов «пробел» ('\х20') и «табуляция» ('\t') в массиве символов, поименован- ном line, и замена каждого символа табуляции на пробел. 374
Сначала i, space и tab инициализируются нулем. Затем i сравнива- ется с константой МАХ. Если i меньше МАХ, то выполняется тело опе- ратора. В зависимости от значения line[i], выполняются операторы if. Затем переменная i инкрементируется и снова сравнивается с кон- стантой МАХ. Тело оператора выполняется до тех пор, пока значение i не станет больше или равно МАХ. Оператор while Синтаксис: while (<выражение>) <оператор> Тело оператора while выполняется нуль или более раз до тех пор, пока выражение <выражение> станет ложным (равным нулю). Вначале вычисляется выражение <выражение>. Если <выражение> ложно, то тело оператора while не выполняется и управление передается на сле- дующий оператор программы. Если <выражение> является истиной (не нуль), то выполняется тело оператора. Перед каждым следующим вы- полнением тела оператора <выражение> перевычисляется. Оператор while может также завершиться при выполнении операторов break, goto, return внутри тела while. Пример: while (i>=0) { stringl [i] = string2 [i] ; } В примере копируются символы из string2 в stringl. Если i больше или равно нулю, то string2[i] присваивается индексной переменной stringl [i] и i декрементируется. Когда i становится меньше нуля, то вы- полнение оператора while завершается. Составной оператор Синтаксис: { [<описания>] [<оператор>] } Выполнение операторов осуществляется в порядке их появления, за исключением случаев, когда очередной оператор явно передает управ- 375
ление в другое место. Пример: if (i>0) { linne [i]=x; x++; } Типично появление составного оператора в качестве тела другого оператора, например, такого, как оператор if. В приведенном примере, если i больше нуля, то последовательно выполняются операторы со- ставного оператора. Оператор break Синтаксис: break; Оператор break прерывает выполнение операторов do, for, switch или while, в которых он появляется. Управление передается оператору, следующему за прерванным. Появление оператора break вне операторов do, for, switch, while приводит к ошибке. Пример: for (i=0; i<LENGTH-l;i++) { for (j=0;j<WIDTH-l;j++) { if (lines [i][j] == '\0') { length[i] =j; break; } } } В примере построчно обрабатывается массив lines строк перемен- ной длины. Оператор break прерывает выполнение внутреннего опера- тора for после того, как найден символ конца строки (\0). При этом эле- менту lengthfi] одномерного массива присваивается значение длины в байтах i-ой строки. Управление передается внешнему оператору for. Переменная i инкрементируется и процесс повторяется до тех пор, пока значение i не станет больше или равно константе LENGTH-1. Оператор continue Синтаксис: continue; 376
Оператор continue передает управление на следующую итерацию в операторах цикла do, for, while, в которых он может появиться. Остав- шиеся операторы в теле вышеперечисленных циклов при этом не вы- полняются. Внутри do или while циклов следующая итерация начинает- ся с перевычисления выражения do или while операторов. Для оператора for следующая итерация начинается с выражения цикла оператора for. Пример: while (i-->0) { x=f(i) ; if (x==l) continue; y=X*X; } Тело оператора выполняется , если i>0. Сначала f(i) присваивается значение х, затем, если х равно 1, выполняется оператор continue. Ос- тальные операторы тела игнорируются и выполнение возобновляется с заголовка цикла, т.е. вычисляется выражение i—>0. Оператор return Синтаксис: return [<выражение>]; Оператор return заканчивает выполнение функции, в которой он появляется, и возвращает управление в вызывающую функцию. Управ- ление передается в точку, непосредственно следующую за вызовом. Значение выражения <выражение>, если оно указано, возвращается в вызывающую функцию. Если выражение <выражение> опущено, то возвращаемая функцией величина не определена. Пример: main () { void draw (int,int); long sq (int); y=sq (x); draw (x,y); } long sq (x) int x; { return (x*x); } void draw (x,y) 377
int x,y; { return; } Функция main вызывает две функции, sq и draw. Функция sq воз- вращает значение х*х в main. Величина возврата присваивается пере- менной у. Функция draw объявляется как функция void и не возвращает значения. Попытка присвоить возвращаемое значение функции draw привело бы к ошибке. Отсутствие оператора return. Если оператор return не появился в определении функции, то управление автоматически передается в вы- зывающую функцию после выполнения последнего оператора в вы- званной функции. Значение возврата вызванной функции при этом не определено. Если значение возврата не требуется, то функция должна быть объявлена с типом возврата void. Оператор null Синтаксис: Оператор null — пустой оператор. Он может появиться в любом месте, где требуется оператор. Когда выполняется оператор null, ничего не происходит. Пример: for (i=0; i<10; linee (i++]=0) f Такие операторы, как do, for, if, while, требуют, чтобы в их теле был хотя бы один оператор. Оператор null удовлетворяет требованиям син- таксиса в случаях, когда не требуется тела оператора. В приведенном примере третье выражение оператора for инициализирует первые 10 элементов массива line нулем. Тело оператора включает оператор null, так как нет необходимости в других операторах. Помеченный оператор null. Оператор null, подобно любому друго- му Си оператору, может быть помечен меткой. Чтобы пометить объект, который не является оператором, такой как закрывающаяся фигурная скобка составного оператора, можно вставить перед объектом помечен- ный оператор null. 378
6.7. Функции Функция (как аналог подпрограммы) — это независимая совокуп- ность объявлений и операторов, обычно предназначенная для выполне- ния определенной задачи. Программы на Си состоят по крайней мере из одной функции main, но могут содержать и другие функции. Определение функции специфицирует имя функции, ее формальные параметры, объявления и операторы, которые реализуют ее алгоритм. В определении функции может быть задан также тип возврата и ее класс памяти. Вызов функции передает управление из вызывающей функции к вызванной. Действительные аргументы, если они есть, передаются по значению в вызванную функцию. При выполнении оператора return в вызыванной функции управление и, возможно, значение возврата пере- даются в вызывающую функцию. Определение функции Синтаксис определения функции следующий: [<класс памяти>][<спецификатор типа>]<описание> ([<список параметров»]) [<описания параметров»] <тело функции» Спецификатор <класс памяти» задает класс памяти функции, кото- рый может быть или static или extern. «Спецификатор типа» и <описание> определяют тип возврата и имя функции. <Список параметров» — это список (возможно пустой) формаль- ных параметров, которые используются функцией. Объявления пара- метров «описания параметров» задают типы формальных параметров. <Тело функции» — это составной оператор, содержащий объявле- ния локальных переменных и операторы. Класс памяти. Спецификатор класса памяти в определении функции определяет функцию как static или extern. Функция с классом памяти static доступна только в том исходном файле, в котором она определена. Все другие функции с классом памяти extern, заданным явно или неявно, дос- тупны во всех исходных файлах, которые образуют программу. Если спецификатор класса памяти опускается в определении функ- ции, то подразумевается класс памяти extern. Тип возвращаемого значения. Тип возврата функции определяет размер и тип возвращаемого значения. Объявление типа имеет следую- щий синтаксис: [«спецификатор типа»], 379
где «спецификатор типа> определяет тип возврата функции. Если «спе- цификатор типа> не задан, то подразумевается, что тип возврата int. Функции не могут возвращать массивов или функций, но они могут возвращать указатели на любой тип, включая массивы и функции. Тип возврата, задаваемый в определении функции, должен соответствовать типам возвратов, заданных в объявлениях этой функции, сделанных где- то в программе. Функции с типом возврата int могут не объявляться перед вызовом. Функции с другими типами возвратов не могут быть вызваны прежде, чем они будут определены или объявлены. Тип значения возврата функции используется только тогда, когда функция возвращает значение, которое вырабатывается, если выполня- ется оператор return, содержащий выражение. Выражение вычисляется, преобразуется к типу возврата, если это необходимо, и возвращается в точку вызова. Если оператор return не выполняется или если выполняе- мый оператор return не содержит выражения, то значение возврата функции не определено. Если в этом случае вызывающая функция ожи- дает значение возврата, то поведение программы также не определено. Пример: static add (х,у) int х,у { return (х+у); } В этом примере по умолчанию тип возврата функции add определен как int. Функция имеет класс памяти static. Это означает, что функция мо- жет быть вызвана только функциями того же самого исходного файла. Формальные параметры. Формальные параметры объявляются в списке параметров в начале описания функции. Список параметров оп- ределяет имена параметров и порядок, в котором они принимают значе- ния при вызове функции. Список параметров состоит из нуля или более идентификаторов, разделенных запятыми. Список должен быть ограничен круглыми скоб- ками даже в случае, когда он пуст. После последнего идентификатора в списке параметров может поя- виться запятая с последующим многоточием (,...), это означает, что чис- ло аргументов функции переменно. Однако предполагается, что функ- ция по крайней мере имеет столько аргументов, сколько следует иден- тификаторов перед последней запятой. Список параметров может состоять только из многоточия (...) и не содержать идентификаторов. Это означает, что число параметров функ- ции переменно и может быть равным нулю. 380
Объявления параметров определяют тип и размер величин, за- поминаемых в формальных параметрах. Эти объявления имеют тот же самый синтаксис, что и другие объявления переменных. Формальные параметры могут быть основного, структурного, адресного типов или типа массив. Параметры могут иметь только классы памяти auto и register. Если класс памяти не задан, то подразумевается класс памяти auto. Если формальный параметр представлен в списке параметров, но не объяв- лен, то предполагается, что параметр имеет тип int. Идентификаторы формальных параметров используются в теле функции в качестве ссылок на величины, передаваемые функции. Эти идентификаторы не могут быть использованы для переменных, объяв- ляемых внутри тела функции. Тип каждого формального параметра должен соответствовать типу фактического аргумента и типу соответствующего аргумента в списке типов аргументов функции, если такой список имеется. Если требуется, компилятор выполняет обычные арифметические преобразования для каждого формального параметра и каждого фактического аргумента независимо. Пример: struct student { char name [20]; int id; long class; struct student ‘nextstu; } student; main () { int match (struct student *, char *); if (match(student.nexstu ,student.name)>0) { { } } match (r,n) struct student *r; char *n; { int i=0; while (r->name [i] == n[i]) if (r->name [i++] == '\0') 381
return (r->id); return (0); } В примере содержатся: объявление структурного типа, объявление функции match, вызов match и определение функции match. Заметим, что одно и то же имя student может быть использовано без противоре- чий для тела структуры и имени структурной переменной. Функция match объявлена с двумя аргументами. Первый аргумент — это указатель на структуру типа student, второй — указатель на объект типа char. Функция вызывается с двумя аргументами. Оба аргумента являют- ся элементами переменной student структурного типа student. Имя массива, заданное в качестве второго аргумента в вызове функции, преобразуется к указателю на char. Соответствующий фор- мальный параметр также объявлен как указатель на char и используется в выражении как идентификатор массива. Так как идентификатор мас- сива рассматривается как адресное выражение, то результат объявления формального параметра как char *п будет тем же самым, что и char п []. Внутри функции локальная переменная i определяется и использу- ется в качестве индекса массива. Функция возвращает структурный элемент id, если элемент структуры name найден в массиве п, в против- ном случае функция возвращает нуль. Тело функции. Тело функции — это составной оператор. Состав- ной оператор содержит операторы, которые определяют действия функ- ции, и может также содержать объявления переменных, используемых в этих операторах. Все переменные, объявленные в теле функции, имеют тип памяти auto, если они не объявлены иначе. Когда вызывается функ- ция, то создается память для локальных переменных и производится их инициализация (если она задана). Управление передается первому опе- ратору составного оператора и начинается процесс выполнения, кото- рый продолжается до тех пор, пока не встретится оператор return или конец тела функции. Управление при этом возвращается в точку вызова. Если функция возвращает значение, то должен быть выполнен опе- ратор return, содержащий выражение. Значение возврата не определено, если не выполнен оператор return или еслив оператор return не было включено выражение. Фактические аргументы. Фактические аргументы могут быть любой величиной основного, структурного, совмещающего или адрес- ного типов. Хотя массивы и функции не могут быть переданы как пара- метры, указатели на эти объекты могут передаваться. Все фактические аргументы передаются по значению. Копия фактических аргументов присваивается соответствующим формаль- 382
ным параметрам. Функция использует эти копии, не влияя на перемен- ные, с которых копия была сделана. Путь доступа из функции к значениям оригинальных переменных обеспечивают указатели. Т.к. указатель на переменную содержит адрес переменной, то функция может использовать этот адрес для доступа к зна- чению переменной. Аргументы-указатели обеспечивают доступ из функ- ции к массивам и функциям, которые запращено передавать как аргументы. Пример: main () { void swap (int *, int *); int x, y; swap (&x,&y); } void swap (a,b) int ‘a, *b; { int t ; t=*a; *a=*b; *b=t; } В примере функция swap объявлена в main как функция с двумя ар- гументами типа указателей на int. Формальные параметры а и b объяв- лены так же, как указатели на целые величины. При вызове функции swap (&х,&у) адрес х запоминается в а и адрес у запоминается в Ь. Теперь два имени или "синонима" существует для одной и той же физической памяти. Ссылки *а и *Ь в функции swap действуют точно так же, как х и у в main. Присваивание внутри функции swap изменяет содержимое х и у. Компилятор проведет проверку типов аргументов при вызове swap, по- скольку имеется список типов аргументов в forward-объявлении swap. Типы фактических аргументов соответствуют списку типов аргументов и списку формальных параметров. И 6.8. Ввод и вывод. Доступ к файлам Работа с внешними файлами и устройствами ввода-вывода в Си реализуется с помощью средств «стандартной библиотеки ввода- 383
вывода», то есть набора функций, разработанных для обеспечения стан- дартной системы ввода-вывода для программ на Си. Каждая исходная программа, которая обращается к функции из стандартной библиотеки, должна вначале содержать строку: ^include <stdio.h> Стандартный ввод и вывод — функции getchar и putchar. Самый простой механизм ввода заключается в чтении по одному символу за раз из "стандартного ввода", обычно с терминала пользователя, с помо- щью функции getchar. Функция getchar() при каждом к ней обращении возвращает следующий вводимый символ. Функция getchar возвращает константное значение eof в случае окончания ввода. Вывод можно осуществлять с помощью функции putchar(c), по- мещающей символ 'с' в "стандартный вывод", который по умолчанию является терминалом. Рассмотрим, например, программу lower, которая преобразует про- писные буквы из своего ввода в строчные: ^include <stdio.h> main() /* программа преобразования ввода в строчные буквы */ { int с ; while ((с = getchar()) != eof) putchar(isupper(с) ? tolower(c) : с); } Функции isupper и tolower, использованные в программе, на самом деле являются макросами, определенными в stdio.h. Макрос isupper проверяет, является ли его аргумент буквой из верхнего регистра, и возвращает ненулевое значение, если это так, и нуль — в противном случае. Макрос tolower преобразует букву из верхнего регистра в ту же бу- кву нижнего регистра. Форматный вывод — функция printf. Две функции: printf для вы- вода и scanf для ввода позволяют преобразовывать численные величины в символьное представление и обратно. Функция printf(control, argl, arg2, ...) преобразует, определяет формат и печатает свои аргументы в стандартный вывод под управле- нием строки control. Управляющая строка содержит два типа объектов: обычные симво- лы, которые просто копируются в выходной поток, и спецификации преобразований, каждая из которых вызывает преобразование и печать очередного аргумента printf. 384
Каждая спецификация преобразования начинается с символа % и заканчивается символом преобразования. Между % и символом преоб- разования могут находиться: • знак минус, который указывает на выравнивание аргумента по ле- вому краю его поля; • строка цифр, задающая минимальную ширину поля. Преобразо- ванное число будет напечатано в поле по крайней мере этой шири- ны, а если необходимо, то и в более широком. Если пре- образованный аргумент имеет меньше символов, чем указанная ширина поля, то он будет дополнен слева (или справа, если было указано выравнивание по левому краю) заполняющими символами до этой ширины. Заполняющим символом обычно является пробел, а если ширина поля указывается с лидирующим нулем, то этим символом будет нуль (лидирующий нуль в данном случае не озна- чает восьмеричной ширины поля); • точка, которая отделяет ширину поля от следующей строки цифр; • строка цифр (точность), которая указывает максимальное число символов строки, которые должны быть напечатаны, или число пе- чатаемых справа от десятичной точки цифр для переменных типа float или double; • модификатор длины L, который указывает, что соответствующий элемент данных имеет тип long, а не int. Ниже приводятся символы преобразования и их смысл: d — аргумент преобразуется к десятичному виду; о — аргумент преобразуется в беззнаковую восьмеричную форму (без лидирующего нуля); х — аргумент преобразуется в беззнаковую шестнадцатеричную форму (без лидирующих Ох); и — аргумент преобразуется в беззнаковую десятичную форму; с — аргумент рассматривается как отдельный символ; s — аргумент является строкой: символы строки печатаются до тех пор, пока не будет достигнут нулевой символ или не будет напечатано количество символов, указанное в спецификации точности; е — аргумент, рассматриваемый как переменная типа float или double, преобразуется в десятичную форму в виде [-]m.nnnnnne[+-]xx, где длина строки из п определяется указанной точностью, точность по умолчанию равна 6; f — аргумент, рассматриваемый как переменная типа float или double, преобразуется в десятичную форму в виде [-]mnun.nnnnn, где длина строки из п определяется указанной точностью, точность по умолчанию равна 6. отметим, что эта точность не определяет количест- во печатаемых в формате f значащих цифр; g — используется или формат %е или %f, который короче; незна- чащие нули не печатаются. 385
Если идущий за % символ не является символом преобразования, то печатается сам этот символ; следовательно, символ % можно напеча- тать, указав %%. Следующая таблица демонстрирует влияние задания различных специфи- каций на печать "hello, world" (12 символов). Двоеточия вокруг каждого поля ограничивают поле и указывают его протяженность. :%10s: :hello, world: :%10-s: :hello, world: :%20s: : hello, world: :%-20s: :hello, world : :%20.10s: : hello, world: :%-20.10s: :hello, world : :%.10s: :hello, world: Форматный ввод — функция scanf. Осуществляющая ввод функ- ция scanf является аналогом printf. Функция scanf(control, argl, arg2,...) читает символы из стандартного ввода, интерпретирует их в соответст- вии с форматом, указанном в аргументе control, и помещает результаты в остальные аргументы. Управляющий аргумент описывается ниже; другие аргументы, каждый из которых должен быть указателем, опре- деляют, куда следует поместить соответствующим образом преобразо- ванный ввод. Управляющая строка обычно содержит спецификации преобра- зования, которые используются для непосредственной интерпретации входных последовательностей. Управляющая строка может содержать: • пробелы, табуляции или символы новой строки ("символы пустых промежутков"), которые игнорируются; • обычные символы (не %), которые предполагаются совпадающими со следующими отличными от символов пустых промежутков сим- волами входного потока; • спецификации преобразования, состоящие из символа %, нео- бязательного символа подавления присваивания *, необязательного числа, задающего максимальную ширину поля и символа преобра- зования. Спецификация преобразования управляет преобразованием сле- дующего поля ввода, нормально результат помещается в переменную, которая указывается соответствующим аргументом. Если, однако, с по- мощью символа * указано подавление присваивания, то это поле ввода просто пропускается и никакого присваивания не производится. Поле ввода определяется как строка символов, которые отличны от символов простых промежутков; оно продолжается либо до следующего символа пустого промежутка, либо пока не будет исчерпана ширина поля, если 386
она указана. Отсюда следует, что при поиске нужного ей ввода, функ- ция scanf будет пересекать границы строк, поскольку символ новой строки входит в число пустых промежутков. Символ преобразования определяет интерпретацию поля ввода; со- гласно требованиям основанной на вызове по значению семантики язы- ка Си соответствующий аргумент должен быть указателем. Допускают- ся следующие символы преобразования: d — на вводе ожидается десятичное целое; соответствующий аргу- мент должен быть указателем на целое; о — на вводе ожидается восьмеричное целое (с лидирующим нулем или без него); соответствующий аргумент должен быть указателем на целое; х — на вводе ожидается шестнадцатеричное целое (с лидирующи- ми Ох или без них); соответствующий аргумент должен быть указателем на целое; h — на вводе ожидается целое типа short; соответсвующий ар- гумент должен быть указателем на целое типа short; с — ожидается отдельный символ; соответствующий аргумент должен быть указателем на символы; следующий вводимый символ по- мещается в указанное место. Обычный пропуск символов пустых про- межутков в этом случае подавляется; для чтения следующего символа, который не является символом пустого промежутка, необходимо поль- зоваться спецификацией преобразования %ls; s — ожидается символьная строка; соответствующий аргумент должен быть указателем символов, который указывает на массив сим- волов, который достаточно велик для принятия строки и добавляемого в конце символа \0; f — ожидается число с плавающей точкой; соответствующий ар- гумент должен быть указателем на переменную типа float; е — символ преобразования е является синонимом для £ Формат ввода переменной типа float включает необязательный знак, строку цифр, возможно содержащую десятичную точку и необязательное поле экспоненты, состоящее из буквы е, за которой следует целое, возможно имеющее знак. Перед символами преобразования d, о и х может стоять 1, которая оз- начает , что в списке аргументов должен находиться указатель на перемен- ную типа long, а не типа int. Аналогично, буква 1 может стоять перед сим- волами преобразования е или f, говоря о том, что в списке аргументов дол- жен находиться указатель на переменную типа double, а не типа float. Например, обращение int i ; float х; 387
char name[50]; scanf("&d %f %s", &i, &x, name); co строкой на вводе 25 54.32e-l thompson приводит к присваиванию i значения 25, х — значения 5.432 и name — строки "thompson", надлежащим образом законченной символом \ 0. Эти три поля ввода можно разделить столькими пробелами, табуляция- ми и символами новых строк, сколько вы пожелаете. Обращение int i ; float х; char name[50]; scanf("%2d %f %*d %2s", &i, &x, name); с вводом 56789 0123 45a72 присвоит i значение 56, x — 789.0, пропустит 0123 и поместит в name строку "45". при следующем обращении к любой процедуре ввода рас- смотрение начнется с буквы а. В этих двух примерах name является ука- зателем и, следовательно, перед ним не нужно помещать знак &. В качестве другого примера рассмотрим элементарный калькуля- тор, используя для преобразования ввода функцию scanf: ^include <stdio.h> main()/* rudimentary desk calculator */ { double sum, v; sum =0; while (scanf("%lf”, &v) !=eof) printf("\t%.2f\n", sum += v) ; } Выполнение функции scanf заканчивается либо тогда, когда она исчерпывает свою управляющую строку, либо когда некоторый элемент ввода не совпадает с управляющей спецификацией. В качестве своего значения функция возвращает число правильно совпадающих и запол- ненных элементов ввода. Это число может быть использовано для опре- деления количества найденных элементов ввода. Форматное преобразование в памяти От функции scanf и printf происходят функции sscanf и sprintf, ко- торые осуществляют аналогичные преобразования, но оперируют со строкой, а не с файлом. Обращения к этим функциям имеют вид 388
sprintf(string, control, argl, arg2, ...) sscanf(string, control, argl, arg2, ...) Как и раньше , функция sprintf преобразует свои аргументы argl, arg2 и т.д. в соответствии с форматом, указанным в control, но помещает результаты в string, а не в стандартный вывод. Конечно, строка string должна быть достаточно велика, чтобы принять результат. Например, если name — это символьный массив, ап — целое, то sprintf(name, "temp%d", п); создает в пате строку вида tempnnn, где ппп — значение п. Функция sscanf выполняет обратные преобразования — она просматривает стро- ку string в соответствии с форматом в аргументе control и помещает ре- зультирующие значения в аргументы argl, arg2 и т.д. Эти аргументы должны быть указателями. В результате обращения sscanf(name, "temp%d", &n); переменная n получает значение строки цифр, следующих за temp в name. Доступ к файлам Во всех до сих пор приведенных примерах данные читались из стандартного ввода и результаты отображались в стандартный вывод, относительно которых предполагалось, что они обязательно предостав- лены программе соответствующей операционной системой. Следующий этап — организация чтения из именованных файлов Прежде чем считывать из некоторого файла или записывать в него, этот файл необходимо открыть с помощью функции fopen из стандарт- ной библиотеки. Функция fopen берет внешнее имя, проводит некото- рые обслуживающие действия и обмен с операционной системой (дета- ли которых не должны нас касаться) и возвращает внутреннее имя, ко- торое должно использоваться при последующих чтениях из файла или записях в него. Это внутреннее имя, называемое "указателем файла", фактически является указателем структуры, которая содержит информацию о фай- ле, такую как место размещения буфера, текущая позиция символа в буфере, происходит ли чтение из файла или запись в него и тому подоб- ное. Пользователи не обязаны знать эти детали, потому что среди опре- делений для стандартного ввода-вывода, получаемых из файла stdio.h, содержится определение структуры с именем file. Единственное необходимое для указателя файла описание демон- стрируется примером: file *fopen(), *fp; 389
Здесь говорится, что fp является указателем на file и fopen возвра- щает указатель на file. Фактическое обращение к функции fopen в программе имеет вид: fp=fopen(name,mode); Первым аргументом функции fopen является "имя" файла, которое задается в виде символьной строки. Второй аргумент mode ("режим") также является символьной строкой, которая указывает, как этот файл будет использоваться. Допустимыми режимами являются: чтение ("г"), запись ("w") и добавление ("а"). Если открывается не существующий файл для записи или добавле- ния, то такой файл будет создан (если это возможно). Открытие сущест- вующего файла на запись приводит к удалению его содержимого. По- пытка чтения несуществующего файла является ощибкой. Ошибки мо- гут быть обусловлены и другими причинами (например, попыткой чтения из файла, не имея на то разрешения). При наличии какой-либо ошибки функция возвращает нулевое значение указателя null. Простейшие функции getc и putc поддерживают чтение и запись, если файл уже открыт. Функция getc с параметром указатель файла возвращает следую- щий символ из файла: c=getc(fp) помещает в "с" следующий символ из файла, указанного посредством fp, и eof, если достигнут конец файла. Функция putc(c,fp) помещает символ "с" в файл fp и возвращает "с". При запуске программы автоматически открываются три файла, которые снабжены определенными указателями файлов. Этими файла- ми являются стандартный ввод, стандартный вывод и стандартный вы- вод ошибок; соответствующие указатели файлов называются stdin, stdout и stderr. Обычно все эти указатели связаны с терминалом, но stdin и stdout могут быть перенаправлены на файлы. При работе с файлами для форматного ввода и вывода можно ис- пользовать функции fscanf и fprintf. Они идентичны функциям scanf и printf, за исключением того, что первым аргументом является указатель файла, определяющий тот файл, который будет читаться или куда будет вестись запись; управляющая строка будет вторым аргументом. Рассмотрим программу CAT, выполняющую конкатенацию файлов. Используемая здесь основная схема оказывается удобной во многих программах: если имеются аргументы в командной строке, то они обра- батываются последовательно. Если такие аргументы отсутствуют, то обрабатывается стандартный ввод. Это позволяет использовать про- грамму как самостоятельно, так и как часть большей задачи. 390
//include <stdio.h> main(argc, argv) int argc; char *argv[J; { file *fp, *fopen(); if(argc==l) /* если аргументы отсутствуют, копируется стандартный ввод*/ filecopy(stdin); else while (--argc > 0) if ((fp=fopen(*++argv,"r"))==null) { printf(”cat:can't open %\n”,*argv); break; } else { filecopy(fp); fclose(fp); } } filecopy(fp) /* копирование файла fp в стан- дартный вывод*/ file *fp; { int c; while ((c=getc(fp)) !=eof) putc(c, stdout); } Функция fclose является обратной по отношению к fopen — она разрывает связь между указателем файла и внешним именем и освобо- ждает указатель файла для другого файла и вызывает передачу инфор- мации из системного буфера во внешний файл. Ввод и вывод строк Стандартная библиотека содержит функцию fgets. В результате об- ращения fgets(line, maxline, fp) следующая строка (включая символ новой строки) считывается из фай- ла fp в символьный массив line; самое большое maxline_l символ будет прочитан. Результирующая строка заканчивается символом \0. В конце файла функция возвращает null. Предназначенная для вывода функция fputs записывает строку (ко- торая не обязана содержать символ новой строки) в файл: fputs(line, fp) 391
Приведем описания функций fgets и fputs, взятые непосредственно из стандартной библиотеки ввода-вывода: #include <stdio.h> char *fgets(s,n,iop) /*get at most n chars from iop*/ char *s; int n; register file *iop; { register int c; register char *cs; cs = s; while(--n>0&&(c=getc(iop)) 1=eof) if ((*cs++ = c)=='\n’) break; ‘cs = ' \0 ’ ; return((c==eof && cs==s) 7 null : s) ; } fputs(s,iop) /*put string s on fils iop*/ register char *s; register file *iop; { register int c; while (c = *s++) putc(c,iop); } И 6.9. Некоторые программы на Turbo С Программы обработки массивов 1. Определить наименьшее общее кратное массива N целых чисел ((include <stdio.h> ((include <math.h> ((include <stdlib.h> ((define N 100 main () { int v[N],n,i,y,fl,z=0; wod(v,&n); /* ввод массива */ У=0; do { /* перебираем числа в порядке возрастания */ У++; fl=0; for(i=0;i<n;i++) /* перебираем эл-ты массива */ 392
{ if(prost(v[i])) /* если число простое */ { z=l ; if(y%v[i]) fl=l; /* если число не HOK, ставим флажок */ } } } while(fl); if(z) printf("ХпНОК простых чисел = %d",y); else printf("\n\7HET ПРОСТЫХ ЧИСЕЛ"); } wodfint a[],int *n) /* функция вводит массив */ { int i; printf("ХпВведите число элементов массива ( 0 < N < 100 ): "); scanf("%d",n); if(*n>=N || *n<=0) { printf("\n\n\n\7******* ******* ОШИБКА! *************\ПИ). exit(1); } printf("ХпВведите массив ( 0 < V[I) < 100 ):\n"); for(i=0;i<*n;i++) { printf("a[%d]=",i+1); scanf("%d",&a[i]); if(a[i]>=100 || a[i]<=0) { printf(”\n\n\n\7******** ****** ОШИБКА! *************\n«j. exit(1) ; } } } int prost(int r) /* функция проверяет, является ли число простым */ { int i,fl=0; for(i=2;i<=sqrt(г);i++) { if(r%i==0) fl=l; } return(!f1); 393
2. Определить наибольший общий делитель тех элементов массива, у значений которых совпадают младшая и старшая цифры. #include <stdio.h> #include <stdlib.h> #define N 50 #define MAX 10000 main() { int a[N] , i,y,n,fl,x,xl,x2,m=0; wod(a,&n); /* ввод массива */ for(y=MAX;y>0;y--) /* перебираем числа в порядке убывания */ { fl=0; for(i=0;i<n;i++) /* перебираем элементы массива */ { х=а[i]; xl=x%10; /* берем младшую цифру */ while(х) { х2=х%10; /* ищем старшую цифру */ х/=10; } if(xl!=x2) continue; /* если цифры не равны, то флаг не поднимаем */ ш=1; /* поднимаем флаг */ if(a[i]%y) fl=l; /* если текущий элемент массива не делится на <у>,*/ /* то поднимаем второй флаг */ } if(!fl && m) /* если все эл-ты массива разделились на число,и */ /* младшая и старшая цифры совпали */ { printf(”ХпОтвет: %d”,y); /* то выводим это число */ break; } } if(!m) printf("\n\7HET ЧИСЕЛ С СОВПАДАЮЩИМИ СТАРШ. И МЛАДШ. ЦИФРАМИ"); } wod(int a[],int *n) /* функция вводит массив */ { int i ; printf("ХпВведите число элементов массива 394
( 0 < N < 50 ) : ") ; scanf("%d”,n) ; if(*n>=N || *n<=0) { printf("\n\n\n\7******* ******* ОШИБКА! *************\n")• exi t(1); } printf(“ХпВведите массив ( 0 < A[I] < 10000 ):\n"); for(i=0;i<*n;i++) { printf("a[%d]=",i+1); scanf("%d",&a[i]) ; if(a[i]>=10000 || a[i]<=0) { printf(”\n\n\n\7************** ОШИБКА! *************\n«). exit(1) ; } } 3. Для элементов массива, количество цифр в значениях которых четное, определить наибольший общий делитель. #include <stdio.h> #include <stdlib.h> #define N 100 #define MAX 1000 main () { int c[N],i,n,y,fl,fl=0; wod(c,&n); /* ввод массива */ for(y=MAX;y>0;y--) /* перебираем числа */ { fl=0; for(i=0;i<n;i++) /* перебираем эл-ты массива */ { if(!chet(с[i])) continue; /* если кол-во цифр нечетное, то ничего не делаем */ if(c[i]%y) fl=l; fl=l; } if (!fl) break; } if (fl) printf (11 ХпОтвет : %d",y); else printf("\n\7HET НУЖНЫХ ЧИСЕЛ"); 395
} wod(int a[],int *n) /* функция вводит массив */ { int i ; printf("ХпВведите число элементов массива ( 0 < N < 100 ) : " ) ; scanf(”%d",n); if(*n>=N || *n<=0) { printf("\n\n\n\7******** ****** ОШИБКА! *************\n-j. exit(1); } printf(”ХпВведите массив ( 0 < C[I] < 1000 ):\n"); for(i=0;i<*n;i++) { printf("a[%d]=”,i+1); scanf("%d",&a[i]); if(a[i]>=1000 || a[i]<=0) { printf("\n\n\n\7****** ******** ОШИБКА! *************\q« J . exit(1); } } } int chet(int r) { int i=0,x; do { r/=10; i + + ; } while(r); x=!(i%2); return(x); 4. Напечатать совершенные числа, содержащиеся в массиве К це лых чисел. #include <stdio.h> #include <stdlib.h> #define К 90 main() 396
int x[K],i,n,y,s,fl=O; wod(x,&n); /* ввод массива */ puts (" \n---------------------') ; for(i=0;i<n;i++) /* перебираем числа */ { s=0; for(y=l;y<x[i] ;y+ + ) if(!(x[i]%y)) s+=y; /* запоминаем сумму делителей */ if(s==x[i]) { printf("\n%d",x[i]); fl=l; } } if(!fl) printf("\n\7HET СОВЕРШЕННЫХ ЧИСЕЛ"); } wod(int a[],int *n) /* функция вводит массив */ { int i ; printf(’ХпВведите число элементов массива ( 0 < К < 90 ) : ") ; scanf("%d",п); if(*n>=K || *п<=0) { printf("\n\n\n\7******** ****** ОШИБКА! *************\п.). exit(l); } printf("ХпВведите массив ( 0 < Х[1] < 1000 ):\п"); for(i=0;i<*n;i++) { printf("a[%d]=" , i + 1) ; scanf("%d",&a[i]}; if(a[i]>=1000 || a[i]<=0) { printf("\n\n\n\7************** ОШИБКА! *************\дИ). exit(l); } } 5. Выделить и напечатать в массиве чисел максимально длинные строго возрастающие и строго убывающие последовательности чисел. 397
#include <stdio.h> #include <stdlib.h> #define M 10000 main() { int y[M] ,n,sl=l,s2 = l,pl = 0,p2 = 0,i,ml = l,m2 = l; wod(y,4n); /* ввод массива */ for(i=l;i<n;i++) /* перебираем числа */ { if (y[i]>y[i-U ) { sl++; /* счетчик возрастающей послед. */ } else sl=l; if(sl>ml) {pl=i-sl+l;ml=sl;} /* ml — длина макс, возр. послед. */ if(у[i]<у[i-1]) { s2++; /* счетчик убыв, послед. */ } else s2=l; if(s2>m2) {p2=i-s2+l;m2=s2;} /* m2 — длина макс, убыв, послед. */ } puts("Строго возраст.:"); if(ml==l) puts("НЕТ"); else { for(i=pl;i<pl+ml;i++) printf("%d ",y[i]>; } puts("ХпСтрого убыв. : ") ; if(m2==l) puts("HET"); else { for(i=p2;i<p2+m2;i++) printf("%d ",y[i]>; } } wod(int a[],int *n) /* функция вводит массив */ { int i ; printf("ХпВведите число элементов массива ( 0 < М < 10000 ): "); scanf("%d°,n); if(*n>=M || *n<=0) { printf("\n\n\n\7******* ******* ОШИБКА! *************^n"). exit(1) ; } printf("ХпВведите массив ( 0 < Y[I] < 100 ):\n"); for(i=0;i<*n;i++) 398
{ printf(“a[%d]=",i+1); scanf("%d“,&a[i]); if(a[i]>=100 || a[i]<=0) { printf("\n\n\n\7******** ****** ОШИБКА! *************\n«j. exit(1); } } Программы обработки строк 1. Удалить из строки, содержащей некоторую фразу (слова, разде- ленные пробелами, а в конце — точка) все слова с нечетными номерами и обратить порядок букв в остальных словах. #include <stdio.h> #include <string.h> #define R !?" /* символы — разделители */ main() { char s[100],c[100],*ql,*q2; int i=0,t,k; puts("ХпВведите предложение"); gets(s); /* ввод предложения */ ql=q2=s; while(*ql) /* пока не конец предложения */ { ql=q2; t=strspn(ql,R); ql+=t; /* ставим ql на начало следующего слова */ t=strcspn(ql,R); q2=ql+t; /* q2 на конец след, слова */ i++; /* i — счетчик слов */ if(i%2) /* если слово нечетное */ { for(k=0;k<q2-ql;k++) { ql [k] = '\007 ' ; /* забиваем слово символами '\007' */ } } else { for(k=0;k<q2-ql;k++) { c[k]=ql[k]; /* копируем слово */ 399
} for(k=0;k<q2-ql;k++) { ql[к]=c[q2-ql-k-l]; /* переворачиваем слово */ } } } t=0; for(i=0;i<strlen(s);i++) { if(s[i]!='\007') s[t++]=s[i]; /* сжимаем предложение */ } s[t]='\0'; puts("\пОтвет:"); puts(s); /* вывод предложения */ } 2. Напечатать все буквы, которые входят в слово с наибольшим ко- личеством букв заданного предложения (имеется в виду слово с наи- большим количеством разных букв, т.е. в слове "QWER" букв больше, чем в слове "QQQQQWWWWWWEEEEE” ) #include <stdio.h> #include <string.h> #define R"., ; : ' \ " () ! ?" /* символы — разделители */ main() { char s[100],c[100],*p,*q,*ql; int i,j,m; puts("\пВведите предложение"); gets(s); /* ввод предложения */ P=s; m=0; for(q=s;*q;) /* перебираем слова */ { ql=q; hwost(&q,c); /* берем очередное слово */ if(mcblen(c)) { m=blen(c); p=ql; /* запоминаем слово с наибольшим кол-вом букв */ } } hwost(&p,c); /* берем нужное слово */ puts("\пОтвет:"); 400
for(i=0;i<strlen(c);i++) /* перебираем буквы */ { if(c[i]!=’\007') /* если буква еще не встречалась, */ { printf(”%с”,с[i] ) ; /* печатаем ее */ for(j=i+l;j<strlen(c);j++) { if(c[j]==c[i]) c[j]='\007'; /* выбрасываем такие же буквы */ /* в остальной части слова */ } } } } int blen(char s[] ) /* ф-ция вычисляет кол-во разных букв в слове */ { char с [100]; int i,j,b=O; strcpy(c,s); /* копируем слово */ for(i=0;i<strlen(c);i++) /* перебираем буквы */ { if(с[i]!='\007|) { b++; /* счетчик букв */ for(j=i+l;j<strlen(c);j++) { if(c[j]==c[i]> c[j]='\007'; } } } return(b); } hwost(char **a,char *d) /* ф-ция выделяет слово */ { char *p=*a; int i=strspn(p,R); p+=i; /* ставим <p> на начало слова */ i=strcspn(p,R); strncpy(d,p,i); /* копируем слово */ d[i]='\0'; /* символ конца строки */ P+=i; *а=р; 401
3. В предложении найти пару слов, из которых одно является об- ращением другого. ♦include <stdio.h> ♦include <string.h> ♦define R /* символы — разделители */ main () { char s[100],c[100],*p[50],*q; int i=0,j,k,n,fl = 0; puts("ХпВведите предложение"); gets(s); /* ввод предложения */ q=strtok(s,R); while(q) /* разбиваем предложение на слова */ { p[i++]=q; q=strtok(NULL,R); } for(j=0;j<i;j++) /* перебираем слова */ for(k=j+l;k<i;k++) { if (!fl) /* проверка флага */ { for(n=0;n<strlen(p[k]);n++) /* переворачиваем слово */ { с[n]=*(p[k]+(strlen(p[k])-n-1)); } c[n] = '\0 ' ; if(!strcmp(c,p[j])) /* если слова одинаковы, то выводим */ { puts("Ответ:"); puts(p[j]); puts(p[k]); fl=l; /* устанавливаем флаг */ } } } if(!fl) puts("\nHeT таких слов"); 4. Напечатать все слова заданного предложения, которые отличны от последнего слова и не содержат повторяющихся букв. ♦ include <stclio.h> ♦include <string.h> 402
#define R /* символы — разделители */ main() { char s[100] , *p[50],*q; int i=0,j,k,n,fl,mh=0; puts("ХпВведите предложение"); gets(s); /* ввод предложения */ puts ( "-----------------------") ; q=strtok(s,R); while(q) /* разбиваем предложение на слова */ { p[i++]=q; q=strtok(NULL,R); } i~~; /* <i> — номер последнего слова */ for(j=0;j<i;j++) /* перебираем слова */ { if(strcmp(p[j],p[i])) /* если слово отлично от последнего,то */ { fl=0; for(k=O;k<strlen(p[j]);k++) /* перебираем буквы */ for(n=k+l;n<strlen(p[j]);n++) { if(p[j][k]==p[j][n]) fl=l; /* если есть одинаковые буквы */ /* устанавливаем флаг */ } if(!fl) { puts(p[j]); /* печатаем нужные слова */ mh=l; } } } if(!mh) puts("\7HET НУЖНЫХ СЛОВ");
Литература Айден К., Колесниченко О. и др. «Аппаратные средства РС» 2-е изд., перераб. и доп. —СПб.: BHV — Санкт-Петербург, 1998. — 608 с., ил. Аладьев В.З., Хунт Ю.А., Шишаков МЛ. Основы информатики. Учеб, посо- бие. — М.: Информационно-издательский дом «Филинъ», 1998. —496 с. Алферова З.В. Теория алгоритмов. —М.: Статистика, 1973. — 162 с. Березин Б. И., Березин С. Б. Начальный курс С и C++. — М: ДИАЛОГ-МИФИ, 1996, —288 с. Болски М.И. Язык программирования Си. Справочник: Пер. с англ. -—М.:Радио и связь, 1988. — 96 с. Ван Тассел Д. Стиль, разработка, эффективность, отладка и испытание про- грамм. —М.: Мир, 1981. — 89 с. Вирт Н. Алгоритмы и структуры данных. — М.: Финансы и статистика, 1988. Грнгас. Г. Начала программирования. —М.: Просвещение, 1987. — 112 с. Дантеманн Джефф. Программирование в среде Delphi. Киев, 1995. Информатика: учеб, пособие для пед. спец, высших учеб, завед. / А.Р. Есаян, В.И.Ефимов, Л.П. Лапицкая и др. — М.: Просвещение, 1991. — 288 с. Канмнн В.А. Информатика: Учебник. — М.: ИНФРА-М, 2000. — 232 с. Кауфман В.Ш. Языки программирования. Концепции и принципы. —М.: Радио и связь. 1993. Кирюхин В.М., Ляпунов А.В., Окулов С.М. Задачи по информатике. Между- народные олимпиады 1989 — 1996 гг. —М.: ABF, 1996. Матчо Джон. Delphi. —М.: Бином, 1995. Михайлов В.Ю., Степанников В.М. Современный Бейсик для IBM PC. Среда, язык, программирование. —М.: Изд-во МАИ, 1993. Морозов В.П., Шураков В.В. Основы алгоритмизации, алгоритмические языки и системы программирования: Задачник. —М.: Финансы и статистика, 1994. Нортон П. Программно-аппаратная организация IBM PC. —М., 1991. Нортон П., Сандлер К., Батпей Т. Персональный компьютер изнутри. —М: Бином, 1995. —443с., ил. Поляков Д.В., Круглов И.Ю. Программирование в среде Турбо Паскаль (вер- сия 5.5). —М.: МАИ, 1992. — 105 с. Попов В.Б. TURBO PASCAL для школьников: Версия 7.0. —М.: Финансы и статистика, 1998. С.113—124, 181—195. Турбо Паскаль 7.0. —К.: Торгово-издательское бюро BHV, 1996. — 448 с., ил. Фаронов В.В. Основы Турбо Паскаля. Т. 1—3. —М.: МВТУ-ФЕСТО- ДИДАКТИК, 1993. Фаронов В.В. Турбо Паскаль (в 3-х книгах). Книга 1. Основы Турбо-Паскаля. —М.: МВТУ-ФЕСТО ДИДАКТИК, 1992. Федоров А. Создание Windows-приложений в среде Delphi. —М.: Компьютер- Пресс, 1995. 404
Фигурнов В.Э. IBM PC для пользователя. —М.: ИНФРА-М, 1995. —427 с. Dictionary of Computing (Data Communication, Hardware and Software Basics, Digital Electronic) Ed. by Frank J. Galland, John Wiley & Sons, Datology Press Ltd, Windsor, England, 1983.
Приложение 1 И Глоссарий терминов ALGOL — алгоритмический язык, разработанный для решения на- учных и инженерных вычислительных задач, в котором впервые (1960) были определены и стандартизованы основные понятия и объекты язы- ков программирования (ALGOrthmic Language). COBOL — язык, ориентированный на обработку данных при реше- нии экономических и управленческих задач (COmmon Buisiness Oriened Language). CP-437 — стандарт IBM для интерпретации 2-й половины (128- 256) кода ASCII, таблица предназначена для греческого алфавита. СР-850 — стандарт IBM для интерпретации 2-й половины (128- 256) кода ASCII, таблица предназначена для восточно-европейских ал- фавитов. СР-852 — стандарт IBM для интерпретации 2-й половины (128- 256) кода ASCII, таблица предназначена для греческого алфавита. СР-862 — стандарт IBM для интерпретации 2-й половины (128- 256) кода ASCII, таблица предназначена для иврита. СР-866 — стандарт IBM для интерпретации 2-й половины (128- 256) кода ASCII, таблица предназначена для русской кириллицы. ESC-последовательность, (Escape code) — коды разметки доку- ментов, аппаратурно интерпретируемые принтерами Epson (и др.), по- зволяющие выделять шрифты, размеры и стили печатаемых символов. FORTRAN — первый из известных машинонезависимых языков программирования, ориентрованный на научные и инженерные расчеты. JavaScript — объектно-ориентированный язык программирования сценариев просмотра ресурсов WWW, является развитием HTML. Latin-1 — Международный стандарт (ISO-8859-1) для интерпретации 2-й половины (128-256) кода ASCII, таблица предназначена для латиницы. Latin-8 — международный стандарт (ISO-8859-8) для интерпретации 2-й половины (128-256) кода ASCII, таблица предназначена для иврита. Latin-C— международный стандарт (ISO-8859) для интерпретации 2-й половины (128-256) кода ASCII, таблица предназначена для кирил- лицы. 406
PASCAL — универсальный язык, являющийся развитием системы ALGOL, ориентированный на широкий класс задач обработки данных и организации вычислений (научные, инженерные, экономические, управ- ленческие и пр.). SQL — стандартизованный язык запросов к реляционной (таблич- ной) базе данных, обеспечивающий реляционно полный набор операций на данными. Агрегат данных, (Complex Data) — совокупность разнотипных данных (иногда повторяющаяся), входящая в состав документа или за- писи базы данных Алгоритм — понятное и точное предписание (указание) исполни- телю совершить определенную последовательность действий, направ- ленных на достижение указанной цели или решение поставленной зада- чи (приводящую от исходных данных к искомому результату). Арифметические операции — четыре действия арифметики (+, -, *, /) и операция получения остатка от деления (%) образуют группу арифметических операций. Их выполнение не имеет каких-либо осо- бенностей, кроме как преобразование типов переменных при их несов- падении. Если в одном выражении встречаются переменные разных типов, как правило, осуществляется преобразование (приведение) типов. Архитектура ЭВМ— общее описание структуры и функции ЭВМ на уровне, достаточном для понимания принципов работы и системы команд ЭВМ. Архитектура не включает в себя описание деталей техни- ческого и физического устройства компьютера. Основные компоненты архитектуры ЭВМ: процессор, внутренняя (основная) память, внешняя память, устройства ввода, устройства вывода. Байт — машинное слово минимальной размерности, адресуемое в процессе обработки данных. Размерность байта — 8 бит — принята не только для представления данных в большинстве компьютеров, но и в качестве стандарта для хранения данных на внешних носителях, для передачи данных по каналам связи, для представления текстовой ин- формации. Кроме того, размерность всех форм представления данных устанавливается кратной байту. При этом машинное слово считается разбитым на байты, которые нумеруются, начиная с младших разрядов. Самая распространенная форма представления данных, использующая одно машинное слово — целое число со знаком и без. Наиболее простая — целое положительное число без знака. В нем каждый разряд машинного слова имеет вес, в два раза больший, чем вес соседнего правого, то есть 1,2,4,8,16 и т.д. или последовательные степени 2. Тогда значение числа в машинном слове равно сумме произведений значений разрядов на их веса. Библиотека (library) — набор функций, в том числе из стандартных библиотек, предопределенных переменных и констант, которые могут быть 407
использованы в программе и хранятся в откомпилированном виде. Блок — составной оператор, включающий описания переменных в начале. Это собственные переменные блока, действие которых не рас- пространяется за его пределы, а время существования совпадает с вре- менем его выполнения. Внешняя Ссылка — обращение к переменной или вызов функции во внутреннем представлении модуля, которые определены в другом модуле и отсутствуют в текущем Время (Time) — тип данных, предназначенный для отображения моментов событий, предполагает наличие встроенных или эмулируемых команд специальной арифметики. Время выполнения (run time) -— период, во время которого проис- ходит выполнение программы. Время компиляции (compiler time) — период, во время которого происходит компиляция программы. Во время компиляции обнаружи- ваются синтаксические ошибки. Вызов функции (call interface) — выполнение ее тела с заданными значениями формальных параметров. Выражение — множество взаимосвязанных операций над пере- менными и константами и скобок в котором результат одной опе- рации является операндом другой. Генерация кода — преобразование элементарных действий, полу- ченных в результате лексического, синтаксического и семантического анализа программы, в некоторое внутреннее представление. Это могут быть коды команд, адреса и содержимое памяти данных, либо текст программы на языке Ассемблера, либо стандартизованный промежу- точный код (например, P-код). В процессе генерации кода производится и его оптимизация. Главная (внутренняя, оперативная) память — представляет со- бой упорядоченную последовательность байтов или машинных слов (ячеек памяти), проще говоря — массив. Номер байта или слова памяти, через который оно доступно как из команд компьютера, так и во всех других случаях, называется адресом. Если в команде непосредственно содержится адрес памяти, то такой доступ этому слову памяти называ- ется прямой адресацией. Глобальные переменные — вне тела функции можно определить глобальные переменные. Этими переменными могут пользоваться все функции, следующие по тексту. Глобальные переменные представляют собой общие данные программы. Дата (Date) — тип данных, предназначенный для обработки и отображения дат событий и другой аналогичной информации, предпо- лагает наличие встроенных или эмулируемых команд специальной арифметики. 408
Двойная точность (Double) — числовое данное (с фиксированной или плавающей точкой), размещенное в двух машинных словах, требует наличия операций специальной арифметики. Двойное слово — Машинное слово двойной длины (двойное слово) — используется для увеличения диапазона представления целых чисел. Двойные слова обрабатываются либо отдельными командами процессо- ра, либо программно (эмуляция). Длинное поле (Long) — данное, занимающее неопределенное или переменное количества машинных слов, обычно предназначенное для размещения текстовой или графической информации. Дополнительный код — беззнаковая форма представления чисел со знаком. В двоичной системе счисления дополнение каждой цифры выглядит как инвертирование двоичного разряда, то есть замена 0 на 1 и наоборот. Если же знак числа представляется старшим разрядом ма- шинного слова, то получается простой способ представления отрица- тельного числа: взять абсолютное значение числа в двоичной системе; инвертировать все разряды, включая знаковый; добавить 1. Заголовок функции — "Интерфейс" функции описан в ее заголов- ке. В нем имеется имя функции, по которому она известна далее в про- грамме. Запись (Record) — агрегат данных, составляющий элемент базы данных (файла), содержащий разнотипную информацию, описывающие некоторый объект (сущность, экземпляр, аспект). Идентификатор — имя переменной (идентификатор) состоит из больших и маленьких латинских букв, цифр и знака (подчеркива- ние) и начинается с буквы. Инициализация — установка начальных значений во время транс- ляции. Интерфейс (Interface ) — совокупность технических и программ- ных средств визуализации информации и ввода данных, обеспечиваю- щая интерактивное взаимодействия пользователя с системой. Код ASCII (ASCII code) — American Standart Code for Information Interchange — 7- или 8-битовый код обмена данными; другие обозначения — IA-5, ANSI Х.34, ISO-7 (код ISO-7 отличается 10-ю кодовыми комбинациями, зарезервированными для националь- ных применений). Кодовая таблица (Code Page) — таблица, устанавливающая стан- дартизованное соответствие графических символов и бинарных кодов, определяемое применением (алфавит, программы, устройства ЭВМ). Компоновщик, редактор связей, линкер (link, Linkage editor) — программа, осуществляющая процесс сборки программы из объектных модулей, в котором производится их объединение в исполняемую про- грамму и связывание вызовов внешних функций и их внутреннего пред- 409
ставления (кодов), расположенных в различных объектных модулях. Эта программа собирает откомпилированный текст программы и функ- ции из стандартных библиотек языка в одну выполняемую программу. Косвенная адресация — случай, когда машинное слово содержит адрес другого машинного слова. Тогда доступ к данным во втором ма- шинном слове через первое называется косвенной адресацией. Команды косвенной адресации имеются в любом компьютере и являются основой любого регулярного процесса обработки данных. Действительно, со- держимое первого машинного слова можно формировать программно, работая с различными (например, последовательными расположенны- ми) словами памяти. Лексика языка программирования — правила "правописания слов" программы, таких как идентификаторы, константы, служебные слова, комментарии. Лексический анализ разбивает текст программы на указанные элементы. Особенность любой лексики — ее элементы пред- ставляют собой регулярные линейные последовательности символов. Например, идентификатор — это произвольная последовательность букв, цифр и символа начинающаяся с буквы или Логические операции — над значениями условных выражений можно выполнить логические операции И (&, AND), ИЛИ (|, OR) и НЕ (!, NOT), которые объединяют по правилам логики несколько условий в одно. Благодаря тому, что любая логическая операция может быть представлена с помощью трех основных логических операций, набора элементов «И», «ИЛИ» и «НЕ» в принципе достаточно для построения любого устройства процессора компьютера, а также для описания лю- бых алгоритмов. Логическое данное (Logical) — тип данных, предназначенный для составления логических выражений и управления вычислительным процессом, типу соответствуют определенные операции и функции (ло- гические). Локальные переменные — переменные, которые "известны" толь- ко данной функции (действительны в данном блоке) и являются ее соб- ственностью. Более того, они создаются в памяти при входе в тело функции и исчезают при выходе. Локальными переменными пользуется функция при необходимости иметь собственные данные. Массив — упорядоченная последовательность переменных одного и того же типа, имеющая общее имя. Номер элемента в последователь- ности называется индексом. Количество элементов в массиве не может быть изменено в процессе выполнения программы. Элементы массива размещаются в памяти последовательно и нумеруются от 1 до п, где п — их количество в массиве. Машинное слово — упорядоченное множество двоичных разрядов, используемое для хранения команд программы и обрабатываемых дан- 410
ных. Каждый разряд, называемый битом — это двоичное число, прини- мающее значения только 0 или 1. Разряды в слове обычно нумеруются, справа налево, начиная с 0. Количество разрядов в слове называется размерностью машинного слова или его разрядностью. Наследование — новый, или производный класс может быть опре- делен на основе уже имеющегося, или базового. При этом новый класс сохраняет все свойства старого: данные объекта базового класса вклю- чаются в данные объекта производного, а методы базового класса могут быть вызваны для объекта производного класса, причем они будут вы- полняться над данными включенного в него объекта базового класса. Иначе говоря, новый класс наследует как данные старого класса, так и методы их обработки. Объектно-ориентированное программирование — технология ООП прежде всего накладывает ограничения на способы представления данных в программе. Все данные об объекте программирования и его связях с другими объектами можно объединить в одну структурирован- ную переменную. В первом приближении ее можно назвать объектом. Кроме того, с объектом связывается набор действий, иначе называемых методами. С точки зрения языка программирования это функции, полу- чающие в качестве обязательного параметра указатель на объект. Тех- нология ООП запрещает работать с объектом иначе, чем через методы, то есть внутренняя структура объекта скрыта от внешнего пользователя. Описание множества однотипных объектов называется классом. Тради- ционная технология программирования "от функции к функции" опре- деляет первичность алгоритма (процедур, функций) по отношению к структурам данных. Технология ООП определяет первичность данных (объектов) по отношению к алгоритмам их обработки (методам). Объектный Модуль — файл данных, содержащий оттранслиро- ванные во внутреннее представление собственные функции и перемен- ные, а также информацию о внешних ссылках и точках входа модуля в символьном виде. Операнд — переменная, константа, выражение, участвующие в операции. Унарная операция — операция с одним операндом. Бинарная — операция с двумя операндами. Оператор — синтаксическая единица программы, которая отража- ет логику ее работы (последовательная, ветвящаяся, повторяющаяся). Для операторов характерен принцип вложенности: составными частями оператора могут быть любые другие операторы, и сам он, в свою оче- редь, может входить составной частью в оператор более высокого уровня. Оператор break (ЯП Си, exit, FoxPro и пр.) — производит выход из са- мого внутреннего цикла, то есть переходит к первому оператору, следую- щему за текущим оператором цикла. Заметим, что "покинуть" одновремен- но несколько вложенных друг в друга циклов при помощи break не удается. 411
Оператор continue (ЯП Си, loop — ЯП FoxPro) — выполняет пе- реход из тела цикла к его повторяющейся части, то есть досрочно за- вершает текущий шаг и переходит к следующему. Оператор return — формирует значение переменной-результата как значение выражения, стоящего за этим ключевым словом. Кроме того, он досрочно прекращает выполнение тела функции и возвращает программу в ту точку, где произошел вызов функции. Оператор цикла — обеспечивает повторяющееся выполнение сле- дующего за ним оператора (или блока) — тела цикла. Шаг цикла (ите- рация цикла) — конкретный факт выполнения тела цикла. Операции инкремента ++ и декремента — (ЯП Си) — увеличи- вают или уменьшают значение единственного операнда до или после использования его значения в выражении. Операции сравнения ("=" — равно, "!=" — не равно, а также „>|. „<»4>=«4<_ч) — дают в качестве результата значения "истина" или "ложь". Выражения с такими значениями называются условными, по- скольку обозначают выполнение или, наоборот, невыполнение некото- рого условия в программе. Они используются в условном операторе (if) и в операторах цикла (do — while, for). Определение переменной — "создает" переменную, выделяя под нее память, задавая имя, тип и, возможно, начальное значение. Переменная — именованная область памяти программы, в которой размещены данные с определенной формой представления (типом). Для того, чтобы воспользоваться переменной, необходимо произвести неко- торые предварительные действия — выполнить определение перемен- ной. Только при наличии такого определения транслятор будет знать ее имя, тип данных и, возможно, начальные значения. Персональный компьютер (ПК) (Personal Computer) — малогаба- ритная ЭВМ, обычно реализованная на микропроцессорах, использую- щая однопользовательскую операционную систему. Поле (Field) — однородный элемент данных определенного типа, описывающий аспект объекта или соответствующий фрагменту доку- мента. Полиморфизм (ООП) — основывается на возможности включе- ния в данные объекта также и информации о методах их обработки (в виде указателей на функции). Принципиально важно, что такой объект становится "самодостаточным". Будучи доступным в некото- рой точке программы, даже при отсутствии полной информации о его типе, он всегда может корректно вызвать свойственные ему ме- тоды. Практический смысл полиморфизма заключается в том, что программист может сделать регулярным процесс обработки несо- вместимых объектов различных типов при наличии у них такого по- лиморфного метода. 412
Препроцессор — предварительная фаза трансляции на уровне пре- образования исходного текста программы с использованием директив препроцессора Приведение типов — в выражениях в качестве операндов могут присутствовать переменные и константы разных типов. Результат каж- дой операции также имеет свой определенный тип, который зависит от типов операндов. Если в бинарных операциях типы данных обоих опе- рандов совпадают, то результат будет иметь тот же самый тип. Если нет, то транслятор должен включить в код программы неявные опера- ции, которые преобразуют тип операндов, то есть выполнить приведе- ние типов. Преобразование типов может включать в себя следующие действия: увеличение или уменьшение разрядности машинного слова, то есть "усечение" или "растягивание" целой переменной; преобразова- ние целой переменной в переменную с плавающей точкой и наоборот; преобразование знаковой формы представления целого в беззнаковую и наоборот. Прикладная программа (Application) — программное средство, предназначенное для решения определенного класса задач и поддержи- вающее некоторый класс технологий. Прикладная программа, подго- товленная на языке программирования (Pascal, Cobol, Basic), обычно называется исходной программой (source program). Двоичная версия программы, выходящая из компилятора и размещаемая в оперативной памяти для последующего выполнения, называется исполнительным модулем (object module) в машинном представлении. Она представляет собой последовательность машинных инструкций (команд), которые конкретно указывают компьютеру, что надо делать. Присваивание — операция, которая значение выражения, стоящее справа от символа "=" запоминает в переменной или элементе массива, стоящем слева. При присваивании происходит преобразование форм представления (типов), если они не совпадают. Программа — одна или несколько последовательностей связанных команд (инструкций), которые, будучи выполнены компьютером, реа- лизуют определенную функцию или операцию. Примерами таких функ- ций или операций могут быть: математические вычисления, поиск или сортировка списка объектов, сравнение и отбор объектов по какому- либо критерию, кодирование или декодирование данных, перемещение слов или чисел в закодированной форме в память компьютера, вывод результатов для печати или отображения. Программист (Programmer) — разработчик прикладных или сис- темных программ, использующий системы программирования. Процессор — центральное устройство компьютера. Назначение процессора: управлять работой ЭВМ по заданной программе; выпол- нять операции обработки информации. Возможности компьютера как 413
универсального исполнителя по работе с информацией определяются системой команд процессора. Эта система команд представляет собой язык машинных команд (ЯМК). Отдельная команда определяет отдель- ную операцию (действие) компьютера. В ЯМК существуют команды, по которым выполняются арифметические и логические операции, опера- ции управления последовательностью выполнения команд, операции передачи данных из одних устройств памяти в другие и пр. В состав процессора входят: устройство управления (УУ), арифметико- логические устройство (АЛУ), регистры процессорной памяти. Пустой оператор — не производящий никаких действий, обозна- чается символом который встречается в программе "сам по себе". Пустой оператор используется там, где по синтаксису требуется нали- чие оператора, но никаких действий производить не нужно. Результат Функции — выходная переменная, которую формирует функция и значение которой используется затем в том месте програм- мы, где она вызывается. Результат, как любая другая переменная, дол- жен быть определенного типа. Семантика языка программирования — это смысл, который за- кладывается в каждую конструкцию языка. Семантический анализ — это проверка смысловой правильности конструкции. Например, если в выражении используется переменная, то она должна быть определена ранее по тексту программы, а из этого определения может быть получен ее тип. Исходя из типа переменной, можно говорит о допустимости операции с данной переменной. Символьное данное (Character) — тип данных, предназначенный для ввода и отображения алфавитной, цифровой и спецсимвольной ин- формации; типу соответствуют определенные операции над перемен- ными и функции (строчные). Синтаксис языка программирования — правила составления предложений языка из отдельных слов. Такими предложениями являют- ся операции, операторы, определения функций и переменных. Особен- ностью синтаксиса является принцип вложенности (рекурсивность) правил построения предложений. Это значит, что элемент синтаксиса языка в своем определении прямо или косвенно в одной из его частей содержит сам себя. Например, в определении оператора цикла телом цикла является оператор, частным случаем которого является все тот же оператор цикла. Система программирования (Programming System) — программ- ная среда разработки приложений с использованием языка программи- рования, библиотеки функций, компилятора или интерпретатора ЯП. Системная программа — программа в машинных кодах, которая поставляется с компьютером (или приобретается отдельно) и расчитана на выполнение опеределенных узко специализированных операций. 414
Совокупность системных программ обычно называется системным про- граммным обеспечением (system software). Составной оператор — последовательность операторов, заклю- ченная в операторные скобки ({}, begin end), образует составной опера- тор (или блок) и входит в охватывающую его конструкцию как одно целое, то есть становится с точки зрения транслятора одним оператором. Стандартное машинное слово — машинное слово, размерность которого совпадает с разрядностью процессора. Большинство команд процессора использует для обработки данных стандартное машинное слово. Тип данных — форма представления данных, которая характеризу- етсяхпособом организации данных в памяти; — множеством допусти- мых значений; — набором операций. Точка входа — адрес переменной или функции во внутреннем представлении модуля, к которым возможно обращение из других мо- дулей. Указатель — переменная, содержимым которой является адрес другой переменной. Соответственно, основная операция для указателя — это косвенное обращение по нему к той переменной, адрес которой он содержит. Указатель, который содержит адрес переменной, ссылает- ся на эту переменную или назначен на нее; наоборот, переменная, адрес которой содержится в указателе, называется указуемой переменной. Управление — целенаправленное воздействие управляющего объ- екта на объект управления, осуществляемое для организации функцио- нирования объекта управления по заданной программе. Управляющие структуры (структурные операторы) — устанавли- вают порядок выполнения отдельных операторов в программе. К струк- турным операторам относятся составной оператор, условный оператор и цикл. Каждый из структурных операторов в свою очередь состоит из эле- ментарных или других структурных операторов. Управляющие структу- ры — составной оператор, условный оператор и цикл — равноправны в том смысле, что любая из них может входить в состав другой. В про- граммах встречаются группы операторов, объединенных в составной оператор, который является элементом цикла или условного оператора, и, наоборот, циклы и условные операторы могут входить в составные опе- раторы или другие циклы. Использование управляющих структур позво- ляет создавать разнообразные и довольно сложные программы. Условный оператор if — используется в программе, когда нужно выполнить одну или другую последовательность действий в зависимо- сти от выполнения некоторого условия. Файл (File) — именованный организованный набор данных опре- деленного типа и назначения, находящийся под управлением операци- онной системы. 415
Файл ASCII (ASCII-File) — файл, содержащий символьную ин- формацию в коде Latin-1 и символьную разметку. Файл бинарный (Binary File) — файл, содержащий произвольную двоичную информацию (текст с бинарной разметкой, программа, гра- фика, архивный файл). Файловая система (File management system) — динамически под- держиваемая информационная структура на устройствах прямого дос- тупа (дисках), обеспечивающая функцию управления данными ОС пу- тем связи "имя-адрес". Фактические параметры — значения формальных параметров, с которыми будет выполняться тело функции при вызове, определяются списком фактических параметров, которые следуют в вызове функции вслед за ее именем, разделенные запятыми и заключенные в скобки. Перед вызовом функции фактические параметры, согласно списку, ко- пируются в соответствующие формальные параметры. Таким образом, функция получает на вход данные через фактические параметры, кото- рые она "видит" как соответствующие им формальные. Фиксированная точка (Fixed) — простейший тип числовых дан- ных, когда число размещено в машинном слове, и диапазон значений зависит только от разрядности слова Формальные параметры — после имени функции в круглых скобках идет список формальных параметров, разделенных запяты- ми. Формальные параметры — это тоже переменные особого рода, что видно из заголовка — их синтаксис в перечислении напоминает определение обычных переменных. Как и все другие переменные они имеют тип и могут быть обычными переменными и массивами. Фор- мальные параметры содержат входные данные функции, передавае- мые при ее вызове и могут использоваться только самой функцией в процессе ее работы. Формат данных (Data format) — стандартизованное представле- ние порции данных для хранения, передачи, отображения. Функция — функция выполняет некоторое законченное действие и имеет "стандартный интерфейс" с набором параметров, через который она вызывается из других функций. Функция printf (ЯП Си) — обеспечивает вывод произвольного числа переменных различных типов в заданном формате. Соответствен- но, она может иметь переменное число фактических параметров в вызо- ве. Первым параметром функции printf является форматная строка. Все символы строки, кроме последовательностей, начинающихся на "%" и "\", выводятся на экран. Если в форматной строке имеется символ "%", то он определяет вид очередного фактического параметра из списка. Функция scanf (ЯП Си) — используется для ввода значений пере- менных с клавиатуры. Она имеет такую же форматную строку и тот же 416
способ задания последовательности фактических параметров — пере- менных и элементов массивов, значения которых вводятся. Цикл ПОКА — отличается от цикла ДО тем, что проверка условия проводится до выполнения тела цикла, и если при первой проверке условие выхода из цикла выполняется, то тело цикла не выполняется ни разу. Цикл ДО — применяется при необходимости выполнить какие- либо вычисления в несколько раз до выполнения некоторого условия. Особенностью этого цикла является то, что он выполняется хотя бы один раз. Числа с плавающей запятой (Float) — числовое данное, разме- щенное в машинном слове в форме мантиссы и порядка, что позволяет представлять широкий диапазон значений; предполагает наличие встро- енной или эмулируемой арифметики (операции с плавающей запятой). Для использования чисел с дробной частью, а также для расширения диапазона используемых числовых данных вводится форма представ- ления вещественных чисел или чисел с плавающей запятой: X = ш*10р, например 25.4 = 0.254*102, где 0.1 < m < 1 — значащая часть числа, приведенная к интервалу 0.1 ... 1, называемая мантиссой, ар — целое число, называемое порядком. Аналогично, если взять основание степе- ни — 2,то получим:Х = ш*2р, где 0.5 <m< 1 — мантисса, ар — двоич- ный порядок. Числовое данное (Numerical) — тип данных, предназначенный для вычислений и составления арифметических выражений, которому соот- ветствуют определенные операции и функции (арифметические). ЭВМ (Computer) — универсальный комплекс технических средств, предназначенный для программированной обработки информации (Электронная вычислительная машина)ю Язык запросов (Query Language) — языковое средство поиска и обработки информации в базе данных или информационно-поисковой системе (ИПС). Язык программирования (Programming Language) — совокуп- ность средств, предназначенная для описания алгоритмов, реализуемых в программах ЭВМ. 417
Приложение 2. Основная таблица ASII-символов и их кодировки Таблица соответствия значений клавиш терминала в десятичном (основание 10), шестнадцатеричном (основание 16), восьмеричном (ос- нование 8), а также в коде ASCII (Американский стандартный код для обмена информацией). Последовательности клавиш, включающие кла- вишу "Control" (Ctrl) вводятся одновременным нажатием клавиши "Control" и указанной клавиши. Эти последовательности основаны на тех последовательностях, которые описаны для большинства стандарт- ных терминалов, и могут быть описаны иначе на других терминалах. Десятичный код Двоичный код Восьмеричный КОД Шестнадца- теричный код Символы кода ASCII Клавиши терминала IBM 0 0000 0000 00 00 NUL <Ctrl+@> 1 0000 0001 01 01 SOH <Ctrl+A> 2 0000 0010 02 02 STX <Ctrl+-B> 3 0000 0011 03 03 ЕТХ <Ctrl-O 4 0000 0100 04 04 EOT <Ctrl-D> 5 0000 0101 05 05 ENQ <Ctrl-E> 6 0000 0110 06 06 АСК <Ctrl+F> 7 0000 0111 07 07 BEL <Ctrl+G> 8 0000 1000 10 08 BS <Ctrl+H> 9 0000 1001 11 09 НТ <Ctrl+I> 10 0000 1010 12 0А LF <Ctrl+J> 11 0000 1011 13 ОВ VT <Ctrl+K> 12 0000 1100 14 ОС FF <Ctrl+L> 13 00001101 15 0D CR <Ctrl+M> 14 0000 1110 16 0Е SO <Ctrl+N> 15 0000 1111 17 0F SI <Ctrl+O> 16 0001 0000 20 10 DLE <Ctrl+P> 17 0001 0001 21 И DC I <Ctrl+Q> 18 0001 0010 22 12 DC2 <Ctrl+R> 418
Продолжение табл. Десятичный код Двоичный код Восьмеричный код Шестнадца- теричный код Символы кода ASCII Клавиши терминала IBM 19 0001 0011 23 13 DC3 <Ctrl+S> 20 0001 0100 24 14 DC4 <Ctrl+T> 21 0001 0101 25 15 NAK <Ctrl+U> 22 0001 ОНО 26 16 SYN <Ctrl+V> 23 0001 0111 27 17 ЕТВ <Ctrl+W> 24 0001 1000 30 18 CAN <Ctrl+X> 25 0001 1001 31 19 ЕМ <Ctrl+Y> 26 0001 1010 32 1А SUB <Ctrl+Z> 27 0001 1011 33 1В ESC <Esc> 28 0001 1100 34 1С FS <Ctrl+\> 29 0001 1101 35 1D GS <Ctrl+'> 30 0001 1110 36 1Е RS <Ctrl+=> 31 0001 1111 37 1F US <Ctrl+-> 32 0010 0000 40 20 SP <Space> (Пробел) 33 0010 0001 41 21 I 1 (Восклицательный знак) 34 0010 0010 42 22 It " (Кавычки) 35 0010 0011 43 23 # # (Знак числа) 36 0010 0100 44 24 $ $ (Знак "доллар") 37 00100101 45 25 % % (Процент) 38 00100110 46 26 & & (Амперсанд) 39 00100111 47 27 ' (Апостроф или диак- ритический знак) 40 0010 1000 50 28 ( (Открывающие (круглые) скобки 41 0010 1001 51 29 ) ) Закрывающие (круглые) скобки 42 0010 1010 52 2А * * (Звездочка) 43 0010 1011 53 2В + + (Плюс) 44 0010 1100 54 2С J ,(Запятая) 45 0010 1101 55 2D - - (Дефис, тире или минус) 46 0010 1110 56 2Е . (Точка) 47 0010 1111 57 2F / / (Косая черта) 48 ООП 0000 60 30 0 0 (Ноль) 49 ООП 0001 61 31 1 1 50 ООП 0010 62 32 2 2 419
Продолжение табл. Десятичный код Двоичный код Восьмеричный код Шестнадца- теричный код Символы кода ASCII Клавиши терминала IBM 51 ООН ООН 63 33 3 3 52 ООП 0100 64 34 4 4 53 ООН 0101 65 35 5 5 54 ООН ОНО 66 36 6 6 55 ООН 0111 67 37 7 7 56 ООП 1000 70 38 8 8 57 ООП 1001 71 39 9 9 58 ООП 1010 72 ЗА : (Двоеточие) 59 ООН 1011 73 ЗВ i ; (Точка с запятой) 60 ООН 1100 74 зс < < (Меньше, чем) 61 ООП 1101 75 3D = =(Равняется) 62 ООП 1110 76 ЗЕ > > (Больше, чем) 63 ООП 1111 77 3F ? ? (Вопросительный знак) 64 0100 0000 100 40 @ @ (Коммерческий знак at) 65 0100 0001 101 41 А А (Латиница, заглав- ные) 66 0100 0010 102 42 В В 67 0100 ООН 103 43 С С 68 0100 0100 104 44 D D 69 01000101 105 45 Е Е 70 01000110 106 46 F F 71 01000111 107 47 G G 72 0100 1000 110 48 Н Н 73 0100 1001 111 49 I 1 74 0100 1010 112 4А J J 75 0100 1011 113 4В К К 76 0100 1100 114 4С L L 77 0100 1101 115 4D М М 78 0100 1110 116 4Е N N 79 0100 1111 117 4F 0 0 80 0101 0000 120 50 Р Р 81 0101 0001 121 51 Q Q 82 0101 0010 122 52 R R 83 0101 ООН 123 53 S S 420
Продолжение табл. Десятичный код Двоичный код Восьмеричный код Шестнадца- теричный код Символы кода ASCII Клавиши терминала IBM 84 0101 0100 124 54 Т T 85 0101 0101 125 55 и u 86 0101 ОНО 126 56 V V 87 0101 0111 127 57 W w 88 0101 1000 130 58 X X 89 0101 1001 131 59 Y Y 90 0101 1010 132 5А Z Z 91 0101 1011 133 5В [ [ (Открывающая квад- ратная скобка) 92 0101 1100 134 5С \ \ (Обратная косая черта) 93 0101 1101 135 5D ] ] (Закрывающая квад- ратная скобка) 94 0101 1110 136 5Е Л Л (Диакритический знак) 95 0101 1111 137 5F - _ (Символ подчеркивания) 96 0110 0000 140 60 ' (Диакритический знак) 97 0110 0001 141 61 а а (Латиница, строчные) 98 01100010 142 62 ь b 99 01100011 143 63 с С 100 0110 0100 144 64 d d 101 01100101 145 65 е е 102 01100110 146 66 f f 103 01100111 147 67 g g 104 ОНО 1000 150 68 h h 105 ОНО 1001 151 69 i i 106 ОНО 1010 152 6А j j 107 ОНО 1011 153 6В k k 108 ОНО 1100 154 6С 1 1 109 ОНО 1101 155 6D m m 110 ОНО 1110 156 6Е n n 111 ОНО НН 157 6F о о 112 0111 0000 160 70 p p 113 0111 0001 161 71 q q 114 0111 0010 162 72 r Г 115 0111 ООН 163 73 s s 116 0111 0100 164 74 t t 421
Продолжение табл. Десятичный код Двоичный код Восьмеричный код Шестнадца- теричный код Символы кода ASCII Клавиши терминала IBM 117 0111 0101 165 75 U U 118 0111 ОНО 166 76 V V 119 0111 0111 167 77 W W 120 0111 1000 170 78 X X 121 0111 1001 171 79 У У 122 0111 1010 172 7А Z Z 123 0111 1011 173 7В { {(Открывающая фи- гурная скобка) 124 0111 1100 174 7С 1 | (Вертикальный штрих (логическое ИЛИ)) 125 0111 1101 175 7D } } (Закрывающая фигур- ная скобка) 126 0111 1110 176 7Е —• ~ (Знак "тильда") 127 0111 1111 177 7F DEL <Del> (Удаление) Описания непечатаемых (неотображаемых) символов ASCII АСК (подтверждение) — управляющий символ при передаче дан- ных, который служит в качестве обычного ответа "да" на различные вопросы, но также иногда указывает: "Я получил вашу последнюю пе- редачу и готов к вашей следующей передаче" BELL (звонок) — управляющий символ общего назначения активи- зирует звонок, гудок или другой звуковой сигнал тревоги на том уст- ройстве, на которое он был послан. BS (возврат) — управляющий символ спецификации формата пе- ремещает каретку, печатающую головку или курсор назад на один про- бел или позицию. CAN (отмена) — управляющий символ общего назначения, указы- вающий что информация предыдущей передачи не должна учитываться, причем объем этой информации определяется пользователем. CR (возврат каретки или возврат) — управляющий символ специ- фикации формата перемещает каретку, печатающую головку или курсор на экране терминала к началу данной строки. В большинстве термина- 422
лов клавиша Возврата вызывает и возврат каретки (CR), и перевод стро- ки (LF). DC1-DC4 (управление устройством) — управляющий символ об- щего назначения, которые управляют терминалом пользователя или аналогичными устройствами. Стандартные функции не присваиваются, за исключением того, что функция DC4 часто означает "останов". Меж- дународный консультативный комитет по телеграфии и телефонии (CCITT) предлагает ряд возможных назначений. Вообще CCITT пред- почитает использование первых двух функций по управлению устрой- ством для "включения", а двух последних — для "выключения", а функ- ции DC2 и DC4 имеют отношение к более важному устройству. В неко- торых системах эти коды помечены соответственно XON, ТАРЕ, XOFF и NO ТАРЕ. "X" означает "передатчик", а "ТАРЕ" и "NO ТАРЕ" озна- чают "Включить ленту" или "Выключить ленту". Эти метки находятся в верхней части некоторых терминалов. DEL (удаление) — управляющий символ общего назначения удаля- ет символ. Символ DEL, в некоторых терминалах называемый RUBOUT, не является, строго говоря, управляющим символом, по- скольку он не группируется с другими управляющими символами кода ASCII. Функция удаления DEL имеет единицы во всех разрядах при двоичном представлении числа (1111 1111, основание 2). Причина тако- го представления имеет историческую основу: единственным способом удалить двоичный образ, отперфорированный на бумажной ленте, был способ пробивки всех кодовых отверстий так, чтобы результирующий образ был эквивалентен нулю. Код ASCII до сих пор рассматривает функцию удаления DEL эквивалентом нуля, хотя многие операционные системы используют символ DEL для удаления предшествующего сим- вола. DLE ("авторегистр 1") — управляющий символ при передаче дан- ных, который использует специальный тип управляющей последова- тельности специально для управления каналом связи и средствами пе- редачи. ЕМ (конец носителя данных) — управляющий символ общего на- значения указывает на конец (бумажной) перфоленты (или другого но- сителя данных). ENQ (запрос) — управляющий символ при передаче данных обыч- но используется для идентификации запроса или информации о статусе. В некоторых системах этот код выглядит как WRU ("Кто вы?"). EOT (конец передачи) — управляющий символ при передаче дан- ных отмечает конец передачи после одного или более сообщений. ESC ("авторегистр 2") — символ общего назначения отмечает на- чало управляющей последовательности. Управляющая последователь- ность состоит из серии кодов которые в качестве группы имеют особое 423
назначение, обычно функции управления. В некоторых терминалах символ ESC называется ALT MODE. ЕТВ (конец блока передачи) — управляющий символ при передаче данных используется при необходимости разбить длинные сообщения на блоки. Символ ЕТВ отмечает границы блока. Блоки обычно не име- ют ничего общего с форматом сообщения, подлежащего передаче. ЕХТ (конец текста) — управляющий символ при передаче данных отмечает конец текста. См. символ SOH. Этот код первоначально назы- вался ЕОМ (конец сообщения) и может использоваться в качестве от- метки как таковой на некоторых терминалах. FF (перевод страницы) — управляющий символ спецификации формата вызывает перемещение каретки, печатающего колесика или курсора к началу следующей страницы. FS,GS,RG,US (символы- разделители файлов, групп, записей и уст- ройств) — набор управляющих символов — разделителей, которые оп- ределяют границы порций информации. Стандарты на употребление таких символов отсутствуют за исключением того, что символ FS, как предполагается, имеет отношение к наибольшему делению, а символ US — к наименьшему. НТ (горизонтальная табуляция) — управляющий символ специфи- кации формата который устанавливает каретку, печатающее колесико или курсор на следующем заранее определенном месте той же строки. Пользователь обычно решает, как осуществлять разметку горизонталь- ной таблицы. LF (перевод строки) — управляющий символ спецификации фор- мата перемещает каретку, печатающую головку или курсор вниз на од- ну строку. Большинство систем объединяет возврат каретки (CR) с пе- реводом строки (LF). В результате этого объединения получается новая строка, называемая NL. NAK (отрицательное подтверждение) — управляющий символ при передаче данных указывает "нет" в ответе на различные вопросы. Ино- гда он описан как: "Я получил вашу последнюю передачу, но в ней есть ошибки, и я жду повторной передачи". NUL (нуль) — управляющий символ общего назначения, который главным образом используется в качестве заполнителя пространства. См. также символ SYN. SI (переключение на стандартный регистр) — управляющий сим- вол общего назначения, используемый после кода SO для указания того, что коды возвращаются к обычному значению в коде ASCII. SO (переключение на дополнительный регистр) — управляющий символ общего назначения указывает на следующие двоичные образы, значения которых выходят за рамки стандартного набора символов кода ASCII и будет продолжать указывать до тех пор, пока не будет введен 424
символ переключения на стандартный регистр. SOH (начало заголовка) — управляющий символ при передаче данных отмечает начало заголовков, если они используются в сообще- ниях наряду с текстом. В заголовках обычно сообщается имя и ячейка с адресом. Этот код первоначально назывался SOM (начало сообщения). STX (начало текста) — управляющий символ при передаче данных, используемый в качестве отметки начала текста и конца заголовка (если используется). Этот код первоначально назывался ЕОА (конец адреса). SUB (замена) — управляющий символ общего назначения, указы- вающий на символ, который должен занять место неправильного сим- вола. SYN (синхронизация) — управляющий символ при передаче дан- ных, используемый некоторыми быстродействующими системами пе- редачи данных, которые используют синхронизированные часы на кон- цах приемника и передатчика. В периоды простоя, когда отсутствие двоичных образов не позволяет часам на приемном конце отслеживать часы передатчика, приемник может нарушать синхронизацию. Каждая передача, следующая за период простоя, поэтому заменяются тремя или четырьмя символами синхронизации SYN. Код SYN имеет двоичный образ, который позволяет приемнику не только запирать (блокировать) часы передатчика, но также определять точки начала и конца каждого символа. Символы синхронизации SYN могут кроме того, использо- ваться для заполнения коротких периодов простоя с тем чтобы поддер- живать синхронизацию, отсюда и данное название. VT (вертикальная табуляция) — управляющий символ специфика- ции формата, который подводит каретку, печатающую головку или кур- сор к следующему заранее определенному ограничителю (обычно строке).
Приложение 3. Кодовая таблица Microsoft Code Page 866 (CP-866) Сим- вол Десятич- ный вид Строка и столбец Восьме- ричный код Шестна- дцатерич- ный код Описание [A] 128 08/00 200 80 Cyrillic А [Б] 129 08/01 201 81 Cyrillic Be [В] 130 08/02 202 82 Cyrillic Ve [Г] 131 08/03 203 83 Cyrillic Ghe [Д] 132 08/04 204 84 Cyrillic De [Е] 133 08/05 205 85 Cyrillic le [Ж] 134 08/06 206 86 Cyrillic Zhe [3] 135 08/07 207 87 Cyrillic Ze [И] 136 08/08 210 88 Cyrillic I [И] 137 08/09 211 89 Cyrillic short I [К] 138 08/10 212 8А Cyrillic Ka [Л] 139 08/11 213 8В Cyrillic El [М] 140 08/12 214 8С Cyrillic Em [Н] 141 08/13 215 8D Cyrillic En [О] 142 08/14 216 8Е Cyrillic 0 [П] 143 08/15 217 8F Cyrillic Pe [Р]* 144 09/00 220 90 Cyrillic Er [С] 145 09/01 221 91 Cyrillic Es [Т] 146 09/02 222 92 Cyrillic Те [У] 147 09/03 223 93 Cyrillic U [ф] 148 09/04 224 94 Cyrillic Ef [X] 149 09/05 225 95 Cyrillic Ha [Ц] 150 09/06 226 96 Cyrillic Tse [Ч] 151 09/07 227 97 Cyrillic Che [Ш] 152 09/08 230 98 Cyrillic Sha [Щ] 153 09/09 231 99 Cyrillic Shcha [Ъ] 154 09/10 232 9А Cyrillic Hard Sign [Ы] 155 09/11 233 9В Cyrillic Yeri 426
Продолжение табл. Сим- вол Десятич- ный вид Строка и столбец Восьме- ричный код Шестна- дцатерич- ный код Описание [Ь] 156 09/12 234 9С Cyrillic Soft Sign [Э] 157 09/13 235 9D Cyrillic E [Ю] 158 09/14 236 9Е Cyrillic Yu [Я] 159 09/15 237 9F Cyrillic Ya [а] 160 10/00 240 АО Cyrillic a [б] 161 10/01 241 А1 Cyrillic be [в] 162 10/02 242 А2 Cyrillic ve [г] 163 10/03 243 АЗ Cyrillic ghe [д] 164 10/04 244 А4 Cyrillic de [е] 165 10/05 245 А5 Cyrillic ie [ж] 166 10/06 246 А6 Cyrillic zhe [з] 167 10/07 247 А7 Cyrillic ze [И] 168 10/08 250 А8 Cyrillic i [й] 169 10/09 251 А9 Cyrillic short i Гк1 170 10/10 252 АА Cyrillic ka [Л] 171 10/11 253 АВ Cyrillic el [М] 172 10/12 254 АС Cyrillic em [Н] 173 10/13 255 AD Cyrillic en [о] 174 10/14 256 АЕ Cyrillic о [П] 175 10/15 257 AF Cyrillic pe [р] 224 14/00 340 Е0 Cyrillic er [С] 225 14/01 341 Е1 Cyrillic es [т] 226 14/02 342 Е2 Cyrillic te [у] 227 14/03 343 ЕЗ Cyrillic u [ф] 228 14/04 344 Е4 Cyrillic ef [X] 229 14/05 345 Е5 Cyrillic ha [ц] 230 14/06 346 Е6 Cyrillic tse [Ч] 231 14/07 347 Е7 Cyrillic che [ш] 232 14/08 350 Е8 Cyrillic sha [ш] 233 14/09 351 Е9 Cyrillic shcha [ъ] 234 14/10 352 ЕА Cyrillic hard sign [Ы] 235 14/11 353 ЕВ Cyrillic yen [Ь] 236 14/12 354 ЕС Cyrillic soft sign [Э] 237 14/13 355 ED Cyrillic e [ю] 238 14/14 356 ЕЕ Cyrillic yu [л] 239 14/15 357 EF Cyrillic 427
Продолжение табл. Сим- вол Десятич- ный вид Строка и столбец Восьме- ричный код Шестна- дцатерич- ный код Описание [Ё] 240 15/00 360 F0 Cyrillic Io [ё] 241 15/01 361 F1 Cyrillic io [I] 242 15/02 362 F2 Ukranian le [i] 243 15/03 363 F3 Ukranian ie м 244 15/04 364 F4 Cyrillic Je [ц] 245 15/05 365 F5 Cyrillic je [Щ 246 15/06 366 F6 Bielorussian short U [] 247 15/07 367 F7 Bielorussian short u [±] 248 15/08 370 F8 Degree symbol ш 249 15/09 371 F9 Large center dot [е] 250 15/10 372 FA Small center dot [«] 251 15/11 373 FB Radical symbol [№] 252 15/12 374 FC Numero sign [S] 253 15/13 375 FD Currency symbol [s] 254 15/14 376 FE Solid square 1 255 15/15 377 FF Required Space
Содержание Предисловие..........................................................3 Глава 1. Основные принципы алгоритмизации и программирования.........8 1.1. Алгоритмы и программы.........................................8 Примеры и решения...............................................16 Вопросы и задания...............................................19 1.2. Данные. Понятие типа данных..................................20 Примеры и решения...............................................27 Вопросы и задания...............................................30 1.3. Логические основы алгоритмизации.............................32 Примеры и решения...............................................36 Вопросы и задания...............................................37 1.4. Языки программирования: эволюция, классификация............38 Вопросы и задания...............................................50 1.5. Системы программирования.....................................51 Вопросы.........................................................60 1.6. Файлы данных.................................................60 Примеры и решения...............................................63 Вопросы и задания...............................................65 1.7. Объектно-ориентированный подход к программированию...........66 Вопросы и задания...............................................74 1.8. Разработка программного обеспечения (ПО).....................74 Вопросы.........................................................79 Глава 2. Язык программирования PASCAL...............................80 2.1. Примеры программ.............................................80 2.2. Лексика языка................................................83 2.3. Переменные и константы. Типы данных.................'........85 2.4. Выражения и операции.........................................93 2.5. Операторы языка..............................................98 2.6 Структурированные типы данных................................109 2.7. Динамические данные.........................................129 2.8. Процедуры и функции.........................................139 2.9. Структура программы.........................................151 2.10. Модули.....................................................156 2.11. Организация ввода-вывода данных. Работа с файлами..........158 Программная реализация алгоритмов главы 1........................171 Упражнения.....................................................176 Глава 3. Интегрированная среда разработки приложений DELPHI......181 3.1. Интерфейс среды DELPHI......................................181 3.2. Характеристика проекта Delphi...............................186 3.3. Компиляция и выполнение проекта.............................198 3.4. Разработка приложения.......................................200 3.5. Средства управления параметрами проекта и среды разработки.210‘ 429
3.6. Pascal и визуальная среда разработки приложений Delphi.....215 3.7. Разработка приложения «Калькулятор»........................221 3.8. Разработка приложения «Редактор текстов»...................232 Упражнения...................................................249 Глава 4. Язык программирования BASIC..............................250 4.1. Примеры программ...........................................251 4.2. Лексика языка программирования Basic.......................253 4.3. Переменные и типы данных...................................255 4.4. Операции...................................................264 4.5. Операторы языка............................................271 4.6. Процедуры и функции........................................279 4.7. Организация ввода-вывода. Работа с файлами.................283 Программная реализация алгоритмов главы 1.......................290 Упражнения...................................................298 Глава 5. Интегрированная среда разработки приложений VISUAL BASIC.299 5.1. Интерфейс среды............................................299 5.2. Характеристика проекта.....................................304 5.3. Компиляция и выполнение проекта............................306 5.4. Разработка приложения......................................311 5.5. Средства управления параметрами проекта и среды разработки.314 5.6. Программа «Калькулятор» в среде Visual Basic...............318 Упражнения...................................................325 Глава 6. Язык программирования С (СИ).............................326 6.1. Примеры программ...........................................327 6.2. Лексика языка программирования Си..........................331 6.3. Структура программы........................................334 6.4. Типы данных. Переменные и константы........................336 6.5. Выражения и операции.......................................353 6.6. Операторы языка............................................369 6.7. Функции....................................................379 6.8. Ввод и вывод. Доступ к файлам..............................383 6.9. Некоторые программы на Turbo С.............................392 Литература........................................................404 Приложение 1. Глоссарий терминов..................................406 Приложение 2. Основная таблица ASII-символов и их кодировки.......418 Описания непечатаемых (неотображаемых) символов ASCII...........422 Приложение 3. Кодовая таблица Microsoft Code Page 866 (CP-866)....426
Голицына Ольга Леонидовна Попов Игорь Иванович Основы алгоритмизации и программирования Учебное пособие Редактор В. М. Голубев Корректор Н Е. Жданова Компьютерная верстка С. Ч. Соколовского Оформление серии П. Е. Родькин Сдано в набор 07.11.2007. Подписано в печать 10.12.2007. Формат 60x90 1/16. Бумага офсетная. Усл. печ. л. 27,0. Уч.-изд. л. 27,8. Тираж 3000 экз. Заказ № 7211. Издательство «ФОРУМ» 101000, Москва-Центр, Колпачный пер., 9а Тел./факс: (495) 625-32-07,625-52-43 E-mail: mail@forum-books.ru По вопросам приобретения книг обращайтесь: Отдел продаж издательства «ФОРУМ» 101000, Москва-Центр, Колпачный пер., 9а Тел./факс: (495) 625-52-43 E-mail: natali.forum@mail.ru Отдел продаж «ИНФРА-М» 127282, Москва, ул. Полярная, д. 31в Тел.: (495) 363-42-60 Факс: (495) 363-92-12 E-mail: books@infra-m.ru Центр комплектования библиотек 119019, Москва, ул. Моховая, д.16 (Российская государственная библиотека, кор. К) Тел.: (495) 202-93-15 Магазин «Библиосфера» (розничная продажа) 109147, Москва, ул. Марксистская, д. 9 Тел.: (495) 670-52-18, (495) 670-52-19 Отпечатано с предоставленных диапозитивов в ОАО «Тульская типография». 300600, г. Тула, пр. Ленина, 109.