4910

Основы программирования на языке турбо паскаль

Книга

Информатика, кибернетика и программирование

Язык программирования Паскаль, разработанный в 1970 г. профессором Швейцарской высшей политехнической школы Никлаусом Виртом специально для целей обучения студентов, быстро завоевал широкую популярность благодаря своей простоте, логичности языковых ...

Русский

2012-11-29

2.87 MB

22 чел.

Язык программирования Паскаль, разработанный в 1970 г. профессором Швейцарской высшей политехнической школы Никлаусом Виртом специально для целей обучения студентов, быстро завоевал широкую популярность благодаря своей простоте, логичности языковых конструкций и максимальному соответствию современным стандартам программирования. Однако основной успех в профессиональной среде ему был обеспечен с появлением диалектов под названием Турбо Паскаль, созданных американской фирмой Борланд. Последовательно сменяющие друг друга версии языка Турбо Паскаль, начиная с 3.0 (1985 г.), существенно увеличили функциональные возможности языка, сохраняя его первоначальную четкость и логичность построения. С помощью последних версий языка (7.0, Borland Pascal, Delphi) уже могут быть созданы программные комплексы практически любого объема и степени сложности.

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

Практически во всех разделах пособия теоретические сведения проиллюстрированы  примерами решения различных задач или их фрагментов. В приложении приведено свыше 100 программ с детальным их описанием, дано также около 200 задач по программированию для самостоятельного решения. Все программы, использованные в пособии, проверены на компьютере.

Учебное пособие представляет собой начальный курс программирования и не предполагает в обучаемых какого-либо опыта работы с компьютером, хотя наличие такого опыта по крайней мере в объеме курса информатики средней школы, безусловно, полезно. В пособии не рассматриваются вопросы организации и использования операционной системы MS DOS, операционной оболочки Norton Commander, интегрированной среды компилятора Turbo Pascal. Эти вопросы достаточно подробно изложены в литературе, их освоение является необходимым условием работы с компьютером.

Язык Турбо Паскаль обладает богатыми функциональными возможностями, но далеко не все они необходимы при освоении начального курса программирования. Цель учебного пособия  обучение основам алгоритмизации и программирования, используя достаточный, с точки зрения автора, набор конструкций языка Турбо Паскаль. В связи с этим в описании языка не рассматривается целый ряд его элементов, не применяются многие стандартные процедуры и функции. Например, в составе разделителей не указывается символ табуляции, в разделах описания Паскаль-программы не упоминается предложение exports, не используются процедуры Mark, Release и др.. Все эти конструкции описаны в литературе и могут быть освоены студентом в дальнейшем самостоятельно.

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

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


С Т Р У К Т У Р Н А Я   С Х Е М А   Э В М

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

от                                                                                                                                      к

 человека                                                                                                                             человеку

от                                                                                                                                      к

  датчика                                                                                                                              исполни-

                                                                                                                                             тельному

                                                                                                                                  устройству

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

Информация всегда вводится в основную память ЭВМ. Процессор производит непосредственную ее обработку. Промежуточные и конечные результаты вычислений записываются в память.

Основная память имеет сравнительно небольшой объем, но высокое быстродействие. При отключении питания ЭВМ информация в основной памяти, как правило, не сохраняется. Для длительного хранения больших объемов информации используются внешние запоминающие устройства: накопители на магнитной ленте (НМЛ), накопители на магнитном диске (НМД) и др. В персональных компьютерах для этой цели, как правило, используют накопители на гибких и жестких магнитных дисках.

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

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

Информация в ЭВМ содержится  в виде последовательности двоичных цифр 0 и 1. Ее обработка осуществляется путем выполнения элементарных машинных команд (сложение, вычитание, умножение и т.п.). Последовательность таких команд называется машинной программой.

Например, вычисление выражения

на условном машинном языке выглядело бы примерно следующим образом:

  1.  ( - )  < c >  < d >  R1

Из содержимого ячейки памяти, где находится число c, вычесть содержимое ячейки памяти, в которой находится число d, и результат записать в ячейку R1. Здесь 1)- номер (адрес) машинной команды; (-)- условный код операции; <c>- адрес первого операнда, т.е. уменьшаемого c; <d> - адрес второго операнда, т.е. вычитаемого d; R1- адрес результата, т.е. номер ячейки памяти, в которую требуется записать результат вычитания  cd.

2) ( * )  < a >  < x >  R2

3) ( * )  < b >  < y >  R3

4) ( + )    R2     R3   R2

5) ( : )    R2     R1  < z >

В этой программе 5 команд. В каждой команде указывается код операции, адреса первого и второго операндов и адрес результата. Все составные части машинной команды - это также последовательности цифр 0 и 1.

Примечание. Операнд - это обобщенный термин для обозначения величин, над которыми выполняется та или иная операция, т.е. аргумент операции (например, для операции деления операндами являются делимое и делитель).

Приведенный выше фрагмент программы на машинном языке может иметь следующий вид:

Адрес команды                          Машинная команда

 10100011     01010111100110111001110010100000

 10100100     01011000100110011001110110100001

 10100101     01011000100110101001111010100010

 10100110     01010110101000011010001010100001

 10100111     01011001101000011010000010011111

Здесь в каждой машинной команде первые 8 двоичных цифр – код операции, остальные группы по 8 цифр – адреса соответственно первого операнда, второго операнда и результата.

Программа, как и обрабатываемые числа, записывается в память ЭВМ. Выполнение команд программы производится, как правило, последовательно, начиная с адреса первой команды (пускового адреса программы). Принцип работы ЭВМ, при котором обрабатываемая информация и обрабатывающая программа одновременно находятся в памяти ЭВМ, называют принципом программного управления.

Машинный язык для человека трудно воспринимаем. Программирование на машинном языке характеризуется очень низкой производительностью. Поэтому для разработки программ обработки данных используют различные алгоритмические языки (Фортран, Бейсик, Паскаль и др.), которые в значительной степени приближены к привычной для человека форме чтения и записи информации. Программа на алгоритмическом языке записывается в память ЭВМ, а затем транслируется (переводится) на машинный язык. Этот перевод осуществляется автоматически специальной программой, которая называется транслятором (или компилятором). ЭВМ обрабатывает информацию только на машинном языке.

С И С Т Е М Ы   С Ч И С Л Е Н И Я

Системы счисления разделяются на позиционные и непозиционные. Примером непозиционной системы счисления является общеизвестная римская система. Например, число 1997 в этой системе имеет вид   M C M X C V II .

В качестве цифр в римской системе счисления используются прописные буквы латинского алфавита:

M = 1000;  D = 500;  C = 100;  L = 50;  X = 10;  V = 5;  I = 1.

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

1000 - 100 + 1000 - 10 + 100 + 5 + 1 + 1 = 1997

Значение цифры в римской системе не зависит от ее положения (позиции) в числе. Поэтому такая система счисления называется непозиционной.

Древнеславянская нумерация сходна с римской тем, что здесь для записи цифр также используют буквы алфавита. Например, число 333 в этой системе имеет вид  Т Л Г , где  Т = 300,  Л = 30,  Г = 3  (дополнительно над числом записывается также знак "титл", напоминающий перевернутую букву Z; титл - это признак числа). Славянская система счисления также является непозиционной.

Непозиционные системы практически непригодны для выполнения арифметических операций.

Примером позиционной системы счисления является привычная для нас десятичная система.

Рассмотрим произвольное число 83887. Это число можно записать как сумму значений отдельных его цифр:  80000 + 3000 + 800 + 80 + 7 . Как видно из этой записи, значение любой цифры в числе, в частности цифры 8, зависит от ее позиции.

Пусть число, записанное в произвольной позиционной системе счисления, имеет +1 цифру в целой части  и  цифр в своей дробной части:

,

где  - -ая цифра целой части,  - -ая цифра дробной части числа.

Тогда значение этого числа можно представить в виде

+  +...+  +  +  +  +...+  ,

где  - основание системы счисления.

Например, для десятичного числа  ( q = 10 )  имеем

83887,45 = 8 + 3 + 8 + 8 + 7 + 4 + 5 .

В ЭВМ наиболее часто применяются двоичная, восьмеричная и шестнадцатеричная системы счисления (2, 8 и 16 c/c). При этом восьмеричная и шестнадцатеричная системы используются, как правило, для компактной записи двоичных чисел.

1. Двоичная система счисления.

Количество цифр, используемых для изображения числа в любой позиционной системе счисления, равно основанию этой системы. В 2 c/c (  = 2 ) для этой цели применяются цифры 0 и 1.

Рассмотрим произвольное двоичное число

11010,101 = 1 + 1  + 0 + 1 + 0 + 1 + 0+ 1=

= 16 + 8 + 2 +  +  = 26 = 26,625   (10 c/c).

Представленная здесь схема разложения двоичного числа является одновременно схемой перевода из 2 с/с в 10 c/c   ( 2 10 ).

Рассмотрим теперь перевод  10 2.  Такой перевод производится отдельно для целой и дробной частей числа.

Пример 1.        43,37  =  101011,01

         43     2 

         42     21     2 

           1      20      10     2

                     1      10     5     2

                               0      4     2     2          

                                       1      2     1          

                                               0                  

Деление выполняется до тех пор, пока не будет получено частное, меньшее делителя. После этого цифры частного и остатков записываются в обратном порядке. В данном случае получим 101011.

В самом деле, мы 5 раз разделили исходное число на 2. Следовательно, в нем 1 раз содержится . Кроме этого, полученные остатки указывают, что в числе содержатся дополнительно  0 раз , 1 раз , 0 раз , 1 раз  и 1 раз .

Тогда можно записать:

43 = 1 + 0 + 1 + 0 + 1 + 1 ,

т.е. мы получили разложение числа по степеням основания 2.

Для перевода дробной части рассмотрим вначале произвольную десятичную дробь. Будем последовательно умножать на 10 исходное число и дробные части получаемых в процессе преобразования чисел до тех пор, пока дробная часть очередного числа не станет равной нулю.

                                          0, 935 10

                                          9, 35 10

                                          3, 5 10

                                          5, 0

Эта схема показывает, что в исходном числе содержится  9  (после первого умножения)  + 3  (после второго умножения) +  5  (после третьего умножения).

Если вместо умножения на 10 мы будем умножать на число q, то получим перевод дробной части числа в систему счисления с основанием q. Для исходного числа, приведенного в примере 1, получим:

                                          0, 375 2

                                          0, 75 2

                                          1, 5 2

                                          1, 0

Следовательно, 0,37  = 0,01  .

Конечная десятичная дробь не всегда образует конечную двоичную дробь. Например, для числа  0,4  имеем:

                                          0, 4 2

                                          0, 8 2

                                          1, 6 2

                                          1, 2 2

                                          0, 4 2

                                          0, 8 2

                                          1, 6 2

                                         ...............

0,  = 0,011001100110 = 0, (0110 .

Естественно, в этом случае полученное двоичное число округляют.

2. Восьмеричная система счисления.

Основанием системы является число 8. Для изображения произвольного числа используются 8 цифр:  0, 1, 2, 3, 4, 5, 6, 7.

Перевод   8 10 :

35174, = 3  + 5  + 1  + 7  + 4  + 6   =  34096 + 5512 +

+164 + 78 + 4 + 6/8  = 12288 + 2560 + 64 + 56 + 4 + 3/4  =  14972,7 .

Пример 2. Перевод  10 8 . Схема перевода такая же, как и для 2 с/c.

397,  =  615,1

              397     8                              0,   2 8

              32      49     8                      1,   6 8

                77      48     6                    4,   8 8

                72        1                            6,   4 8

                  5                                      3,   2 8

                                                          1,   6 8

                                                         …………

                          0, = 0,146314631… = 0,(1463

Полученная восьмеричная дробь числа (615,15) округлена до двух цифр.

Правило округления: чтобы округлить дробное число до  цифр, нужно к (+1)-ой цифре добавить половину цены разряда для данной системы счисления, после чего отбросить все дробные цифры, начиная с -ой. Для 8 c/c половина цены разряда равна 4, для 2 с/c - 1, для 16 c/c - 8.

В рассмотренном выше примере имеем (для   = 2):

            0, 1 4 6 3 1 4 6 3

        +            4               



           0, 1 5 2 3 1 4 6 3

Примечание. Здесь сложение выполнено в восьмеричной системе счисления.

Перевод  8 2. Для перевода восьмеричного числа в 2 c/c нужно каждую восьмеричную цифру записать в виде двоичной триады, т.е. трех двоичных цифр.

Восьмеричное

число

Двоичное

число

Двоичная

триада

0

1

2

3

4

5

6

7

   0

   1

 10

 11

100

101

110

111

000

001

010

011

100

101

110

111

Например,  3763,2 =  011 111 110 011 , 010 100  = 11111110011,010  (отброшены незначащие нули).

Приведенное выше правило перевода  8 2  связано с тем, что  8 =  .

В самом деле,

3763,24 = 3 + 7 + 6 + 3 + 2 + 4 = (0 + 1 + 1 ) + (1 + 1 + 1) + (1 + 1 + 0) + (0 + 1 + 1) + (0 + 1 + 0) + (1 + 0 + 0) = 0+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 0+ 0+ 1+ 1+ 0+ 1+0+ 1+ 0+ 0 =

= 011 111 110 011, 010 10 .

Для перевода  2 8  следует разделить двоичное число влево и вправо от запятой на триады, а затем заменить каждую триаду одной восьмеричной цифрой. Если первая триада в целой части или последняя триада в дробной части числа получаются неполными, то нужно дополнить их незначащими нулями.

Пример 3.

1011110111,1011010 = 001011110111,101101010 = 1367,55

В связи с тем, что перевод  10 8  или  8 10  выполняется быстрее, чем перевод  10 2  или  2 10, то перевод 10 2, как правило, производят по схеме 10 8 2 , а вместо  2 10 соответственно  2 8 10 .

3. Шестнадцатеричная система счисления.

Основанием системы является число 16. Для изображения произвольного числа нужно использовать 16 цифр. Так как цифр 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 недостаточно, то  дополнительно применяют первые буквы латинского алфавита: A (цифра 10), B (цифра 11), C (цифра 12), D(цифра 13), E (цифра 14), F (цифра 15).

Пример 4. Перевод  16 10:

A8B7,E =  101 + 81 + 111 + 71 + 141 = 104096 + 8256 +

+ 1116 + 7 + 14/16 = 43191,87  .

Пример 5. Перевод  10 16 .

                 7643,  =  1EDB,

7643    16                                              0, 4 16

64         477    16                                 6, 4 16

124        32        29    16                       6, 4 16

112       157      16       1                        6, 4 16

 123      144      14                               …………

 112        13

   11

Перевод  16 2 .  Так как  16 =  , то в этом случае каждая шестнадцатеричная цифра должна быть представлена двоичной тетрадой.

Шестнадцате-

ричное число

Двоичное

число

Двоичная

тетрада

Шестнадцате-

ричное число

Двоичное

число

Двоичная

тетрада

0

1

2

3

4

5

6

7

   0

   1

 10

 11

100

101

110

111

0000

0001

0010

0011

0100

0101

0110

0111

8

9

A

B

C

D

E

F

1000

1001

1010

1011

1100

1101

1110

1111

1000

1001

1010

1011

1100

1101

1110

1111

Пример 6.

4AD,B8 = 0100 1010 1101, 1011 1000 = 10010101101,10111.

Перевод  2 16.  Исходное число разделяют влево и вправо от запятой на тетрады, а затем каждую тетраду записывают одной шестнадцатеричной цифрой.

Пример 7.

11101111100001,0110 =0011101111100001,01101100 =  3BE1,6С

Для сокращения вычислений вместо  10 2  выполняют  10 16 2 , а вместо  2 10  выполняют  2 16 10 .

Примечание. Очевидно, что переводы из 16 с/с в 8 с/с и наоборот выполняют с применением 2 с/с как буферной. Например, перевод 16 8 производится по схеме 16 2 8.

4. Сложение и вычитание в  2, 8 и 16 c/c.

Методика выполнения операций сложения и вычитания в этих системах аналогична выполнению соответствующих операций в 10 с/с. Ниже приведены примеры указанных операций.

Пример 8. Двоичная система счисления.

                                                                                    Проверка вычитания

     1 0 1 1 0 1 1, 1 1 1           1 0 1 1 0 1 1, 1 1 1             1 0 1 1 0 1, 0 1 0

  +    1 0 1 1 0 1, 0 1 0         -   1 0 1 1 0 1, 0 1 0          +  1 0 1 1 1 0, 1 0 1

                         

   1 0 0 0 1 0 0 1, 0 0 1             1 0 1 1 1 0, 1 0 1           1 0 1 1 0 1 1, 1 1 1

Пример 9. Восьмеричная система счисления.

                                                                               Проверка вычитания

       7 3 0 4 6, 3 5                   7 3 0 4 6, 3 5                6 4 2 5 2, 6 7

     +   6 5 7 3, 4 6                  -   6 5 7 3, 4 6            +    6 5 7 3, 4 6

                              

     1 0 1 6 4 2, 0 3                   6 4 2 5 2, 6 7               7 3 0 4 6, 3 5

Пример 10. Шестнадцатеричная система счисления.

                                                                              Проверка вычитания

      8 A 7 B E, D 3                 8 A 7 B E, D 3              7 B 6 C A, 6 7

   +    F 0 E 4, 6 C              -    F 0 E 4, 6 C                     F 0 E  4, 6 C

                               

      9 9 8 A 3, 3 F                 7 B 6 C A, 6 7                 8 A 7 B E, D 3

Определенная трудность у начинающего программиста возникает при выполнении операции вычитания в 2, 8 или 16 с/с. Ссылка на то, что методика получения результата аналогична выполнению вычитания в 10 с/с, здесь не всегда помогает. Дело в том, что многие манипуляции, в том числе арифметические действия, мы выполняем автоматически, не задумываясь над последовательностью своих действий. Формализация же этих действий и составляет основу алгоритма решения поставленной задачи. В связи с этим обсудим в деталях порядок выполнения операции вычитания на конкретном примере.

Пример 11.   8 A 0 0 3 E 7 B  -  1 9 D 5 6 4 A A  =  7 0 2 A D 9 D 1

1)

                                                10

                     8 A 0 0 3 E 7 B  

            -  1 9 D 5 6 4 A A

                     .  .   .  9 D 1

Обозначим цифры уменьшаемого через , вычитаемого - через , разницы - через  (если в вычитаемом меньше цифр, чем в уменьшаемом, то вычитаемое нужно дополнить слева незначащими нулями). При нумерации цифр справа налево индекс  в данном примере изменяется от 0 до 7.

Если  , то результат получаем обычным образом. Для    имеем

(BA)16 = (11 – 10)10 = 1 .

Для    имеет место   .  Поэтому ищем ближайшую слева значащую цифру (это  ), отнимаем от нее единицу и переносим эту единицу в первый разряд. То, что в  изъята единица, обозначено точкой, а то, что это значение добавлено в первый разряд, записано над цифрой 7  (1016 = 1610). Тогда

(10 + 7)16 =  1716 = 2310;    (23 – 10)10 = 1310 = D16;

= D – 4 = 9  (от значения цифры Е ранее была отнята 1).

2)

                                  

                             10 10 1010

                     8 A 0 0 3 E 7 B  

            -  1 9 D 5 6 4 A A

               7 0 2 A D 9 D 1

. Ближайшая слева значащая цифра – это   A. Производим последовательный перенос единицы в  , , . Эти действия обозначены на схеме операции.

(10 + 3 – 6)16 = (19 – 6)10 = 1310 = D16 ;

(F – 5)16 = (15 – 5)10 = 1010 = A16 ;

(FD)16 = (15 – 13)10 = 2 ;

9 – 9 = 0 ;

8 – 1 = 7 .

Правильность выполнения операции вычитания целесообразно проверить сложением вычитаемого и разницы.

Ч И С Л А   С   Ф И К С И Р О В А Н Н О Й

И   П Л А В А Ю Щ Е Й   З А П Я Т О Й

Каждая ЭВМ использует для представления чисел фиксированное количество двоичных разрядов. Их называют обычно разрядной сеткой ЭВМ.

Представим, что в условной ЭВМ (например, на калькуляторе) разрядная сетка содержит 10 десятичных разрядов:

    1          2           3          4          5           6          7           8          9         10  

Произвольное число в общем случае имеет целую и дробную части. Следовательно, в разрядной сетке нужно установить границу между этими частями.

Поставим разделяющую запятую, например, между шестым и седьмым разрядами. Тогда первые 6 разрядов сетки представляют целую часть числа, а последние 4 разряда - его дробную часть. Максимальное значение числа в этом случае равно 999999,9999; минимальное -  0,0001. Следовательно, при такой разрядной сетке обработка чисел может быть организована лишь в диапазоне  0,0001 .. 1000000, что явно недостаточно.

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

В ЭВМ, как правило, применяют один из двух способов представления чисел с фиксированной запятой:

1) запятая фиксирована перед старшим разрядом; в этом случае число имеет только дробную часть и не имеет целой части;

2) запятая фиксирована после младшего разряда; в этом случае число имеет только целую часть и не имеет дробной части.

Наиболее часто применяется второй способ.

Числа с плавающей запятой имеют следующую форму представления:

,

где   - мантисса;   - порядок; 10 - основание системы счисления, записанное в этой же системе  (  = 2 ;   = 8 ;   = 16 ). Это так называемая экспоненциальная (или показательная) форма записи числа.

Например, число 358,5 можно записать в виде

0,3585  = 358,5   = 3585,0   = 0,003585  .

Чтобы обеспечить единственность представления числа, на мантиссу накладывается следующее ограничение:

0,1    < 1  ( в данной c/c).

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

В разрядной сетке машины часть разрядов выделяется для мантиссы, а часть - для порядка числа.

Предположим, что в приведенной ранее разрядной сетке для мантиссы отведено 8 разрядов, а для порядка - 2 разряда (знак числа и знак порядка временно не рассматриваются). Тогда максимальное значение мантиссы составляет 0,99999999 , что примерно равно 1; максимальное значение порядка равно 99; число имеет максимальное значение  1  ,  что достаточно для любых практических применений.

Сравним выполнение операции сложения для чисел с фиксированной запятой (целых чисел) и чисел с плавающей запятой.

Пусть   = 78535,   = 416.  В формате целых чисел они имеют вид  = 0000078535,   = 0000000416. Их сумма получается по обычному правилу сложения:

                             0 0 0 0 0 7 8 5 3 5

                        +   0 0 0 0 0 0 0 4 1 6

                          

                             0 0 0 0 0 7 8 9 5 1

В формате с плавающей запятой заданные числа имеют вид

= 0,78535  ,    = 0,416  ,

или в машинном представлении  = 7853500005,   = 4160000003.

Непосредственно складывать мантиссы, если слагаемые имеют разные порядки, нельзя. В этом случае сложение должно выполняться в следующей последовательности:

1) Определяется разница порядков слагаемых ; здесь имеем   = 2.

2) Если   > 0,  то   сдвигается вправо на разрядов; если < 0, сдвигу подвергается ;  при = 0  сдвиг не производится.

В данном примере после сдвига получим:

 =  0,78535000;     =  0,00416000

3) Выполняется сложение мантисс

=  0,78951000

4) Результату приписывается максимальный из порядков слагаемых

 =  max()  =  5

В результате получаем

= 7895100005

При сложении мантисс может иметь место случай   > 1  (например, для  = 0,8 ,   = 0,7  ) . Тогда получаемый результат нужно нормализовать, т.е. суммарную мантиссу сдвинуть на один разряд вправо, а к порядку добавить 1.

Из примера видно, что способ сложения чисел с плавающей запятой более сложный по сравнению со сложением целых чисел. Следовательно, для выполнения арифметических операций над числами с плавающей запятой требуется больше машинного времени процессора, чем для выполнения таких же операций по отношению к целым числам. В связи с этим практически в любой ЭВМ числа представлены как в формате с фиксированной запятой, так и в формате с плавающей запятой. При программировании рекомендуется применять в первую очередь целые числа, если это возможно по условиям задачи. Обычно целочисленные переменные в программе определяют объекты, которые могут принимать только целые значения (количество предметов, порядковый номер предмета и т.п.). Числа с плавающей запятой отображают вещественные числа, которые в технических задачах определяют, как правило, результаты измерения каких-либо параметров (длина, вес, площадь, время и т.п.).

В заключение рассмотрим вопрос о происхождении названия "число с плавающей запятой".

Пусть в разрядной сетке условной машины содержится число

   u      p   

5

8

6

5

9

1

0

0

0

1

т.е. число   = 0,586591   =  5,86591 .

Если рассматривать его как число с фиксированной запятой, то разделяющая запятая должна быть поставлена после первого разряда (  = 1 ).

Для числа

5

8

6

5

9

1

0

0

0

2

запятая будет установлена после второго разряда ( = 58,6591;   = 2).

Для числа

5

8

6

5

9

1

0

0

0

3

запятая устанавливается после третьего разряда  ( = 586,591)  и т.д.

При этом создается впечатление, что запятая "плавает" по разрядной сетке при изменении порядка числа.

П Р Е Д С Т А В Л Е Н И Е   И Н Ф О Р М А Ц И И   В   П Э В М

Языки высокого уровня (Паскаль, Си, Бейсик и т.п.) не ориентированы на конкретный тип ЭВМ. Это означает, что программа, написанная, например, на языке Паскаль, должна решаться без изменений на ЭВМ любого типа, имеющей транслятор с данного языка. В связи с этим программиста практически не интересуют конкретные технические сведения о конструкции и особенностях функционирования отдельных устройств ЭВМ. Тем не менее для создания эффективной программы необходимо обладать некоторым минимальным объемом знаний о технических параметрах ЭВМ, на которой будет реализована эта программа, в частности о способах представления числовой и нечисловой информации.

В ПЭВМ, как и во многих машинах другого типа, минимальной единицей обрабатываемой информации является байт (byte). Байт состоит из 8 двоичных разрядов, или бит (bit, от слов BInary digiT - двоичная цифра). Биты нумеруются  0, 1, 2, 3, 4, 5, 6, 7. Биты 0..3 и 4..7 образуют два полубайта - левый и правый, представляемые как двоичные тетрады. При записи содержимого байта каждый полубайт обозначают одной шестнадцатеричной цифрой.

Возможные значения байта:

              0000 0000  =  00

              0000 0001  =  01

              0000 0010  =  02

              ................

              1111 1111  =  FF16  = 25510.

Следовательно, байт может принимать 256 различных значений.

Соответствие между кодовыми комбинациями байта и символами, реализуемыми на ПЭВМ, отображается в кодовой таблице ASCII (American Standard Code for Information Interchange – американский стандартный код для обмена информацией). В этой таблице представлены латинские и русские буквы, цифры, знаки операций и др. Например, цифре 6 соответствует кодовая комбинация 00110110 (), букве K - код 01001011 (4B16) и т.д.

Каждый байт в памяти ЭВМ имеет свой номер (адрес). Адреса изменяются последовательно от 0 до некоторого максимального значения, определяемого объемом памяти ЭВМ. Объем памяти измеряют в килобайтах, мегабайтах, гигабайтах.

1 Kбайт  =    байт  =  1024 байта;

1 Мбайт  =    Kбайт =    байт;

1 Гбайт  =    Мбайт =    байт.

Для ПЭВМ, имеющей объем памяти 1 Мбайт, максимальный адрес равен FFFFF (FFFFF = 100000 - 1 =  - 1 =  - 1).

Байты могут обрабатываться каждый отдельно или полями. Поле - это группа последовательных байтов. Длина поля равна количеству содержащихся в нем байтов. Адресом поля является адрес его крайнего левого байта. Некоторые поля имеют отдельные наименования: слово (поле длиной 2 байта), двойное слово (поле длиной 4 байта).

Все, что обрабатывает ЭВМ, обобщенно называют данными. К ним, в частности, относятся целые и вещественные числа, а также так называемые логические данные.

1. Целые числа (числа с фиксированной запятой). 

ПЭВМ имеет несколько типов целых чисел, различающихся между собой количеством содержащихся в них разрядов. Здесь будет рассмотрен тип integer.

Для чисел типа integer отводится поле длиной 2 байта. Биты этого поля нумеруются в последовательности  0, 1, ... , 15.  Нулевой бит, т.е. бит с нулевым порядковым номером содержит знак числа ("0" - это знак "+", "1" - знак "-").

Например, число   3104 =  C20  в формате integer имеет вид

0C20  =  0000 1100 0010 0000

Здесь 16 c/c используется для компактного изображения двоичного числа.

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

Отрицательные числа с фиксированной запятой представлены в так называемом дополнительном коде.

Дополнительный код отрицательного числа - это его дополнение до такого числа, которое в этой же системе счисления представлено единицей и столькими нулями, сколько цифр имеет исходное число.

Пример 1.

          10 c/c                     16 c/c                       2 c/c

        1 0 0 0 0                 1 0 0 0 0                  1 0 0 0 0

      -   2 8 9 3                -   A 5 4 C               -    1 0 1 0

                                      

          8 1 0 7                     5 A B 4                     0 1 1 0

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

Для 10 c/c максимальная цифра - это 9, для 16 c/c - F, для 2 c/c - 1. Отметим, что для 2 c/c первый этап указанного преобразования сводится к инвертированию числа, т.е. замене нулей на единицы, а единиц - на нули.

Для  числа -3104   в  формате  integer  имеем  F3E0. Для  числа -58  = -003A  получим  FFC6.

Дополнительный код позволяет заменить операцию вычитания операцией сложения с дополнительным кодом отрицательного числа.

Пример 2. Пусть нам требуется выполнить в 10 c/c операцию

 6 5 8 6 1

- 4 8 2 7 3



 1 7 5 8 8

Вместо вычитания числа 48273 выполним сложение с его дополнительным кодом:

                                           6 5 8 6 1

                                       +  5 1 7 2 7

                                      

                                        11 7 5 8 8

Обведенная единица переноса выходит за пределы разрядной сетки и теряется. Следовательно, в обоих случаях мы получили одинаковые результаты.

Использование дополнительного кода позволяет отказаться от установки в процессоре блока вычитания, что упрощает его конструкцию.

Максимальное значение числа формата integer:

= 0111 1111 1111 1111 = 1000 0000 0000 0000 - 1 =  - 1  =  32767

Минимальное значение определяется кодом  1000 0000 0000 0000, которому соответствует число  -32768.

Рассмотрим формирование отрицательного минимума для формата integer.

8000  = 1000 0000 0000 00002 

    1111 1111 1111 1111  - доп.код числа -1

 + 1000 0000 0000 0001  - доп.код числа -111 1111 1111 1111 = - (  - 1)

 

1  1000 0000 0000 0000    

Единица переноса из старших складываемых разрядов отбрасывается.

Результат:     8000  =  - (  - 1) - 1  =  -    = - 32768 .

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

Пример 3 (для 16 с/с):

        исходное число                                                   - 5B9538B6

       дополнительный код                                             A46AC74A

       дополнительный код дополнительного кода    - 5B9538B6

2. Вещественные числа (числа с плавающей запятой).

ПЭВМ имеет несколько типов вещественных чисел, различающихся количеством разрядов, отведенных для мантиссы и порядка числа. Мы будем рассматривать тип real.

Тип real - это двоичное число, занимающее 6 байт памяти. Нумерация двоичных разрядов  0, 1, 2, ... , 47.  Первые пять байтов занимает мантисса числа, шестой байт - характеристика. Нулевой бит отведен для знака числа. Отрицательные числа изображаются в прямом коде.

Характеристика - это преобразованный определенным образом порядок числа.

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

=  + 1000 0000 =  + 80  =  + 128 ,

где   - характеристика числа,   - его порядок.

Это приводит к смещению значения порядка на 128, в связи с чем характеристику называют также смещенным порядком.

Характеристика всегда положительная.

Если   - 128  < 0,   то    0  <  128;

если                 = 0,   то          =  128;

если         0 <   127, то   128 <   255.

Максимальной характеристике  = 255 соответствует порядок  = 127, минимальной характеристике   = 0  - порядок  = -128. При этом получаем соответственно максимальное и минимальное значения вещественного числа:

= 1     1.7  ;    = 1     2.95   .

Примечание. Если в программе задать  x > 1.7 , то при трансляции будет выведено на экран сообщение «Const out of range» (Константа вне допустимых границ), если задать  x < 2.95 , то переменной x будет присвоено нулевое значение.

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

Пример 4.

687,25 = 2AF,4 = 0,2AF4   = 0, 0010 1010 1111 0100   =

= 0, 1010 1011 1101     (после нормализации мантиссы) =

= 0010 1011 1101 0000 0000 0000 0000 0000 0000 0000 1000 1010

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

Здесь порядок   = 10, характеристика   = 10 + 128 = A + 80 = 8A.

В формате real имеем  2B D0 00 00 00 8A .

Для отрицательного числа достаточно записать единицу в нулевой разряд, что эквивалентно добавлению значения 8 к первой цифре положительного числа. В этом случае для числа  687,25 получим AB D0 00 00 00 8A (первая цифра A = 2 + 8 = 0010 + 1000 = 1010).

3. Логические данные.

Логические переменные и логические операции относятся к области математики, которая называется алгеброй логики. В алгебре логики рассматриваются высказывания, в отношении которых имеет смысл говорить об их истинности или ложности. Например, "снег белый", "сегодня - пятница", " > 0", ""  и т.д. Истинность высказывания может принимать одно из двух значений:  0 (высказывание ложное) или 1 (высказывание истинное). Алгебра логики широко применяется при проектировании и анализе работы устройств ЭВМ, поскольку элементы, входящие в состав таких устройств, являются бинарными (двоичными) и могут находиться лишь в одном из двух возможных состояний, которые обозначаются соответственно 0 и 1. В программировании операции алгебры логики используются при вычислении логических выражений.

Алгебра логики определяет 16 логических операций. Наиболее важными из них являются три: отрицание, логическое умножение и логическое сложение.

а) Отрицание  ( операция НЕ ). Таблица операции:

Читается: "Не нуль есть единица".

б) Логическое умножение (конъюнкция, операция И). Таблица операции:

0 0 = 0

0 1 = 0

1 0 = 0

1 1 = 1

Читается: "Нуль и нуль есть нуль".

в) Логическое сложение (дизъюнкция, операция ИЛИ).  Таблица операции:

0 0 = 0

0 1 = 1

1 0 = 1

1 1 = 1

Читается: "Нуль или нуль есть нуль".

Логические операции являются битовыми. Если их применяют для полей битов, то эти операции выполняются отдельно для каждой пары бит.

Пример 5. Пусть мы имеем два поля X и Y длиной 4 байта:

X = F570 1A8B ;   Y = 37E4 90CD .

Здесь шестнадцатеричная запись используется только с целью компактности представления содержимого поля памяти.

Тогда

      =  0 A 8 F   E 5 7 4

X /\ Y =  3 5 6 0    1 0 8 9

 X \/ Y =  F 7 F 4    9 A C F

Примечание. Чтобы выполнить записанные выше операции, следует исходные числа (содержимое полей памяти) X и Y представить в виде последовательности двоичных тетрад, применить заданные логические операции отдельно для каждой пары бит, а затем каждую тетраду полученного числа записать одной шестнадцатеричной цифрой.

Л Е К С Е М Ы   И   Р А З Д Е Л И Т Е Л И

Программа на Паскале состоит из лексем и разделителей. Лексема - это минимальная значимая единица языка, состоящая из одного или нескольких последовательных символов.

Лексемами являются следующие элементы:

    - специальные символы;

    - зарезервированные слова;

    - числа;

    - строки символов;

    - идентификаторы;

    - метки.

Разделителями в Паскале являются:

    - пробел;

    - конец строки (разделитель строк);

    - примечание (комментарий).

Пробел - это пустое место в тексте на бумаге или на экране дисплея.

Представим, что мы записываем в память ЭВМ произвольный текст. Слова в тексте разделены пробелами. Память ЭВМ - это линейная последовательность байтов. В каждом байте содержится один символ текста. Пробел, как и буквы в словах, отображается вполне конкретным содержимым байта.

Соответствие между символом и его кодом представлено в кодовых таблицах. Для ПЭВМ обычно используется таблица ASCII (American Standard Code for Information Interchange). В этой таблице записаны 256 символов с порядковыми номерами  0 .. 255  (цифры, прописные и строчные буквы, знаки операций, знаки препинания и др.). Порядковый номер символа в таблице ASCII - это код данного символа.

Первые 32 символа ASCII - управляющие символы, которые используются при обращении к внешним устройствам. Эти символы имеют названия, но не имеют графического обозначения. Обычно в программе их задают порядковым номером в таблице ASCII. Например, #7, #12 (символ "#" - это знак номера). Порядковым номером можно задавать и другие символы. Например, #32 (пробел), #158 (буква "Ю"). Содержимое байта для пробела будет иметь значение  3210 = 2016 =001000002, для буквы "Ю" – 15810= 9E16 = 100111102.

Текст "Programming in Pascal" будет изображен следующей последовательностью байтов:

50726F6772616D6D696E6720696E2050617363616C

Память ЭВМ можно представить в виде длинной ленты, разделенной на ячейки (байты). Такая лента представляет собой одномерный объект. В отличие от плоскости (листа бумаги), являющейся двумерным объектом, на ленте нельзя записать строки текста друг под другом. Следовательно, необходимо использовать какой-либо символ для обозначения строк записанного текста. Таким символом является "Конец строки", используемый для разделения текста в памяти ЭВМ на отдельные строки, длина которых может быть различной. Этому символу соответствуют два последовательных байта с кодами #13 и #10.

Комментарий в программе определяется парой символов, обозначающих его начало и окончание. В качестве таких символов могут использоваться  "{" и "}" или "(*" и "*)". Текст комментария, заключенный в фигурные скобки, может содержать любые символы ASCII, кроме символов начала и конца комментария. Символы "(*" и "*)" имеют более высокий приоритет; между ними могут быть символы "{" и "}". Комментарий не влияет на работу программы и предназначен для лучшего понимания программы человеком. При трансляции все комментарии удаляются из программы.

Лексемы в Паскаль-программе обязательно разделяются одним или несколькими символами-разделителями. Обычно это пробелы.

При конструировании лексем используются буквы и цифры. К буквам относятся прописные и строчные латинские буквы (но не буквы национального алфавита, в частности русского):  A B C D ... X Y Z a b c d ... x y z . Цифры:  0  1  2  3  4  5  6  7  8  9 .

1. Специальные символы (их количество равно 27):

+  -  *  /  .  ,  :  ;  '  ^  =   <>   >   <   >=   <=   (  )  [  ]  {  }  :=   ..  @  $  #

Примечание. Символом "<>" в Паскале обозначается операция отношения "не равно" (""), символом ">=" - операция "больше или равно" (""), символом "<=" - операция "меньше или равно" (""). Символ «^» (тильда) в дальнейшем используется для обозначения динамических переменных.

2. Зарезервированные слова.

Эти слова имеют четко установленный смысл в Паскаль-программе. Примеры таких слов:   Begin,   End,   If,   And,   Array,  For .

Зарезервированные слова нельзя применять в программе для обозначения переменных, массивов и других объектов.

Прописные и строчные буквы в зарезервированных словах эквивалентны.

Например, BEGIN  Begin  begin.

Примечание. В программах, приводимых в учебном пособии, зарезервированные слова выделены полужирным шрифтом.

3. Числа. В Паскаль-программе используются целые десятичные, целые шестнадцатеричные и вещественные десятичные числа.

Целые десятичные числа записываются обычным образом и должны находиться в диапазоне от  -2 477 483 648  до  2 147 483 647  (от  -  до  ).

Пример 1.   0     21     -456     3897653     -987321123  .

Для обозначения целых шестнадцатеричных чисел используется префикс $, который ставится перед числом. Эти числа определены в диапазоне от $00000000  до  $FFFFFFFF. В качестве шестнадцатеричных цифр могут использоваться как прописные, так и строчные буквы латинского алфавита, но предпочтительно все же использовать прописные буквы.

Пример 2.   $0   $A5F  $E45D07B9  $ab7f.

Шестнадцатеричные числа применяются главным образом для указания содержимого поля памяти или задания адреса переменной.

Вещественные числа могут быть представлены в двух различных формах записи: обычной и показательной. В обычной форме число записывается в виде целой и дробной частей, разделенных точкой; в показательной - в виде мантиссы и порядка с основанием 10, при этом в качестве признака основания ставится прописная буква "E" или строчная буква “e”.

Пример 3.   0.6  -32.648   6.0E-1  0.6E0  0.06E1  0.06E+1  -3.2648E1  -5.6e-12.

4. Строка символов - это последовательность символов таблицы ASCII, заключенная в апострофы. Апостроф определяет границы строки.

Если внутри строки нужно поставить апостроф, то он ставится дважды.

Пример 4.

'A';   'a+b=c';   'This string has 30 characters';  ' Символ  '' - это апостроф'

В строке прописные и строчные буквы считаются различными, так как они имеют различные номера в таблице ASCII. Поэтому

'PASCAL'    'Pascal';   'ПРОГРАММА'    'программа'

5. Идентификатор (имя). Это последовательность букв и цифр, начинающаяся с буквы.

Конструкцию грамматических объектов алгоритмического языка можно наглядно и точно изобразить на синтаксической диаграмме, которая в данном случае имеет вид:

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

Например, для цифры имеем

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

Примеры идентификаторов:                                

X     A8    alpha   Massiv   z52d9   eps    Res_52_a     ___75

Прописные и строчные буквы в идентификаторах считаются эквивалентными. Поэтому PASCAL, Pascal, pascal - это один и тот же идентификатор.

Длина идентификатора в Турбо Паскале не ограничивается, но значимыми считаются лишь первые 63 символа.

6. Метка.

Метка в Паскале представляет собой целое без знака и используется для маркировки операторов программы. Метка может принимать значения от 0 до 9999. В Турбо Паскале в качестве метки можно применять также идентификатор. Внешним признаком метки в программе является двоеточие, которое записывается перед маркируемым оператором.

П Р О С Т Ы Е   Т И П Ы   Д А Н Н Ы Х

Данные - это общее понятие для всего того, с чем оперирует ЭВМ.

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

Для примера рассмотрим в ПЭВМ два поля памяти А1 и А2 длиной 4 байта:

                       A1                                        A2

Содержимое полей A1 и A2 - это последовательность двоичных цифр 0 и 1, которая ничего не означает, если не применить к ней конкретную машинную операцию.

Четырехбайтное поле в ПЭВМ может интерпретироваться как целое число длиной 4 байта (тип longint, условно формат L), как число с плавающей запятой длиной 4 байта  (тип single, условно формат S), как строка символов, как битовая последовательность и др. Если для полей A1 и A2 мы применим операцию сложения для формата L  “( + ) L   A1   A2”  ,  то тогда содержимое полей A1 и A2 будет рассматриваться как целые двоичные числа типа longint.

Для операции   “( + ) S    A1   A2”  содержимое полей A1 и A2 интерпретируется как числа с плавающей запятой типа single.

При использовании логической операции     “( \/ )    A1   A2”  содержимое этих же полей памяти интерпретируется как битовые последовательности длиной 32 бита.

Пусть содержимое A1 в шестнадцатеричной записи имеет вид    50 6A 37 B1. Рассмотрим его интерпретацию при обработке различными машинными операциями.

  1.  Тип longint.

5 + 0 + 6 + 10 + 3 + 7 + 11 + 1 =  1 349 088 353.

б) Тип single (в предположении, что подобно типу real первые три байта поля A1 - мантисса, а четвертый байт - характеристика вещественного числа).

= B1;   =  - 80  = B1 - 80 = 31  = 490,101 0000 0110 1010 0011 0111  = 0,1101 0000 0110 1010 0011 0111  =

= 0,D06A37  = D06A37    = 13 658 679 33 554 432 = 458 309 215 275 328

в) Битовая последовательность:   0101 0000 0110 1010 0011 0111 1011 0001

г) Строка символов (по таблице ASCII):   'Pj7Б' .

При программировании на Ассемблере, который фактически представляет собой условную запись машинного языка, программист может по-разному использовать одно и то же поле памяти. Эта свобода действий одновременно таит в себе опасность неверной трактовки содержимого поля памяти (например, целое число может ошибочно обрабатываться как число с плавающей запятой). Ограничение такой свободы и возложено на тип данных, основным назначением которого является контроль корректности использования переменных.

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

Тип данных определяет:

    - множество значений, которые имеет право принимать переменная заданного типа;

    - множество операций, которые допустимы при обработке этой переменной;

    - формат внутреннего представления данных в памяти ЭВМ, в том числе объем памяти, выделяемой для размещения переменной.

С каждой переменной в программе может быть сопоставлен только один тип.

Классификация типов данных в Турбо Паскале представлена на приведенной ниже схеме.

В учебном пособии будут рассмотрены все перечисленные здесь типы данных, кроме классов, которые являются основой в объектно-ориентированном программировании.

Классификация простых типов данных представлена на следующей схеме. Здесь термин "ординальный" означает "порядковый" (ordinal - порядковое числительное).

Ординальный тип описывает конечное и упорядоченное множество значений, для каждого элемента которого однозначно определяется предыдущий и следующий за ним элементы. В частности, последовательность целых чисел является ординальным типом данных. Например, для элемента 5 существует единственное следующее значение (6) и единственное предыдущее значение (4). Символы алфавита также представляют собой ординальный тип данных. Например, в латинском алфавите для символа "M" следующим является символ "N", а предыдущим - "L". В то же время вещественные числа нельзя отнести к ординальному типу. Так, для числа 15.8 существует бесконечное множество следующих и предыдущих значений (15.81, 15.801, 15.8001 и т.д.).

Каждый ординальный тип имеет минимальное и максимальное значения. Для всех значений, кроме минимального, существует предшествующее значение; для всех значений, кроме максимального, существует последующее значение.

Предописанные функции succ, pred и ord воспринимают аргументы любого из ординальных типов.

Функция succ() (succeed - следующий) дает следующее после  ординальное значение; функция pred() (predecessed - предшествующий) - предшествующее значение; функция ord() дает ординальный (порядковый) номер для значения .

Значения переменных ординального типа однозначно отображаются на последовательность порядковых номеров  0, 1, 2,... Исключение сделано только для целых чисел, которые отображаются сами на себя.

  Пример 1.

  succ(8) = 9 ;     succ('d') = 'e' ;

  pred(8) = 7 ;     pred('d') = 'c' ;

  ord(' ') = 32;     ord('Ю') = 158 ;

  ord(8) = 8 ;        ord(-8) = -8 .

Для всех ординальных типов существуют операции отношения "=", "<>", "<", "<=", ">", ">=". При этом предполагается, что оба операнда в операции отношения имеют одинаковый тип. Отношение определяется с помощью порядковых номеров, присущих операндам. Например, проверка истинности отношения  'A' > 'Z' заменяется проверкой отношения ord('A') > ord('Z').

Термин "предописанный" (или "предопределенный"), в отличие от термина "зарезервированный", означает, что в принципе предописанные слова можно переназначать в программе, например, использовать их как имена переменных. Однако такое переназначение делать не рекомендуется, поскольку это ухудшает понимание программы.

1. Логический тип (тип boolean).

Логическое значение - это одно из двух значений истинности, которые обозначают предописанными именами false и true (false эквивалентно значению 0, true - значению 1 в алгебре логики; использование имен false и true вызвано стремлением отличать в программе логические значения от числовых значений 0 и 1).

Логический тип определен так, что false < true, что соответствует машинному представлению логических констант false и true.

Операции отношения всегда дают логический результат. Например, отношение x+1<y  при  x = 0, y = 0  дает  false; при  x = 0, y = 10  - значение true.

Для переменных логического типа в Турбо Паскале определены четыре логические операции:

1) отрицание  not;

2) логическое умножение  and;

3) логическое сложение  or;

4) исключающее ИЛИ  (отрицание равнозначности, сложение по модулю 2)  xor.

Исключающее ИЛИ определяется следующей таблицей операций:

0 0 = 0

0 1 = 1

1 0 = 1

1 1 = 0

Как было ранее указано в разделе «Представление информации в ПЭВМ», в алгебре логики основными операциями являются отрицание not, логическое умножение and и логическое сложение or. Все остальные операции могут быть выражены через эти базовые операции. Например, a xor b эквивалентно

(a  or  b)  and  (not  a  or  not  b).

Обозначения в символах алгебры логики:

.

Справедливо также выражение

.

Примечание.

Если переменные  a, b, c  имеют тип boolean, то оператор

c:=a xor b

эквивалентен оператору

c:=a<>b.

В самом деле, из таблицы операций видно, что если переменные a и b имеют одинаковые значения, то переменная с получает значение 0, в протвном случае – значение 1.

Пример логического выражения:

((x + 1 < y) and not (x > 5)) or ((y > 0) and true) .

Вычислим значение этого выражения при  x = 5, y = 5 :

((6 < 1) and not (5 > 5)) or ((5 > 0) and true) ( false and not false ) or 

( true and true ) ( false and true ) or ( true and true ) false or true true .

В символике алгебры логики:

.

Для переменной типа boolean отводится один байт памяти. Машинное представление значения true имеет вид 00000001, значения false -  00000000, т.е. ord(false) = 0, ord(true) =1.

Существуют несколько предописанных логических функций, т.е. функций, которые дают логический результат. В частности, при обработке целочисленных переменных применяется функция odd(x), которая дает значение true, если целое x - нечетное, и значение false, если x - четное. Другие логические функции будут рассмотрены позже.

2. Целые типы данных.

Значениями переменных целого типа являются элементы ограниченного подмножества целых чисел.

В Турбо Паскале имеется пять целочисленных типов данных: shortint, byte, integer, word и longint.

Тип shortint (short  intеger – короткое целое) обозначает целочисленную переменную длиной один байт со знаком. Биты однобайтного поля памяти нумеруются в последовательности 0, 1, 2, 3, 4, 5, 6, 7. Нулевой бит – это знак числа:  0 – «+», 1 – «-». Следовательно, значение числа определяется семью двоичными цифрами. Число типа shortint изменяется в пределах  -128 .. 127 (минимальное значение  100000002 = -12810, см. раздел «Представление информации в ПЭВМ»).

Тип byte – это короткое целое длиной один байт без знака (все восемь бит - двоичные цифры), пределы изменения  0 .. 255.

Тип integer определяет целое число длиной два байта со знаком. Пределы изменения –от –32768 до 32767. Этому типу соответствует предописанная константа MaxInt, равная максимальному значению числа: MaxInt  =    - 1  =  32767 .

Тип  word – это целое длиной два байта без знака, пределы изменения  0 .. 65535.

Тип  longint - длинное целое размером 4 байта со знаком, пределы изменения

- 2 147 483 648 .. 2 147 483 647.

Для типа longint  предопределена константа MaxLongint, равная   - 1  =  2147483647.

Для целых значений допустимы следующие арифметические операции:

              +   сложение;

               -   вычитание;

               *   умножение;

              div деление нацело (целая часть от деления);

              mod  остаток от деления.

Пример 2.

19 div 5 = 3;   19 mod 5 = 4

Очевидно, что   a  mod  b  =  a - (a div b) b

В арифметическом выражении не могут стоять рядом два знака операции. Например, нельзя писать a * -b. Здесь должно быть  a * (-b).

Старшинство операций:

1) выражения в скобках;

2) *, div, mod (операции типа умножения);

3) +, - (операции типа сложения).

Операции одинакового старшинства выполняются слева направо.

Пример 3.

5 + 3 * 7 div 4 - 17 mod 3 =  5 + 5 - 2 =  8

Примечание. На машинном уровне для целочисленных переменных может быть использована лишь одна операция деления, но ее результат записывается в два различных регистра (регистр – это быстродействующая ячейка памяти): в один регистр – частное, во второй – остаток от деления. Тогда в Паскаль-программе содержимое первого регистра воспринимается как результат операции div , а второго – как результат операции mod.

Операции возведения в степень в Паскале нет. Для целых показателей степени эта операция может быть заменена многократным умножением. Более эффективный способ программной реализации операции возведения в степень рассмотрен в разделе «Вычисление степенной функции».

Целый результат дают следующие предописанные функции:

    1) abs(i) - абсолютное значение целого аргумента i;

    2) sqr(i) - квадрат значения целого аргумента i;

    3) trunc(R) - целая часть вещественного значения R;

    4) round(R) - целое значение, ближайшее к вещественному значению R.

Пример 4.

  trunc(3.3) = 3;      round(3.3) = 3;

  trunc(3.5) = 3;      round(3.5) = 4;

  trunc(3.8) = 3;      round(3.8) = 4;

  trunc(-3.3) = -3;    round(-3.3) = -3;

  trunc(-3.8) = -3;    round(-3.8) = -4.

Следовательно,

trunc(x) = sign(x) trunc(abs(x));   round(x) = sign(x) round(abs(x)),

где sign(x) = 1  при  x > 0,   sign(x) = 0  при  x = 0  и  sign(x) = -1  при  x < 0.

Если  i - целое, то    succ(i) = i + 1;  pred(i) = i - 1;  ord(i) = i .

Целочисленным переменным можно присваивать значения не только десятичных, но и шестнадцатеричных констант. Отрицательные значения можно задавать, используя знак "-" или дополнительный код числа. В этом смысле приведенные ниже операторы для переменных  i, k и l  эквивалентны.

i:=-$5D;         i:=$A3;

k:=-$7C0F;       k:=$83F1;

l:=-$2BF01400;   l:=$C40FEC00 .

Целочисленные переменные могут рассматриваться как битовые последовательности. В связи с этим к ним применимы следующие операции:

Shl k (Shift  Left) – сдвиг влево на k разрядов;

Shr k (Shift  Right) – сдвиг вправо на k разрядов;

Not – отрицание;

And – логическое умножение;

Or – логическое сложение;

Xor – исключающее ИЛИ.

При сдвиге влево разряды, выходящие за пределы разрядной сетки, теряются, а справа в число добавляется k нулевых бит. Аналогичная работа выполняется при сдвиге вправо.

Для положительных чисел операции «shl k» и «shr k» эквивалентны соответственно умножению и делению на .

Пример 5

Var  m,n,p : integer;

................

 p:=100;  m:=p shl 2;  n:=p shr 2;

Получим:   m = 400;  n = 25.

В двоичном представлении:

p = 0000 0000 0110 0100;

m = 0000 0001 1001 0000;   n = 0000 0000 0001 1001.

При компиляции программы выражения типа  р* и р div  автоматически заменяются операциями сдвига.

Логические операции  not, and, or, xor выполняются поразрядно (см. пример 5 в разделе «Представление информации в ПЭВМ»).

Рассмотрим теперь возможные способы определенич четности целочисленных переменных.

Вполне очевидно что целочисленная переменная k четная, если

k mod 2 = 0,

и нечетная, если

        k mod 2 = 1.

В то же время несколько выше было указано, что для определения четности может быть использована логическая функция odd(k).

Запишем последовательно в двоичном представлении числа  5, 6, 7, 8, 9:

0101

0110

0111

1000

1001

Из этой записи видно, что четность двоичного числа определяется значением последнего разряда (0 – четное, 1 – нечетное число). Следовательно, функция odd(k) анализирует лишь последний разряд числа. Это эквивалентно вычислению следующего выражения:

odd(k)  k and 1  k and 00000001

Операция and – это логическая операция, mod – арифметическая, которая выполняется на порядок дольше по сравнению с логической операцией. Следовательно, использование функции odd(k) при определении четности повышает быстродействие программы (микрооптимизация программы).

3. Символьный тип данных (тип char). Обозначение char - это сокращение слова  character (символ, 'kæriktə).

Значениями переменных символьного типа являются элементы конечного и упорядоченного множества символов. Это значение обозначается одним символом, заключенным в апострофы.

Пример 6.        'A',  'a',  '8',  '''' (апостроф как символ пишется дважды).

Вне зависимости от реализации, т.е. от конструкции конкретного транслятора, для символьного типа справедливы следующие допущения.

1) Десятичные цифры от '0' до '9' упорядочены в соответствии с их значениями и записаны одна за другой. Следовательно,

succ('5') = '6';  pred('5') = '4' .

2) Имеются все прописные буквы латинского алфавита от 'A' до 'Z'. Это множество упорядочено по алфавиту, но не обязательно связно. Следовательно, в любой реализации должно выполняться 'I' < 'J', но может не выполняться succ('I') = 'J'.

3) Могут быть строчные буквы латинского алфавита от 'a' до 'z'. Если это так, то это множество букв упорядочено по алфавиту, но не обязательно связно.

В таблице ASCII заданы как большие, так и малые латинские буквы, причем их последовательность непрерывная.

Для символьного типа определены две взаимно обратные функции преобразования ord и chr:

k = ord(ch) - порядковый номер символа ch;

ch = chr(k)  - символ с порядковым номером k.

Очевидно, что

chr ( ord ( ch ) ) = ch ;      ord ( chr ( k ) )  = k .

В последнем случае должно быть  k = 0 .. 255 .

Рассмотрим содержательный смысл функций ord и chr на машинном уровне. Как уже отмечалось, внутреннее представление символа в байте памяти - это его порядковый номер по таблице ASCII. Например, для буквы "Ю" имеем  #158, тогда содержимое байта равно  9E16  = 100111102 .

Примечание. «#» - это порядковый номер символа в таблице ASCII.

Если байт, содержащий значение 10011110, используется в программе при выводе результатов как символ, то печатается буква "Ю"; если этот байт используется как число, то печатается значение 158. Следовательно, функции ord и chr никакого преобразования содержимого байта не производят; они лишь разрешают программе по-разному трактовать это содержимое (как символ или как число).

Для символьного типа определены все операции отношения.

Считается, что  ch1 < ch2,  если  ord(ch1) < ord(ch2), где ch1, ch2 - переменные символьного типа.

4. Вещественные типы.

Вещественные числа - это числа с плавающей запятой. Широкое их использование характерно для инженерно-технических задач. Тем не менее процессоры 8086, 8088 и другие, на которых были построены первые ПЭВМ малой производительности, не имеют в своем составе операций над числами с плавающей запятой. Такие операции выполняются программным путем. Это означает, что транслятор включает в программу пользователя подпрограммы арифметических операций над числами с плавающей запятой. Тогда вместо выполнения, например, одной машинной команды сложения производится обращение к подпрограмме и выполняется целая группа машинных команд.

Для повышения производительности компьютера при обработке вещественных чисел к нему добавляют сопроцессор. Это небольшая плата, на которой аппаратно реализованы операции с плавающей запятой. При этом скорость выполнения таких операций увеличивается в несколько раз.

При отсутствии сопроцессора реализуется только один вещественный тип - тип real; при наличии сопроцессора реализуются также типы single, double, extended, comp. Вещественные типы отличаются друг от друга количеством разрядов, отводимых для представления мантиссы и порядка. В частности, переменная типа single имеет длину 4 байта, из них три байта - мантисса и один байт - характеристика; для переменной типа double отводится 8 байт памяти и т.д. В дальнейшем при разработке программ в качестве вещественного типа будет рассматриваться только тип real. Информация о других вещественных типах представлена в разделе «Использование сопроцессора».

Значение числа типа real в десятичном представлении может изменяться в диапазоне от  до . Размер мантиссы такого числа обеспечивает получение 11 – 12 значащих десятичных цифр.

Для вещественных типов определены четыре арифметические операции:

                   +  сложение ;

                   -  вычитание ;

                   *  умножение ;

                   /  деление .

Результатом операций "+", "-", "*"  является вещественное значение, если хотя бы один из операндов имеет вещественный тип. Операция "/" дает вещественное значение и в том случае, когда оба ее операнда относятся к целочисленным типам.

Например,  12 / 5 = 2.4 ,  в то время как  12 div 5 = 2 .

Стандартные функции abs(x) и sqr(x) дают вещественный результат, если их аргумент x имеет тип real. Вне зависимости от типа аргумента следующие стандартные функции  всегда дают  вещественный результат: sin(x), cos(x), ln(x), exp(x), arctan(x), sqrt(x) (корень квадратный).

Вещественный результат при вещественном аргументе дают также функции:

Int(x) - целая часть вещественного значения x;

Frac(x) - дробная часть вещественного значения x  (Frac(x) = x - Int(x)).

Отсутствующие в Турбо Паскале функции arcsin(x), arccos(x), lg(x) могут быть определены следующим образом:

        а)

Запись на Паскале:

If abs(x)=1 then

 arcsin:=pi/2

Else

 arcsin:=arctan(x/sqrt(1-sqr(x)));

б)  

На языке Паскаль:

If x=0 then

 arccos:=pi/2

Else

 arccos:=arctan(sqrt(1-sqr(x))/x)+pi*byte(x<0);

В выражении  byte(x<0)  используется аппарат приведения типов, который будет рассмотрен позже. В данном случае

       byte(x<0) = 1,  если  x < 0;  byte(x<0) = 0,  если  x 0.

в)  lg(x):   ln(x)/ln(10).

Степенная функция для вещественного показателя степени вычисляется следующим образом:

 exp(b * ln(a))

С Т Р У К Т У Р А   П А С К А Л Ь - П Р О Г Р А М М Ы

Паскаль-программа состоит из заголовка и блока:

Блок содержит разделы описаний, в которых определяются все локальные по отношению к данной программе объекты, и раздел операторов, где заданы действия, которые необходимо выполнить над этими объектами.

Приведенная синтаксическая диаграмма наглядно показывает, что в блоке может отсутствовать любой из разделов, кроме раздела операторов.

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

В заголовке программы указывается имя программы:

Пример 1.

 Program  Example;

1. Раздел описания меток.

Любой оператор в программе может быть промаркирован меткой. Метка ставится перед оператором и отделяется от него двоеточием. Все метки должны быть описаны в разделе описания меток.

Пример 2.

 Label  10, 20, Met15;

2. Раздел описания констант.

Этот раздел определяет некоторые идентификаторы как синонимы констант.

Пример 3.

 Const  g = 9.81;

        Nmax = 100;

        TextString = 'Нажмите клавишу Enter';

Константа pi является предописанной и равна    pi = 3.1415926536 .

В разделе описания констант можно использовать выражения, в состав которых входят константы, знаки операций и некоторые функции (abs, odd, ord и др.). Значения таких выражений вычисляются при компиляции программы.

Пример 4.

 Const  pi4 = pi/4;

        MaxSize = MaxInt div SizeOf(real);

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

Предположим, что константа  Nmax = 100  в примере 3 определяет максимальный размер массива. Если в операторах программы в различных местах непосредственно записано значение 100, то при необходимости изменения размера массива нужно везде заменить это значение другим, например, числом 200. При этом не исключено, что где-нибудь в программе будет пропущена такая замена или же, наоборот, изменено число 100, которое не является размером массива. В то же время, если в программе везде записано Nmax, то достаточно изменить лишь одно число в разделе констант.

3. Раздел описания типов.

Типы данных real, integer, boolean, char являются предопределенными и используются в разделе описания переменных. Если программисту требуется ввести новый тип данных, то этот тип нужно описать в разделе описания типов.

Пример 5.

 Type Interval = 10..50;

       Ar = array[1..100] of real;

Здесь  Interval - диапазонный тип, Ar - тип массива с вещественными компонентами.

4. Раздел описания переменных.

Каждое имя переменной (простой, составной или другого типа) должно быть приведено в разделе описания переменных.

Пример 6.

Var  i, j, k : integer;

    a, b : real;

    X : Ar;

    d : Interval;

Примечание. Var – это сокращение слова variable (переменная, 'vεriəbl).

5. Раздел описания процедур и функций. Назначение и структура раздела будут изложены в дальнейшем при рассмотрении процедур и функций.

6. Раздел операторов.

Раздел содержит в себе операторы, реализующие обработку информации в программе. Его синтаксическая диаграмма :

Раздел операторов - это частный случай составного оператора, который включает в себя один или несколько операторов, заключенных в "операторные скобки"  begin .. end.  Разделителем между операторами является точка с запятой.

Операторы Паскаль-программы - это оператор присваивания, условный оператор, оператор перехода и др.

Из синтаксической диаграммы следует, что между словами Begin и End могут отсутствовать какие-либо операторы, может быть одна или несколько точек с запятой. Это возможные примеры пустого оператора, который будет рассмотрен несколько позже.

7. Последовательность разделов.

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

Пример 7.

Const Nmax = 100;

Type Ar = array[1..Nmax] of integer;

Var  X : Ar;

Const  g = 9.81;

Type  Ars = array[0..Nmax+1] of real;

Var  i,n : byte;

     Y,Z : Ars;

При этом должны выполняться два основополагающих правила:

1) ни одно имя не должно быть описано дважды;

2) любое имя может быть использовано лишь после его описания (например, имя Nmax используется в разделе Type после описания этого имени в разделе Const).

Тем не менее рекомендуется без необходимости не изменять порядок следования описаний, принятый в стандартном языке Паскаль: Label, Const, Type, Var, процедуры и функции.

А Л Г О Р И Т М   И   С П О С О Б Ы

Е Г О   П Р Е Д С Т А В Л Е Н И Я

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

Для формализации записи алгоритмов используют различные изобразительные средства. К основным из них относятся формульно-словесный, блок-схемный и запись с помощью алгоритмических языков.

1. Формульно-словесный способ.

Здесь для записи алгоритма используется естественный язык с привлечением, если это необходимо, математических формул и обозначений.

Пример 1. Найти  

       Шаг 1. Положить  равным .

       Шаг 2. Если  , то положить  равным .

       Шаг 3. Если  , то положить  равным . Конец.

Пример 2. Найти наибольший общий делитель двух целых чисел .

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

Например, для  = 420  и   = 90  имеем

420 = 2 2 3 5 7;     90 = 2 3 3 5 .

Наибольший общий делитель в этом случае равен 2 3 5 = 30.

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

Более просто поставленная задача решается с помощью так называемого алгоритма Евклида.

Обозначим наибольший общий делитель через . Вполне очевидно, что  .

Тогда алгоритм Евклида можно описать следующим образом.

       Шаг 1. Если  = 0, то принять  и закончить вычисления, иначе перейти к

                    шагу 2.

       Шаг 2. Вычислить  и  .

       Шаг 3. Заменить значение  на значение , а значение  - на значение . Пе-

рейти к шагу 1.

Здесь q - целая часть от деления m на n;  r - остаток от деления.

При   = 420,  = 90  имеем:

       Шаг 1.  = 90 0;

       Шаг 2.  = [420/90] = 4;   = 420 - 4 90 = 60;

       Шаг 3.  = 90;  = 60;

       Шаг 1.  = 60 0;

       Шаг 2.  = [90/60] = 1;  = 90 – 1 60 = 30;

       Шаг 3.  = 60;  = 30;

       Шаг 1.  = 30 0;

       Шаг 2.  = [60/30] = 2;  = 60 – 2 30 = 0;

       Шаг 3.  = 30;   = 0;

       Шаг 1.  = 0     = 30 .

Основным недостатком формульно-словесной записи алгоритма является то, что здесь используется естественный язык, для которого органически присуща неоднозначность слов. К недостаткам данного способа относят также ненаглядность записи алгоритма.

2. Блок-схемный способ.

Блок-схема - это графическое изображение логической структуры алгоритма. Здесь каждый этап вычислительного процесса изображается в виде определенной геометрической фигуры, называемой блоком. Основными блоками являются:

                                                 -  начало или конец алгоритма  (овал);

   

                                        - ввод или вывод данных  (параллелепипед);

                                        - обработка данных, т.е. вычислительный

                                        процесс  (прямоугольник);

             

                                        - логический блок  (ромб).

      

Связь между блоками изображают горизонтальными или вертикальными линиями. Излом линии связи делается только под прямым углом. При направлении линии связи слева направо или сверху вниз стрелку на линии можно не ставить; при направлении справа налево, снизу вверх или при наличии излома стрелка обязательна.

3. Алгоритмические языки применяются для записи алгоритмов в виде программ, предназначенных для реализации на ЭВМ.

О П Е Р А Т О Р   П Р И С В А И В А Н И Я

В блок-схемах обычная математическая запись , означающая, что переменная  принимает значение выражения , представляется в виде  (читается " становится равным " ). При выполнении этого оператора переменная  теряет свое предыдущее значение и получает новое значение.

Каждое имя в программе (в том числе в блок-схеме) – это косвенное обозначение адреса поля памяти. Следовательно, при выполнении оператора  в оперативной памяти будут использованы поля с именами

Выполнение оператора присваивания производится в четкой последовательности во времени:

1) из памяти в процессор читаются значения переменных  (содержимое полей памяти с адресами );

2) в процессоре вычисляется значение выражения  ;

  1.  результат вычислений записывается в память в поле с именем .

               y                                a                                  b                              x

                                                                                                                       Память

В обычном математическом представлении знак "=" имеет два смысла. Запись  можно интерпретировать следующим образом:

1) вычислить значение выражения  и присвоить его переменной ;

2) проверить, равно ли значение переменной  значению выражения .

Поскольку в программе двусмысленность недопустима, то в первом случае используется символ ":=" (операция присваивания), а во втором случае - символ "=" (операция отношения).

Использование операции присваивания позволяет записывать такие конструкции, которые в обычной математической интерпретации бессмысленны. Например, . Это означает, что к текущему значению переменной  добавляется 1, после чего полученное значение присваивается той же переменной . Следовательно, значение переменной  увеличивается на единицу.

Пример 1. Обменять значения переменных  и .

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

                                                                     

                                                                     2  

                                1                                                                         3

Числа  1, 2, 3  указывают последовательность пересылок содержимого полей памяти ,  и .

Представленную выше схему можно реализовать такими операторами:

;  ;   .

Подчеркнем еще раз смысл оператора присваивания. Если в правой части оператора записана переменная, например,

,

то это означает, что содержимое поля памяти с именем (адресом)  пересылается в поле . Разумеется, такая пересылка осуществляется через процессор.

Если в правой части оператора присваивания стоит выражение, например,

,

то вначале в процессор читаются значения операндов (содержимое поля , содержимое поля  и содержимое поля памяти, в котором записана константа 1), вычисляется значение выражения, а полученный результат записывается в поле памяти, имя которого указано в левой части оператора присваивания (в данном случае в поле ).

В Паскале оператор присваивания имеет такое же обозначение, как и на блок-схеме. Его синтаксическая диаграмма:

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

Выражения могут быть арифметические, логические и строковые.

Старшинство операций:

      1) not;                              ( отрицание )

      2) *, /, div, mod, and; ( операции типа умножения )

      3) +, -, or, xor;  ( операции типа сложения )

      4) =, <>, <, <=, >, >=; ( операции отношения )

      5) in.   (операция принадлежности )

Примечание. Операция in будет определена при рассмотрении множеств.

Пример 2. 

(25 – 3*3)/(3 + 1) * 7 – 4 + 2*3 =  (25 – 9)/4 * 7 – 4 + 6 = 16/4 * 7 + 2 =

4.0 * 7 + 2 = 28.0 + 2 = 30.0

Некоторую особенность имеет вычисление логических операций and и or, для которых в Турбо Паскале применяется так называемая короткая схема вычислений. По этой схеме вначале вычисляется первый операнд. Если для операции and получено при этом значение false, то второй операнд не вычисляется, так как результат операции все равно будет иметь значение false. Аналогично, если первый операнд операции or имеет значение true, то второй операнд не вычисляется. Например, в выражении  (x<0) and (y>x-1) при  x = 1 будет вычислен лишь первый операнд и получено общее значение false вне зависимости от значения, которое имеет переменная y.

Оператор присваивания выполним, если переменная и выражение совместимы по присваиванию. Основные варианты совместимости по присваиванию:

1) переменная и выражение относятся к одному типу.

2) переменная относится к типу real, а значение выражения - к одному из целых типов.

Пример 3.

Var  a,b : real;

    m,n : integer;

Begin

 a:=55.85; m:=15;

 b:=m; n:=a;

В операторе  n := a  не выполняется требование совместимости по присваиванию. Если допустить такой оператор, то имела бы место потеря точности, так как переменной n можно присвоить лишь целую часть значения переменной a (при присваивании  b:=m  потери точности не происходит). Если в программе действительно нужно присвоить переменной n значение переменной a, то это можно сделать следующим образом:

n := Trunc(a)    или    n := Round(a) .

Другие варианты совместимости по присваиванию будут рассмотрены позже.

У С Л О В Н Ы Й   О П Е Р А Т О Р

С помощью условного оператора реализуются разветвляющиеся вычислительные процессы.

Разветвляющимися процессами называются такие, в которых в зависимости от значения некоторого признака обработка данных производится по одному из возможных направлений (по одной из ветвей).

В условном операторе в зависимости от выполнения проверяемого условия вычисление производится по одной из двух возможных ветвей (если количество ветвей вычислительного процесса свыше двух, то в этом случае обычно используется оператор Case, который в дальнейшем рассматривается в разделе «Оператор варианта»).

Пример 1.

Это сигнатура, функция знака  y = sign(x). График функции:

                                   y

                                  1

                                   0                                   x

                                         -1

Блок-схема вычисления функции знака:

                            да                                  нет

                             

                                                           да                                  нет

                                                                                            

Здесь производятся две проверки. В первой из них () определяется первая ветвь вычислений, но остаются неопределенными вторая и третья ветви в формуле вычисления функции знака. Разделение этих ветвей выполняет вторая проверка (). В последнем случае знак «=» - это операция отношения, но не присваивание переменной  нулевого значения.

Синтаксическая диаграмма условного оператора:

Выражение между if и then должно иметь тип boolean, т.е. это логическое выражение. Если значение выражения равно true, то выполняется оператор 1; в противном случае - оператор 2 или не выполняется никаких действий (при отсутствии альтернативы else).

Ранее  указывалось, что операторы в Паскаль-программе разделяются между собой точкой с запятой. В связи с этим внутри любого структурного оператора, в том числе оператора if, символ ";" не должен встречаться, иначе все, что стоит после него, будет считаться уже другим оператором.

Пример 2.

If x>0 then

 y:=sqrt(x);

Else

 y:=sqr(x);

В этом примере точка с запятой, стоящая перед словом else, заканчивает текст условного оператора. А это приводит к ошибке, поскольку оператора, начинающегося с зарезервированного слова else, в Паскале нет.

Следовательно, перед словом else в условном операторе не должно быть точки с запятой, в противном случае это рассматривается как ошибка.

Паскаль-программа для функции знака:

Program Sign1;

Var  x : integer;

    y : shortint;

Begin

 Ввод x

 If x>0 then

   y:=1

 Else

   If x<0 then

     y:=-1

   Else

     y:=0;

 Печать  y

End.

Если x - вещественная переменная, то сравнение в большинстве случаев нужно выполнять по , где - достаточно малое число (погрешность задания исходных данных). Как уже ранее указывалось, вещественные числа, как правило, определяют результаты измерений, производимых с определенной точностью. В этом случае значение  с точки зрения математики является положительным числом. Если же переменная  означает вес груза, то значение  кг должно считаться эквивалентным нулю, если точность измерения веса  кг.

На числовой оси вещественный нуль - это -окрестность точки 0. Другими словами, если x  , то  x  считается равным нулю.

              x < 0                                   x  = 0                                  x > 0

                                           -               0                                                             x

Следовательно, для вещественных переменных

  

Аналогично,  и т.п.

Реализация функции знака для вещественного аргумента:

Program Sign2;

Const  eps = 0.000001;

Var x : real;

    y : shortint;

Begin

 Ввод x

 If x>eps then

   y:=1

 Else

   If x<-eps then

     y:=-1

   Else

     y:=0;

 Печать  y

End.

В реальных программах вычисление sign(x) оформляется в виде функции, обращение к которой аналогично, например, обращению к функции sin(x).

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

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

Поскольку областью определения корня нечетной степени является вся числовая ось, то в программе должны анализироваться альтернативные варианты  x < 0, x = 0 и x > 0. Учитывая, что ln(x), используемый при вычислении степенной функции, определен лишь при x > 0, рассматриваемую задачу можно реализовать следующим образом:

 If abs(x)<eps then

   y:=0

 Else

   y:=sign(x)*exp(ln(abs(x))/3);

То же, но без использования функции знака:

 If abs(x)<eps then

   y:=0

 Else

   Begin

     y:=exp(ln(abs(x))/3);

     If x<0 then

       y:=-y;

   End;

Здесь рекомендуется установить  eps <= 1E-30  ( при  x =  и  n = 3  получим   ).

Пример 3.

;  сравнить   и  .

Var  a,x,y : real;

    b : boolean;

Begin

 a:=5; x:=0.88;

 y:=a*sin(x)/sqrt(1-sqr(cos(x));

 b:=y=a;

Значения  и   равны лишь при точных вычислениях. При вычислении на ЭВМ мы имеем дело с приближенными значениями вещественных переменных.

Здесь мы получим  b = false, так как  y - a = 1.5 10-11    (точное значение  y - a = 0).

Правильная реализация:

Const eps = 0.000001;

Var  a,x,y : real;

    b : boolean;

Begin

 a:=5; x:=0.88;

 y:=a*sin(x)/sqrt(1-sqr(cos(x));

 b:=abs(y-a)<eps;

Здесь имеем  b = true.

Пример 4.  .

Блок-схема для примера 4.

                               да          нет

    да          нет

Ниже приведена программная реализация для примера 4.

Program Max;

Var y,a,b,c : real;

Begin

 Ввод a,b,c

 y:=a;

 If b>y then 

   y:=b;

 If c>y then

   y:=c;

 Печать  y

End.

Примечание. Здесь, в отличие от предыдущих примеров, нет необходимости проверять отношения между значениями переменных с учетом погрешности .

Операторы 1 и 2, изображенные на синтаксической диаграмме условного оператора, в свою очередь могут быть условными операторами.

Пример 5.

Многоступенчатая схема вложенности условных операторов:

                  false                       true

                                                

                                                 false                      true

                              false                    true                false                    true

Здесь B1,B2,B3,B4 – некоторые логические выражения, принимающие значения true или false; S1,S2,S3,S4,S5 – операторы (например, операторы присваивания).

If B1  then

 If  B2  then

   If B3  then

     S1

   Else

     S2

 Else

   If B4  then

     S3

   Else

     S4

Else

 S5 .

В программе каждое слово "else" в условном операторе относится к ближайшему слову "if", не связанному с каким-либо словом "else".

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

Пример 6. Сгруппировать переменные   в порядке возрастания.

Содержание алгоритма:

  1) Если , то обменять значения   и   ( ).

  2) Если  , то     .

  3) Если  , то     .

Program Group;

Var  a,b,c,R : real;

Begin

 Ввод  a,b,c

 If a>b then

   Begin

     R:=a; a:=b; b:=R;

   End;

 If a>c then

   Begin

     R:=a; a:=c; c:=R;

   End;

 If b>c then

   Begin

     R:=b; b:=c; c:=R;

   End;

 Печать  a,b,c

End.

П Р О Ц Е Д У Р Ы   В В О Д А - В Ы В О Д А

Для ввода-вывода данных в Паскаль-программе применяются предописанные процедуры ввода-вывода Read, Readln, Write, Writeln (Readln - сокращение слов Read Line - читать с новой строки; Write Line - записать строку). Каждая из этих процедур представляет собой некоторую подпрограмму, которая активизируется оператором процедуры. Процедуры ввода-вывода часто называют просто операторами ввода-вывода.

Мы будем рассматривать только ввод с клавиатуры и вывод на экран дисплея. Процедуры ввода-вывода для произвольных файлов будут рассмотрены позже.

Основной процедурой ввода является Read. С ее помощью можно прочитать любое количество исходных данных, указав в скобках идентификаторы переменных, которым нужно присвоить конкретные значения.

Структура обращения к процедуре ввода:

Read( список-ввода ),

где список-ввода - это имена переменных, разделенные между собой запятыми.

Элементами списка ввода являются простые переменные, т.е. это не могут быть массивы, константы, функции или выражения.

Если в программе встречается процедура Read(x), то решение прерывается, и программа ожидает от пользователя набора на клавиатуре значения переменной x и нажатия клавиши Enter.

При наборе с клавиатуры вводимые данные накапливаются в буфере ввода, который представляет собой специально выделенную для этого область памяти. Максимальная длина буфера ввода при работе с клавиатурой составляет 127 байтов. Одновременно набираемые данные высвечиваются на экране дисплея. После нажатия клавиши Enter происходит чтение данных из буфера ввода и присваивание их значений переменным, имена которых записаны в списке ввода. Числа, набираемые с клавиатуры в буфер ввода, должны представлять собой правильные константы и отделяться друг от друга одним или несколькими пробелами.

Пусть в буфер ввода набраны следующие данные:

15.8   -4   73.7   12.9

и после этого нажата клавиша Enter.

Тогда процедура Read(a,b,c,d), где a, b, c, d - переменные типа real, прочтет четыре числа из буфера ввода и присвоит их значения этим переменным.

Написанная выше процедура ввода эквивалентна следующим:

Read(a); Read(b); Read(c); Read(d).

При отработке процедуры Read каждое введенное значение удаляется из буфера.

Предположим, что в буфере ввода после нажатия клавиши Enter содержатся числа

15.8   -4   73.7

Поскольку процедура Read(a,b,c,d) эквивалентна четырем процедурам

Read(a); Read(b); Read(c); Read(d),

то будут выполнены только первые три из них. Программа снова приостанавливает свою работу в ожидании нового заполнения буфера ввода и нажатия клавиши  Enter.

Пусть в аналогичной ситуации буфер ввода имеет вид:

15.8   -4   73.7   12.9   -16   65.87

Тогда из буфера будут введены первые 4 числа и программа продолжит свою работу. Если дальше в программе будут встречаться процедуры Read, то оставшиеся в буфере два числа будут введены этими процедурами.

Рассмотренный пример можно проиллюстрировать следующей схемой:

Буфер ввода

1

5

.

8

-

4

7

3

.

7

1

2

.

9

-

1

6

6

5

.

8

7

Каждое из полей  a, b, c, d  имеет размер 6 байтов ( a,b,c,d : real). При передаче процедурой Read значения переменной из буфера ввода в поле памяти производится преобразование формы ее представления (из литерного формата, в котором каждый символ занимает один байт, в формат real).

Процедура Readln отличается от Read тем, что после ее выполнения производится переход на новое содержимое буфера ввода. При этом текущее содержимое буфера стирается.

Пусть буфер ввода имеет вид:

15.8   -4   73.7   12.9   -16   65.87

При выполнении процедур

Readln(a,b); Readln(c,d);

из буфера будут введены первые два числа, содержимое буфера стирается, а работа программы приостанавливается в ожидании нового заполнения буфера.

Процедура Readln без параметров производит только сброс буфера ввода и переход к его новому содержимому. В частности, Readln(a,b,c,d) эквивалентна следующим процедурам:

Read(a); Read(b); Read(c); Read(d); Readln.

Для вывода данных используются процедуры Write и Writeln. Процедура Writeln отличается от процедуры Write тем, что после ее выполнения происходит переход на новую строку экрана.

Элементами списка вывода, в отличие от списка ввода, являются выражения. Частным случаем выражения являются константа, имя переменной, функция. В свою очередь частным случаем константы является текстовая константа, т.е. последовательность символов, заключенная в апострофы.

Пусть заданы две процедуры:

Write('  Переменная x = '); Write('  Переменная y = ');

При их выполнении на экране будет отпечатано:

 Переменная x =   Переменная y =

Для процедур

Writeln('  Переменная x = '); Writeln('  Переменная y =');

получим:

 Переменная x =

 Переменная y =

Пример вывода выражений:

 Write(a+b,'  ',5,'  ',2*sin(x)-1).

Выводом можно управлять с помощью форматов. Элемент списка вывода может быть записан в форме

                      v

             или   v:w

             или   v:w:d .

Здесь  v - выражение, w и d - форматы.

Если указано только выражение v, то количество позиций в строке экрана, отводимое для печати значения v, зависит от типа этого значения.

Для целых переменных отводится столько позиций, сколько цифр содержит значение переменной. Для знака "-"  отводится одна позиция перед первой цифрой числа. Для знака "+" никакой позиции не отводится.

Пример 1.

Var i : integer;

    l : longint;

Begin

 i:=555; l:=123456789;

 Write('i=',i); Writeln('   l=',l);

 i:=-555; l:=-123456789;

 Write('i=',i); Writeln('  l=',l);

Будет отпечатано:

i=555   l=123456789

i=-555  l=-123456789

Для вещественного значения отводится 17 позиций. Число печатается в виде мантиссы и порядка. Длина мантиссы - 11 цифр, из них одна цифра целой части числа.

Пример 2.

Var  R : real;

Begin

 R:=555; Write('R=',R);

 R:=-555; Writeln('   R=',R);

Будет отпечатано:

R= 5.5500000000E+02   R=-5.5500000000E+02

Здесь  E -признак десятичного порядка.

Формат w указывает ширину поля (количество позиций, отводимых для изображения значения v).

Пусть  n - количество цифр целой переменной, включая знак "-", если он имеется.

Если  w > n, то значение v печатается в правой части поля w; если  w < n, то для печати значения v отводится n позиций (отсекание "лишних" цифр не производится).

Пример 3.

Var  i : integer;

    l : longint;

Begin

 i:=5555; l:=5555;

 Write('i=',i:8); Writeln('   l=',l:2);

 i:=-5555; l:=-5555;

 Write('i=',i:8); Writeln('   l=',l:2);

Будет отпечатано:

i=    5555   l=5555

i=   -5555   l=-5555

Если для вещественной переменной задано значение w > 17, то слева от переменной печатается соответствующее количество пробелов; если w < 17, то соответственно уменьшается количество цифр мантиссы. Минимальное количество цифр мантиссы равно 2, в этом случае значение w < 8 воспринимается как  w = 8.  Значение мантиссы при печати округляется по последней печатаемой цифре.

Пример 4.

Var R : real;

Begin

 R:=555;

 Write('R=',R:20); Writeln('   R=',R:17);

 Write('R=',R:12); Writeln('   R=',R:8);

 Writeln('R=',R:5);

Будет отпечатано:

R=    5.5500000000E+02   R= 5.5500000000E+02

R= 5.55000E+02   R= 5.6E+02

R= 5.6E+02

Формат d применяется только для вещественных значений и означает, что число должно печататься в виде целой и дробной частей, разделенных точкой; при этом значение d указывает, сколько позиций из общего количества w должно быть отведено  для печати дробной части числа. Если  d = 0, то печатается только целая часть без разделяющей точки. Если поля w недостаточно для печати всех цифр числа, то к значению w добавляется необходимое количество позиций.

Пример 5.

Var R : real;

Begin

 R:=123.456789;

 Write('R=',R:12:5); Writeln('   R=',R:8:5);

 Write('R=',R:8:1); Writeln('   R=',R:8:0);

 Writeln('R=',R:8:10);

 R:=-123.456789;

 Write('R=',R:12:5); Writeln('   R=',R:8:5);

 Write('R=',R:8:1); Writeln('   R=',R:8:0);

 Writeln('R=',R:8:10);

Будет отпечатано:

R=   123.45679   R=123.45679

R=    123.5   R=      123

R=123.4567890000   

R=  -123.45679   R=-123.45679   

R=   -123.5   R=     -123   

R=-123.4567890000

Если  w < 0, то значения числовых переменных, а также переменных типов char, string и boolean записываются в левые позиции поля w. При этом, вне зависимости от значения w, для переменной отводится лишь столько позиций, сколько символов содержит ее значение. Если вещественная переменная записывается в виде мантиссы и порядка, то мантисса округляется до двух цифр.

Пример 6.

Var   k : integer;  R : real;

     b : boolean;

Begin

 k:=125; r:=-32.63; b:=true;

 Writeln(k:10,’ 1’);

 Writeln(k:-10,’ 1’);

 Writeln(k:-1,’ 1’);

 Writeln(r:18:4,’ 1’);

 Writeln(r:-18:4,’ 1’);

 Writeln(r:-1:4,’ 1’);

 Writeln(r:28,’ 1’);

 Writeln(r:-0,’ 1’);

 Writeln(‘0123456789’:20,’ 1’);

 Writeln(‘0123456789’:-20,’ 1’);

 Writeln(b:20,’ 1’);

 Writeln(b:-1,’ 1’);

Будет напечатано:

      125 1

125 1

125 1

           -32.6300 1

-32.6300 1

-32.6300 1

          -3.2630000000E+01 1

-3.3E+01 1

         0123456789 1

0123456789 1

               TRUE 1

TRUE 1

Примечание. В список ввода могут входить лишь вещественные и целочисленные переменные, а также переменные типа char и string. Следовательно, переменные других типов (массивы, множества и др.) не могут быть непосредственно введены процедурой Read. Косвенная методика ввода таких переменных рассматривается дальше в соответствующих разделах.

В список вывода, кроме типов, перечисленных в списке ввода, могут входить также переменные типа boolean.

О П Е Р А Т О Р   П Е Р Е Х О Д А

Синтаксическая диаграмма:

      

Оператор перехода указывает, что дальнейшая работа должна продолжаться в другой части программы, а именно с того места, где находится метка.

Переход внутрь сложного оператора запрещен (такими операторами являются составной и условный операторы, операторы цикла, варианта, присоединения).

Пример. Алгоритм Евклида.

Program Evklid1;

Label 10;

Var m,n,d,q,r : word;

Begin

 Read(m,n); Writeln('m = ',m,'  n = ',n);

 10:

 If n>0 then

   Begin

     q:=m div n; r:=m mod n;

     m:=n; n:=r;

     Goto 10

   End

 d:=m; Writeln('d=',d)

End.

Примечание. Оператор  q := m  div  n  является избыточным, поскольку вычисленное значение q в программе Evklid1 не используется.

Рассмотрим вкратце, что собой представляет метка в программе.

При трансляции Паскаль-программы каждый ее оператор преобразуется в одну или несколько машинных команд. Эти команды размещаются в последовательных полях памяти. Каждая команда имеет конкретный адрес, определяемый адресом крайнего левого байта поля памяти, которое занимает команда. Команды, как правило, выполняются последовательно, в порядке их записи в памяти. Однако если встречается команда перехода, которой соответствует оператор Goto в Паскаль-программе, то дальше будет выполняться не следующая по порядку машинная команда, а команда, адрес которой указан в команде перехода. Этот адрес перехода и соответствует метке в Паскаль-программе.

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

Ниже перечисляются случаи, когда целесообразно применять оператор Goto:

  •  выход из блока в вызывающую программу (переход с помощью оператора Goto на конец блока);
  •  переход на конец тела цикла;
  •  принудительный выход из цикла;
  •  переход к удаленному фрагменту программы.

Однако для того, чтобы явно не применять оператор Goto, в Турбо Паскале для первых трех случаев предусмотрены специальные операторы, а именно Exit, Continue и Break.

Примечание. Операторы Continue и Break введены в версии 7.0 языка Турбо Паскаль.

В частности, алгоритм Евклида легко реализовать без операторов Goto с помощью операторов цикла While или Repeat.

П У С Т О Й   О П Е Р А Т О Р

В Паскаль-программе точка с запятой используется как разделитель между операторами, т.е. она не входит в состав оператора. Поэтому если в программе поставить подряд две точки с запятой, то будет считаться, что между ними находится пустой оператор. Тот же эффект мы будем иметь, если перед словом end поставить точку с запятой, если установить точку с запятой непосредственно после слова else, если нет ни одного оператора между альтернативами then и else и др. Пустой оператор не выполняет никаких действий, но может иметь метку. Эта метка может быть использована, например, для перехода с помощью оператора Goto на конец составного оператора  beginend.

Пример.

Program Empty;

Label 10,20;

Var  x,y,z : real;

Begin

 x:=-15.3; ;

 y:=exp(x)-1;

 10: ;

 z:=x+y;

 20:

End.

Здесь три пустых оператора.

О П Е Р А Т О Р   Ц И К Л А   С   П Р Е Д У С Л О В И Е М

Циклом называется многократно выполняемый участок программы.

Обычно для организации циклических программ используют операторы цикла. В Паскале определены три таких оператора: оператор цикла с предусловием, оператор цикла с постусловием и оператор цикла с параметром.

Синтаксическая диаграмма оператора цикла с предусловием:

Схема выполнения оператора While:

Значение выражения должно быть типа boolean. Оператор после слова do выполняется нуль или более раз. Чтобы цикл не был бесконечным (случай "зацикливания"), значение выражения в операторе должно изменяться таким образом, чтобы оно стало равным false.

Пример 1. Зацикливание программы.

 x:=10;

 While x>0 do

   Begin

     y:=x+1; z:=y-2;

   End;

Пример 2. Алгоритм Евклида.

Program Evklid2;

Var  m,n,d,r : word;

Begin

 Read(m,n); Writeln('m = ',m,'  n = ',n);

 If m<n then

   Begin                    { Обмен значений m и n, }

     r:=m; m:=n; n:=r;      { если  m < n           }

   End;

 While n>0 do

   Begin

     r:=m mod n; m:=n; n:=r;

   End;

 d:=m; Writeln('d = ',d)

End.

Если в данном примере введено  m = 10,  n = 0,  то цикл  While ни разу не выполняется и в результате получаем  d = 10.

Пример 3. Модификация алгоритма Евклида.

Program Evklid2а;

Var  m,n,d : word;

Begin

 Read(m,n); Writeln('m = ',m,'  n = ',n);

 While (m>0) and (n>0) do

   If m>n then

     m:=m mod n

   Else

     n:=n mod m;

 d:=m+n; Writeln('d = ',d)

End.

Программы Evklid2 (пример 2) и Evklid2a (пример 3) можно считать сопоставимыми по быстродействию. Компьютерный эксперимент показывает, что программа Evklid2a работает примерно на 9% быстрее по сравнению с программой Evklid2. Это достигается за счет того, что здесь, в отличие от программы Evklid2, в каждом цикле не выполняются операторы  m := n   и   n := r.

Работа цикла While в примере 3 заканчивается, когда переменная m или переменная n становятся равными нулю.

Оператор цикла с предусловием наиболее часто применяют при реализации итерационных процессов.

П Р О Г Р А М М И Р О В А Н И Е   И Т Е Р А Ц И О Н Н Ы Х   Ц И К Л О В

Итерационным называется такой вычислительный процесс, для которого заранее неизвестно количество выполнений цикла; это количество зависит от конкретных значений исходных данных. Критерием окончания циклического процесса является выполнение некоторого заранее заданного условия.

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

Как правило, признаком окончания вычислений является достижение заданной точности:

где     - максимально допустимая погрешность вычисления функции ;

 - предыдущее и очередное значения функции  .

Пример 1. Итерационная формула Ньютона для функции   :

,

где    - начальное приближение.  В частности, для    получим:

.

Рассмотрим последовательность вычисления функции   для   = 16; = 0.01 .

           = 16 ;

           = 0.5 * (16 + 16/16) = 8.5 ;

           = 0.5 * (8.5 + 16/8.5) = 5.191176 ;

           = 0.5 * (5.191176 + 16/5.191176) = 4.136664 ;

           = 0.5 * (4.136664 + 16/4.136664) = 4.002257 ;

           = 0.5 * (4.002257 + 16/4.002257) = 4.000005 .

Условие    выполняется при n = 4. Следовательно, значение  = 4.000005  есть значение искомой функции  при  = 16 с погрешностью не более   = 0.01 .

          а)                                                                         б)

       а)                                                                             б)

В блок-схеме варианта а) нельзя применить оператор цикла с предусловием, так как в операторе While проверка условия выполнения цикла должна производиться в самом начале цикла. В связи с этим внесены изменения в схему реализации итерационной формулы Ньютона (вариант б)).

Следует отметить, что здесь переменные  и  не являются элементами массива, это два последовательных во времени значения функции  (предыдущее и текущее значения).

В программе вместо исходных переменных    будут использованы переменные  y, yn.

Program Newton1;

Const eps = 0.00001;

Var x,y,yn : real;

Begin

 Ввод и печать x

 y:=x; yn:=x+2*eps;

 If x>0 then

   While abs(y-yn)>eps do

     Begin

       yn:=y;

       y:=0.5*(yn+x/yn);

     End;

 Печать  y

End.

Оператор "If x > 0  then" в программе Newton1 исключает ее прерывание из-за деления на нуль при  x = 0 (если x = 0, то цикл While не выполняется и результатом является значение y = 0).

Пример 2. Вычисление функции по ее разложению в ряд.

В Паскале, как и в других языках программирования высокого уровня, определены стандартные функции sin(x), cos(x) и др. Вполне очевидно, что вычисление этих функций производится по некоторым формулам, а не по таблицам значений. Одним из методов вычисления функций является разложение их в ряд, в частности, в так называемый ряд Маклорена (существуют и другие ряды, например, ряд Лорана, ряд Фурье, разложение по полиномам Чебышева и пр.). Программная реализация таких рядов сводится к организации итерационных циклов.

Рассмотрим методику вычисления по формулам разложения в ряд на примере функции  .

                                                   ( 1 )

Как известно, n! = 1 2 3 ... n . Например, 5! = 1 2 3 4 5 = 120.

В частности, считают    1! = 1;   0! = 1 .

Тогда  

Является очевидным, что при малых значениях аргумента  (например, < 0.1), значение функции  примерно равно первому члену ее разложения в ряд, т.е. значению самого аргумента  (в связи с этим в учебнике тригонометрии записывается   при малых значениях переменной  х).

Пусть   .  Тогда

0.5-0.020833+0.0002604-

                                                                                  - 0.00000155+...

При   = 0.001  достаточно взять первые три члена разложения функции sin(x)  в ряд. Тогда  y = sin(0.5) = 0.4794 .

Как видно из примера, элементы разложения функции  очень быстро убывают по абсолютному значению, стремясь в бесконечности к нулю.

Обозначим члены ряда через  . Тогда общий член ряда

                                                                                                         ( 2 )

Частная сумма, определяющая с некоторой погрешностью значение искомой функции , есть

                                             

                                             

                                             

                                              …………………………..

                                                                                                               ( 3 )

Условие окончания вычислений (окончания итерационного цикла):

или, используя (3),

.

Каждый очередной член ряда (1) можно вычислять непосредственно по формуле (2), задав соответствующее значение . Однако при этом значительная часть вычислений будет дублировать друг друга.

Например, при вычислении  нужно выполнить действия

,

для вычисления   - действия

.

Однако при вычислении  уже были получены значения  и 5!  Поэтому при вычислении  целесообразно использовать уже имеющееся значение :

Выведем формулу для общего случая вычисления  .

Из формулы (2), подставив вместо  значение , получим

.

Тогда

                                            ( 4 )

Здесь используется равенство

Формула типа (4), в которой для вычисления очередного члена  числовой последовательности  используется значение ее предыдущего члена , называется рекуррентной. Слово «рекуррентный» оначает «возвращающийся». В данном случае это элемент последовательности, который определяется через предыдущие и для его вычисления нужно возвращаться к ним.

Например, для арифметической прогрессии мы имеем

;

аналогично для геометрической прогрессии

Для вычисления ряда (1) имеем следующие рабочие формулы:

;   

Program MacLoren;

Const  eps = 0.00001;

Var  x,a,S,n  :  real;

Begin

 Read(x); Writeln(‘x = ’,x:8:3);

 a:=x; S:=x; n:=0;

 While abs(a)>eps do

   Begin

     a:=-sqr(x)*a/((2*n+2)*(2*n+3));

     S:=S+a; n:=n+1

   End;

 Writeln(‘S,n = ’,S:8:3,’   ‘,n:3:0);

End.

Значение целочисленной переменной  непосредственно входит в формулу для вычисления элемента . Чтобы исключить многочисленные преобразования integer real, в программе переменная  объявлена вещественной. Тогда в процедуре Writeln формат  n:3:0 указывает, что значение переменной n следует печатать без дробной части и точки, разделяющей ее целую и дробную части.

Блок-схема вычислений:

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

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

Вычислим  и . Поскольку на заданном интервале расположен лишь один корень функции, то .

Разделим исходный интервал пополам и определим

 и  .

Если , то передвинем левую границу к середине интервала, т.е. примем    и  . В противном случае к середине интервала сдвигаем правую границу:   . Деление интервала пополам будем продолжать до тех пор, пока не будет выполнено условие  .

Определение корня функции методом половинного деления реализовано ниже в программе Root на примере функции

на интервале   .

Program Root;

Const  eps = 0.001;

Var n : byte;

    a,b,c,Fa,Fb,Fc : real;

Begin

 a:=0.5*pi; b:=1.5*pi;

 Fa:=0.25*sqrt(a)+sin(a); Fb:=0.25*sqrt(b)+sin(b);

 n:=0;

 While (b-a)>eps do

   Begin

     n:=n+1;

     c:=0.5*(a+b); Fc:=0.25*sqrt(c)+sin(c);

     If Fa*Fc>0 then     { если Fa*Fc>0, то переменные }

       Begin             { Fa и Fc имеют одинаковый }

         a:=c; Fa:=Fc    { знак }

       End

     Else

       Begin

         b:=c; Fb:=Fc

       End

   End;

 c:=0.5*(a+b); Fc:=0.25*sqrt(c)+sin(c);

 Writeln('n = ',n,'  c = ',c:9:6,'  Fc = ',Fc:12);

End.

Результаты вычислений по программе Root приведены ниже в таблице.

0.01      

0.001      

0.0001      

0.00001      

0.000001      

 9

12

15

19

22

3.641670

3.638986

3.638746

3.638702

3.638699

-2.41473E-03

-2.33136E-04

-3.81965E-05

-1.63049E-06

-1.07303E-07

Примечание. Рассмотренный ранее алгоритм Евклида  также является итерационным. Здесь количество циклов заранее неизвестно и определяется значениями исходных данных  и . Условием окончания цикла является выполнение отношения  .

В Ы Ч И С Л Е Н И Е   С Т Е П Е Н Н О Й   Ф У Н К Ц И И

Рассмотрим вычисление функции  , где  - целое неотрицательное значение (переменная  может быть вещественного или целого типа).

Если , то вычисляется , после чего . При  имеем .

Пусть . Для получения значения  в виде

требуется выполнить 164 умножения. Однако возможна и другая схема вычислений:

               1-я степень

       2-я степень

         4-я степень

        8-я степень

      16-я степень

      32-я степень

      64-я степень

    128-я степень

=

Здесь требуется только 10 умножений.

В приведенной схеме используется разложение показателя  по степеням числа 2:

165 = 128 + 32 + 4 + 1 .

Обозначим через  текущее значение показателя степени (исходное значение  нельзя изменять в процессе вычисления функции ), через  - сомножитель, включаемый в состав произведения .

Начальные значения переменных  

Описание алгоритма:

  1.  если  нечетное, то значение  включается в состав произведения , после чего показатель степени  уменьшается на 1;
  2.  если  четное, то значение  возводится в квадрат, после чего показатель степени  уменьшается в 2 раза;
  3.  работа алгоритма продолжается до тех пор, пока  

Проиллюстрируем работу алгоритма при рассмотренном выше значении  

1)  нечетное   

2)  четное   

3)  четное   

4)  нечетное   

5)  четное   

6)  четное   

7)  четное   

8)  нечетное   

9)  четное   

10)  четное   

11)  нечетное   

Примечание. Деление на 2 осуществляется путем сдвига целочисленной двоичной переменной на один разряд вправо. Следовательно, здесь мы имеем 11 умножений, 7 операций сдвига и 3 вычитания, на что затрачивается значительно меньше машинного времени по сравнению с многократным умножением (164 операции умножения)..

Блок-схема программы:

Легко убедиться, что эта программа дает правильный результат при любом .

Program Exponent;

Var m,n : word;

    x,y,b : real;

Begin

 Read(x,n);

 y:=1; b:=x; m:=n;

 While m>0 do

   Begin

     While not odd(m) do

       Begin

         m:=m div 2; b:=sqr(b);

       End;

     m:=m-1; y:=y*b;

   End;

 Writeln('y = ',y:12);

End.

Комментарии к программе.

1. Проверка четности целой переменной m возможна двумя способами:

   a) с помощью функции odd(m);

   б) путем вычисления логического выражения   m mod 2 = 1.

Первый способ более эффективен по скорости выполнения и размеру объектного кода.

Четность двоичного числа определяется значением его последнего разряда (0 или 1). Работа функции odd(m) сводится к логическому умножению значения m на целочисленную константу  0000 0000 0000 0001  и анализу полученного результата (0 или 1, что соответствует машинному представлению значений false и true). Другими словами, здесь имеет место . Такие действия выполняются быстрее, чем операция деления mod.

2. Процедура Writeln('y=',y:12).  Печать вещественных значений в общем случае целесообразно выполнять в форме целой и дробной частей, разделенных точкой. Изображение вещественного числа в виде мантиссы и порядка предпочтительнее в тех случаях, когда диапазон возможных изменений соответствующей переменной очень большой, что имеет место в данной программе (здесь для печати всего числа отводится 12 позиций, при этом мантисса будет иметь 12 - 6 = 6 цифр).

3. В программе Exponent используется итерационный цикл. Количество его повторений зависит от значения показателя степени n.

О П Е Р А Т О Р   Ц И К Л А   С   П О С Т У С Л О В И Е М

Синтаксическая диаграмма:

 

Между зарезервированными словами Repeat и Until может быть произвольное количество операторов, разделенных точкой с запятой. В частном случае здесь может быть лишь один пустой оператор. В отличие от цикла While группу операторов в теле цикла Repeat не нужно заключать в операторные скобки   BeginEnd.

Оператор Repeat выполняется до тех пор, пока не станет истинным логическое выражение, записанное после слова Until. При этом тело цикла (операторы между Repeat и Until) выполняется по крайней мере один раз. Выражение после Until должно иметь тип boolean.

В теле цикла Repeat должно изменяться значение выражения, записанного после слова Until, таким образом, чтобы это выражение в конечном счете приняло значение true, в противном случае будет иметь место зацикливание программы.

Схема выполнения:

Пример 1. Алгоритм Евклида.

Program Evklid3;

Var m,n,d,r : word;

Begin

 Read(m,n); Writeln('m = ',m,'  n = ',n);

 If m<n then                { обмен значений m и n, }

   Begin                    { если  n > m           }

     r:=m; m:=n; n:=r;

   End;

 If n>0 then

   Repeat

     r:=m mod n;

     m:=n; n:=r;

   Until n=0;

 d:=m; Writeln('d = ',d)

End.

Если бы в программе не была предусмотрена проверка  n > 0,  то оператор Repeat выполнялся бы при любых значениях исходных данных, в том числе и при  n = 0.  В последнем случае в теле цикла имело бы место деление на нуль, что привело бы к прерыванию работы программы.

Оператор цикла с постусловием, как и оператор цикла с предусловием, в основном применяется для реализации итерационных процессов.

Пример 2. Итерационная формула Ньютона для   .

Program Newton2;

Const eps = 0.00001;

Var  x,y,yn : real;

Begin

 Ввод и печать x

 y:=x;

 If x>0 then

   Repeat

     yn:=y;

     y:=0.5*(yn+x/yn);

   Until abs(y-yn)<eps;

 Печать  y

End.

Здесь в отличие от программы Newton1, использующей оператор While, не требуется до начала цикла искусственно создавать разницу между переменными y и yn, превышающую значение eps.

Пример 3. Определить значение и порядковый номер наибольшего числа Фибоначчи, не превышающего заданного значения b.

Числа Фибоначчи получаются с помощью рекуррентного отношения

.

Имеем последовательность чисел:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...

Program Fibonacci;

Var k,b,fi,fi1,fi2 : word;

Begin

 Read(b); Writeln('b = ',b);

 fi:=0; fi1:=1; k:=1;

 Repeat

   fi2:=fi1+fi;

   fi:=fi1; fi1:=fi2;

   k:=k+1;

 Until fi2 > b;

 Writeln('k = ',k,'  fi = ',fi);

End.

Примечание. Поскольку после окончания цикла While будет получено  fi2 > b, а в самом теле цикла произведен перенос  , то ближайшим наибольшим числом Фибоначчи, не превышающим заданную величину  b, является значение переменной fi.

О П Е Р А Т О Р   Ц И К Л А   С   П А Р А М Е Т Р О М

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

Пример 1. Вычислить  путем многократного умножения ( - целое положительное число).

В приведенной ниже блок-схеме программы   i – параметр цикла, т.е. переменная, которая управляет количеством его повторений. В подготовительном блоке параметру цикла назначается определенное начальное значение, модифицирующий блок изменяет значение параметра, управляющий блок проверяет условие окончания цикла.

Пример 2. Вычислить и отпечатать значения функции  для , изменяющегося от начального значения  до конечного значения  с шагом .

Количество вычислений функции  :

,

где квадратные скобки определяют целую часть вещественного числа.

В этом примере количество выполнений цикла заранее не указывается, но легко определяется по значениям исходных данных.

Блок-схема решения задачи: 

Для компактности изображения блок-схемы подготовительную, модифицирующую и управляющую части цикла рисуют одним блоком, который называют оператором цикла. На представленной ниже блок-схеме   -  соответственно  начальное значение, конечное значение и шаг изменения параметра цикла. Если  = 1 , то его можно не показывать на блок-схеме.

Блок-схема для примера 1 будет иметь вид:

Синтаксическая диаграмма оператора цикла с параметром:

Имя переменной на диаграмме - это параметр цикла. Он должен иметь ординальный тип и должен быть описан в том же блоке, где находится сам оператор цикла.

Выражение 1 - начальное значение параметра, выражение 2 - его конечное значение. Эти выражения должны быть ординального типа, совместимого с типом параметра цикла.

Если в операторе цикла используется зарезервированное слово to, то шаг изменения параметра цикла равен +1;  для downto этот шаг равен  -1.

Блок "Оператор" в синтаксической диаграмме - это тело цикла. В частном случае это может быть составной или пустой оператор.

В теле цикла параметр не должен изменяться. Начальное и конечное значения параметра вычисляются только один раз, до начала цикла.

При нормальном завершении цикла значение его параметра считается неопределенным.

Работу оператора цикла можно отобразить следующей блок-схемой:

               а)  Вариант to                                           б) Вариант downto

Из блок-схемы можно вывести такие заключения:

1) если в теле цикла изменяются переменные, входящие в состав выражений 1 или 2, то это не отражается на количестве повторений цикла;

2) если начальное значение параметра цикла больше его конечного значения (вариант to), то цикл ни разу не выполняется;

3) в варианте downto цикл ни разу не выполняется, если начальное значение параметра цикла меньше его конечного значения.

Пример 1а. Паскаль-программа для примера 1:

Program Mult;

Var  x,y : real;

    i,n : word;

Begin

 Ввод и печать  x, n

 y:=1;

 For i:=1 to n do

   y:=y*x;

 Печать y

End.

Пример 2а. Паскаль-программа для примера 2:

Program Table;

Var  x,xn,xk,hx,y : real;

    i,n : word;

Begin

 Ввод и печать  xn, xk, hx

 n:=Round((xk-xn)/hx)+1; x:=xn;

 For i:=1 to n do

   Begin

     y:=exp(sqrt(x))*sqr(sin(x));

     Печать x,y

     x:=x+hx

   End;

End.

Пример 3. Вычисление факториала   y=n! =

Var i,n : byte;

      y : longint;

Begin

 Read(n);

 y:=1;

 For i:=2 to n do

   y:=y*i;

 Writeln(‘y = ‘,y)

End.

Пример 4. Вычисление двойного факториала   y=n!!

Двойной факториал – это произведение четных чисел   при четном значении переменной n  или произведение нечетных чисел    при нечетном  n.

Var i,n : byte;

      y : longint;

Begin

 Read(n);

 y:=1;

 For i:=2 to n do

   If odd(i) = odd(n) then

     y:=y*i;

 Writeln(‘y = ‘,y)

End.

Пример 5. Вычислить ряд

четырьмя способами:

1) последовательно слева направо;

2) последовательно справа налево;

3) слева направо отдельно положительные и отрицательные элементы, затем их вычитание;

4) то же, но справа налево.

Program Summa;

Var  SumLR,     { сумма, все слева направо }

    SumRL,     { сумма, все справа налево }

    SumLRNeg,  { сумма, отрицательные эл-ты, слева направо }

    SumLRPos,  { сумма, положительные эл-ты, слева направо }

    SumRLNeg,  { сумма, отрицательные эл-ты, справа налево }

    SumRLPos,  { сумма, положительные эл-ты, справа налево }

    ElemLRNeg, { очередной отриц.элемент, слева направо }

    ElemLRPos, { очередной полож.элемент, слева направо }

    ElemRLNeg, { очередной отриц.элемент, справа налево }

    ElemRLPos  { очередной полож.элемент, справа налево }

           : single;

    i : integer;       { параметр цикла }

Begin

 SumLR:=0; SumRL:=0; SumLRPos:=0; SumLRNeg:=0;

 SumRLPos:=0; SumRLNeg:=0;

 For i:=1 to 5000 do

   Begin

     ElemLRPos:=1/(2*i-1); ElemLRNeg:=1/(2*i);

     ElemRLPos:=1/(10001-2*i); ElemRLNeg:=1/(10002-2*i);

     SumLR:=SumLR+ElemLRPos-ElemLRNeg;

     SumRL:=SumRL+ElemRLPos-ElemRLNeg;

     SumLRNeg:=SumLRNeg+ElemLRNeg;

     SumLRPos:=SumLRPos+ElemLRPos;

     SumRLNeg:=SumRLNeg+ElemRLNeg;

     SumRLPos:=SumRLPos+ElemRLPos;

   End;

 Writeln('SumLR = ',SumLR:10:7);