Основы программирования

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

  • 1. Перед составлением программы необходимо, используя алгоритм решения, подготовить несколько примеров решения задачи с различными исходными данными. Эти примеры нужны для того, чтобы установить, правильно ли решается задача по программе. Традиционно они называются контрольными примерами или тестами.
  • 2. Провести всесторонний анализ исходных данных задачи, промежуточных и тех данных, которые трактуются как результаты ее решения. При этом необходимо определить следующее:
    • а) какие типы данных будут использоваться в программе: числовые, символьные, логические или те и другие;
    • б) для числовых данных установить, какие это будут числа: целые, вещественные или комплексные;
    • в) для целых чисел определить возможные минимальные и максимальные их значения;
    • г) определить, с какой точностью придется оперировать вещественными числами: обычной или удвоенной;
    • д) для символьных данных установить, отдельные ли символы будут использоваться или их строки;
    • е) уточнить, какие структуры данных будут фигурировать в программе: одномерные, двухмерные, трехмерные массивы, множества, списки и др.;
    • ж) для массивов установить, будут ли применяться статические или динамические массивы, т. е. будут ли измеряться размеры массивов (количество элементов, строк, столбцов) в процессе выполнения программы.

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

  • 3. Определить способ ввода исходных данных задачи, а именно установить, будут ли вводиться все исходные данные сразу или последовательно частями (ввод с подкачкой), откуда будет осуществляться ввод.
  • 4. Определить способ вывода данных решения, а именно на какие устройства, кроме видеотерминала, следует выводить результаты решения, какие форматы вывода необходимо использовать для размещения данных на внешних носителях информации.
  • 5. Программировать следует так, чтобы размещенные в ОП исходные данные задачи в процессе выполнения программы не изменялись. Это необходимо для многократного прогона программы во время ее отладки и возможности использовать эту программу в виде составной части другой программы.
  • 6. При программировании арифметических выражений необходимо применять самые быстродействующие операции. Например, если нужно вычислить 42, то лучше программировать операцию 4x4. Она выполняется быстрее, чем возведение в степень. Если есть необходимость вычислить 2 х Ь, то лучше применить операцию Ь + Ь, так как сложение на компьютере выполняется быстрее, чем умножение.
  • 7. Если окажется, что некоторые группы команд программы повторяются в нескольких ее местах, их следует запрограммировать отдельно и оформить в виде подпрограммы, к которой можно обращаться из основной программы в местах повторений групп команд. Это сократит длину программы.
  • 8. При программировании циклов необходимо следить за тем, чтобы величины, не изменяющиеся в цикле, были вынесены за его пределы. В особенности это касается вложенных циклов, так как именно на их обработку чаще всего тратится львиная доля времени выполнения программы. В общем же нужно стремиться минимизировать число повторений в циклах.
  • 9. Программу рекомендуется составлять не на экране монитора компьютера, а на столе. Это гарантирует меньшую усталость глаз и сокращает появление дополнительных ошибок.
  • 10. При отладке программы необходимо пользоваться специальными средствами отладки: покомандной прокруткой наиболее подозрительных участков программы, ее трассировкой и др.

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

Условимся, что оперативная память учебной машины состоит из 4096 ячеек с адресами от 0 до 1095, а программа и исходные данные используют адреса, начиная с номера 1024, это так называемое поле рабочих ячеек и исходных данных.

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

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

Код

операции

а1

а2

а3

Код

операции

  • 1028
  • 1029
  • 1039

а б

Рис. 2.7. Представление абстрактной команды в символьном (а)

и числовом (б) виде

Названия операций, их произвольные коды, операнды, результаты выполнения операций и значения флагов приведены в табл. 2.3.

Таблица 2.3. Операции, их коды и результаты выполнения

Название

операции

Код

Операнды

Результат

операций

Flag = 1

Flag = 0

1

Сложение

001

аь а2

аз = а[ + а2

а3<0

а3>0

2

Вычитание

002

аь а2

со

СЗ с5

и

со

сЗ

аз < 0

аз > 0

3

Умножение

003

аь а2

Зз ~ 3( х а3

4

Деление

004

аь а2

аз = 3|/а2

5

Переход безусловный

005

аг

Переход по адресу а2

6

Переход условный по Flag = 1

006

аг

Переход по адресу а2, если Flag = 1

7

Переход условный по Flag = 0

007

32

Переход по адресу а2, если Flag = 0

8

Пересылка

008

аЬ а2

Пересылка содержимого ячейки aj в ячейку <2

9

Стоп

009

Прекращение выполнения программы

Рассмотрим следующие примеры составления элементарных программ.

Пример 2.1. Требуется составить программу вычисления объема шарового сегмента по формуле

V = nh

где И — высота сегмента; г — радиус шара.

Исходные данные этой задачи: высота сегмента И и радиус шара г. Числовые константы я = 3,14 и 3.

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

5.

  • 1.
  • 2. 3.

= И х И

12 = П х г,

к

Ц =^2 х ?4

к1

пк2

Стоп

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

Поместим исходные данные к и г соответственно в ячейки 1024 и 1025.

Поскольку всего необходимо выполнить шесть операций, константы я и 3 могут быть размещены в ячейках 1032, 1033, определяемые следующим образом:

1032 = 1025 + 6 + 1, 1033 = 1032 + 1.

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

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

003 1024 1024 1026.

По адресу 1026 мы поместили результат операции, т. е. значение к2.

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

003 1032 1026 1027.

Результат выполнения этой команды помещен по адресу 1027.

Третья команда — деление высоты И на 3:

004 1024 1033 1028.

Четвертая команда — вычитание из г результата выполнения третьей команды:

002 1025 1028 1029.

Пятая команда — вычисление объема V

003 1027 1029 1030.

Шестая команда — стоп:

009 ---.

Записывая команды последовательно одна за другой и предполагая, что они размещаются в последовательных ячейках ОП, начиная с ячейки 512, окончательно получим программу вычисления объема V в следующем виде:

512

003

1024

1024

1026

513

003

1032

1026

1027

514

004

1024

1033

1028

515

002

1025

1028

1029

516

003

1027

1029

1030

517

009

У = кИ2

И

2

пИ2

А

3

/

г

Стоп

2)

Пример 2.2. Справа от второй вертикальной черты даны пояснения к выполняемым операциям. Слева — адреса ОП, где размещены команды. Результирующее значение V находится в ячейке 1030.

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

+ Ь)2, если а < Ь

Вычислить значение функции У = <

если о > Ь.

а - Ь

В данной задаче программа должна содержать последовательности команд, согласно которым У вычисляется по формуле У= (а + Ь)2, если а < Ь, и У = (а + Ь)/2, если а> Ь. Эти последовательности должны быть взаимно исключающими, т. е. когда вычисление ведется по одной формуле, оно не выполняется по другой, и наоборот. Очевидно, для того, чтобы знать, какую формулу использовать, необходимо проверять условие а < № и в зависимости от этого направлять вычисления по тому или другому пути.

Для того чтобы установить а < Ы, можно применить операцию вычитания а - Ь и проверить знак разности.

Если а < Ь, следует осуществить переход к последовательности команд, вычисляющих У по формуле У=(а + Ь)2, если нет, продолжить вычисления У по формуле У=(а + Ь)/2. Поэтому последовательность машинных операций может быть такой:

. Z^ = а - Ь.

2.12 переход к п. 5, если а - Ь < 0.

  • 4. Z4 безусловный переход к п. 7.
  • 5. z5 = а + Ь.
  • 6. = ^5 х^5-
  • 7. Стоп.

Для осуществления условных переходов конструкторами ЭВМ предусмотрены две команды: переход по значению знакового флага, равного 1, и переход по flag = 0. Эти команды принципиально действуют одинаково. В зависимости от значения флага они помещают в регистр IP адрес перехода или помещают в него адрес очередной по порядку команды программы. Кроме этих команд имеется еще команда безусловного перехода, в соответствии с которой переход осуществляется всегда по указанному в ней адресу. Команды переходов под соответствующими условными кодами представлены в табл. 2.3.

Пример 2.3. Исходные данные задачи (значения а, Ь) разместим соответственно в ячейках 1024, 1025. Константу 2 поместим в ячейку 1030. Используя представленную ранее последовательность операций, программу запишем следующим образом:

512

002

1024

1025

1026

а - b

513

006

516

Переход по flag = 1

514

004

1026

1030

1029

У=(а-Ь)/2

515

005

518

Переход безусловный

516

001

1024

1025

1028

а + b

517

003

1028

1028

1029

у=(а + Ь)2

518

009

Стоп

Другой вариант программы, в которой используется переход по flag = 0, представлен ниже:

512

002

1024

1025

1026

а - b

513

007

517

Переход по flag = 0, т. е. а > b

514

001

1024

1025

1027

а + b

515

003

1027

1027

1029

у = (а + Ь)2

516

007

518

Переход безусловный

517

004

1026

1030

1029

1

II

518

009

Стоп

Таким образом, по количеству команд обе программы равноценны.

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

Пример 2.4. Необходимо вычислить значение так называемой знаковой функции у=8щпх. График и аналитическая запись этой функции представлены на рис. 2.8.

Очевидно, что для того, чтобы проверить условия X < 0 или х > 0, необходимо выполнить действия вычитания 0-х и х - 0 и по полученным значениям флагов осуществить переход. На этом основании для вычисления у = вщпх может быть предложена следующая последовательность действий.

. 1 = 0 -X.

  • 2. переход к п. 9, если х <0.
  • 3. ?3 = х - 0.
  • 4. ^ — переход к п. 7, если х > 0.
  • 5. г5 =0.
  • 6. ^ — переход безусловный кп. 10.
  • 7. ? 5 =1.
  • 8. ^8 — переход безусловный КП. 10.
  • 9. ^5 =0-1.
  • 10. Стоп.

-1, еслихсО;

У ‘

y=signx = ^0, еслих=0;

1, еслих>0. 1

ч

1

-1

-У*

Рис. 2.8. График и аналитическая запись функции у = sign х

Исходное данное х разместим в ячейке 1024, константы 0 и 1 — соответственно в ячейках 1029, 1030. Тогда программа может быть представлена следующей последовательностью команд:

512

002

1029

1024

1025

0-х

513

006

520

Переход по flag = 1

514

002

1024

1029

1025

х — 0

515

007

518

Переход по flag = 0

516

008

1029

1025

у = 0 (пересылка)

517

005

521

Переход безусловный

518

008

1030

1025

у = 1 (пересылка)

519

005

521

Переход безусловный

520

002

1029

1030

1025

т=-1

521

009

Стоп

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

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

Пример 2.5. Пусть, например, необходимо найти сумму элементов одномерного массива, содержащего 100 компонент S= я, + я2 + ... + я, + ... + ат. При программировании этой задачи следовало бы выбрать некоторую ячейку с адресом В, переслать в нее нулевое значение суммы S, а затем 100 раз выполнить команду сложения 001 Bass. Однако элементы я, имеют в ОП переменные адреса. Значит, пришлось бы 100 раз записать команду 001 Bass с конкретным адресом 001 я, т. е. существенно растянуть программу.

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

В команде, адрес которой модифицируется, записывается начальный адрес последовательности я,, я2, ..., я„ ..., я„ и регистр-модификатор. Непосредственно в регистре все время образуется переменная добавка к начальному адресу. Модификация осуществляется ЦП аппаратно путем прибавления к начальному адресу текущей добавки. Полученный адрес называется действительным или исполнительным адресом. С этим адресом ЦП и выполняет очередную команду программы.

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

На рис. 2.9 показана схема обращения к подпрограмме. Основная программа и подпрограмма изображены в виде строк, условно представляющих последовательности команд. Стрелками показаны переходы к выполнению команд подпрограммы (обращения) и возвраты в основную программу.

Основная программа

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

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

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

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

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

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

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

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

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

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

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

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

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

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

если я < Ь

Стек

Схема работы стека

Рис. 2.10. Схема работы стека

(а + Ь)2,

а-Ъ

если а > Ь

требовалось на самом деле вычислять У по формуле У=(а - Ь)1/2, при а > Ь, то в программе вместо команды по адресу 514 необходимо было бы вставить команду вычисления (а - Ь)2, а затем команду вычисления (а - Ь)2/2. Это повлекло бы изменение не только команд указанных вычислений, но и изменение адресов размещения остальных команд в памяти машины и адресов переходов, т. е. пересмотра почти всех команд программы.

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

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

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

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

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

Приведем примеры символических обозначений машинных операций, запасаемых рабочих ячеек и констант. Согласно табл. 2.3 обозначим: сложение — С, вычитание — В, умножение — У, деление — Д, переход безусловный — ПБ, переход условный по flag = 1 — ПУ1, переход условный по flag = 0 — ПУО, пересылка — П, останов — Стоп. Для резервирования памяти примем обозначение ПАМ6, где 6 — количество резервируемых ячеек ОП. Константы будем определять так: Конст 3.14 или Конст 2.

В этих обозначениях программа вычисления объема шарово-

' М

г — го сегмента по формуле V = пЬ

к

у

будет иметь такой вид:

НАЧАЛО:

КОНЕЦ:

ПИ:

ТРИ:

У

У

в

У

ь

пи

ь

г

ь

ЯП1

ТРИ

ЯШ ЯП 1

ЯП2

ЯП2 ЯП1

ЯП1 ЯП2 V

Стоп — —

КОНСТ 3,14 -КОНСТ 3 -

ПАМ 2 -

ь лЬ2

ь

3

V

ОСТАНОВ

КОНСТАНТА 3,14

КОНСТАНТА 3

РАБОЧАЯ ПАМЯТЬ ДВЕ ЯЧЕЙКИ

Операнды ЯШ, ЯП2 — первая и вторая рабочие ячейки. Обозначения «НАЧАЛО:, КОНЕЦ:, ПИ:, ТРИ:, ЯП :» — метки программы.

В символьных обозначениях программа вычисления значе-

(а + Ь)2, если я < Ь

ний функции У =

- Ь)/2, если а > Ь

будет такой:

НАЧАЛО:

М1:

КОНЕЦ:

ЯП:

ДВА:

В

ПУ1

ПБ

С

У

СТОП

ПАМ

КОНСТ

ЯП

- М1

К11 ДВА у

- КОНЕЦ -

а Ь

ЯП ЯП

2

а-Ь

Переход условный на метку М1

У = (о- Ь)! 2

Переход безусловный на КОНЕЦ ’

ЯП а + Ь у у = (а + Ь)1 Останов

РАБОЧАЯ ПАМЯТЬ ОДНА ЯЧЕЙКА

КОНСТАНТА 2

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

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

С - 001 В - 002 У - 003 Д - 004 ПБ - 005 ПУ1 - 006 ПУ0 - 007 П - 008 Стоп — 009.

Условимся, что программа загружается в ОП с адреса 524. Тогда, поскольку она содержит семь команд, для исходных данных а, b можно выбрать ячейки 524 + 7 = 531, 531 + 1 = 532. Для результата решения можно использовать ячейку 532 + 1 = 533. Рабочая ячейка будет иметь адрес 533 + 1 = 534, а константа «Два» — 535. Метка «М1» имеет адрес 524 + 4 = 528.

На основании этого получаем распределение памяти и машинную программу Начало — 524

М1 -

Конец — а —

530

b - 532

У - 533

ЯП - 534

ДВА - 535

524

002

531

532

534

а-Ь

525

006

528

Переход условный

526

004

534

535

533

У = (а- Ь)! 2

527

005

530

Переход безусловный

528

001

531

532

534

а + b

529

003

534

534

533

У = + Ь)2

530

009

Останов

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

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

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

В настоящее время на персональных компьютерах IBM PC в качестве машинно-ориентированных языков используются языки ASM-86, MASM, TASM (ассемблер-1986, макроассемблеры). Программы, составленные на этих языках, переводятся на машинный язык компьютеров соответствующими ассемблерами-трансляторами. Различие языков А8М-86, МА8М состоит в том, что в А8М-86 каждой символьной команде соответствует одна и только одна машинная операция. В макроассемблере МА8М есть команды (макрокоманды), которым соответствуют группы машинных операций.

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

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

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

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

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

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

Первым по времени создания (1957 г.) языком высокого уровня был РОЯТКАМ. Аббревиатура происходит от сокращения двух английских слов Р(Жти1а Т11А№1аЦоп — перевод формул. Язык разрабатывался фирмой 1ВМ и предназначался для решения информационных задач преимущественно вычислительного характера. Наибольшую популярность во всем мире получила четвертая версия (видоизменение) этого языка — FORTRAN IV (1962 г.). Компиляторы с этого языка были установлены практически на всех ЭВМ вычислительных центров 60—70-х годов прошлого века. На этом языке изготовлены и стандартизованы программы решения большинства задач вычислительной математики и оптимизации функций, объединенные в разные библиотеки [9].

Язык прошел ряд этапов совершенствования — РОЯТ11АЫ-77, Р(ЖТ11АМ-90 и в настоящее время на персональных компьютерах используется как Р(ЖТКАМ-2000 [11].

В 1960 г. официально был зарегистрирован язык А1ЛЮЬ-60 (АЬОогйпцс Ьап§?щ — алгоритмический язык). Этот язык появился как результат попытки создать универсальный язык, предложения которого были бы максимально приближены к общепринятым математическим обозначениям и обеспечивали бы простой обмен программами в среде специалистов различных отраслей. Язык требовал строгой дисциплины программирования, являлся удачным инструментом для выработки хорошего стиля программирования, в связи с этим получил название университетского языка.

Тем не менее на практике он не получил широкого распространения в основном потому, что не выдержал конкуренции с языком FORTRAN по быстродействию скомпилированных программ и не был поддержан фирмой ?BM. Однако ALGOL-60 послужил базой для разработки более совершенных языков: PL/1, PASCAL, Си.

Язык BASIC (Begginner's All-purpouse Symbolic Instruction Code) — многоцелевое символическое кодирование для начинающих), разработан сотрудниками Дартмудского университета США для обучения программированию студентов-гуманитариев. Основное его отличие от известных к тому времени языков — общение пользователя с ЭВМ при помощи терминала типа печатающей машинки с дисплеем. В те времена в США стали распространяться вычислительные центры коллективного пользования, на которых к центральному процессору подключалось много терминалов, работающих в режиме с разделением времени, т. е. когда каждому из них на протяжении определенного сеанса работы периодически выделялся квант времени. Именно для работы в таких системах и был разработан BASIC.

Системы коллективного пользования исчезли, но язык BASIC остался. Он прошел достаточно длинный путь эволюционного развития — от языка, ориентированного на интерпретацию программ, до языка, предусматривающего компиляцию исходного текста. К слову говоря, один из первых интерпретаторов этого языка разрабатывал Билл Гейтс — президент фирмы Microsoft.

Известны такие названия языка, как GW BASIC, TURBO BASIC (1987 г.), QBASIC, QUIC BASIC (1988 г.), VISUAL BASIC. Более того, VISUAL BASIC выпускается в нескольких версиях: Learning Edition (Учебная редакция), Professional Edition (Профессиональная редакция), Enterprise Edition (Редакция для предприятия), VISUAL BASIC for Application (Visual BASIC для приложений).

BASIC оказался весьма приспособленным для работы на персональных компьютерах, широко использовался и совершенствовался в 80—90-х годах прошлого века и, по-видимому, поэтому сохранился и остается в эксплуатации в настоящее время.

Хотя и уступил свои позиции в среде профессионалов, PASCAL (1973 г.), названный так его разработчиком Н. Виртом (Швейцария) в честь французского математика Блеза Паскаля, он по-прежнему остается одним из лучших университетских языков. Разработанная на его основе программная среда DELFI является надежной системой современного стиля программирования.

Язык Си, потеснивший PASCAL, разработан в США в 1972 г. году как инструмент, предназначенный для использования профессионалами. Будучи языком высокого уровня, он включал элементы автокодов, т. е. был максимально приближен к машинному языку. Благодаря этому, программы, составленные на этом языке, получились достаточно быстродействующими и экономными по памяти. Язык включает средства, позволяющие, как утверждают специалисты, программировать любые алгоритмы. Другие специалисты утверждают, что он менее надежен, чем PASCAL.

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

В последние годы сильно возросла популярность языка JAVA. По существу — это упрощение C++. Язык сохранил гибкость C++, по объему средств стал меньше и надежнее C++. Он содержит в себе не только элементы объектно-ориентированного программирования, но и достаточно простые формы создания параллельных программ, подготовки Web-страниц и порталов для сети INTERNET.

Кроме перечисленных языков за более чем 50-летний период их разработки были созданы другие достаточно известные языки программирования высокого уровня: COBOL, LISP, PL/l, APL, SNOBL, MODULA-2, PROLOG, ADA. Этот список не исчерпывает всех разработанных языков — их более сотни. Но почему их так много?

Дело в том, что спектр информационных задач, решаемых на компьютерах, очень широк. Алгоритмы их решения различны. Входные и результирующие данные и по количеству, и по структурам также различны. Поэтому различны приемы составления программ решения задач. В связи с этим при разработке ориентировались как на учет специфики программирования того или иного класса задач, так и ставились цели создать универсальный язык. Например, COBOL предназначался для решения задач экономического характера, в которых приходилось манипулировать большими объемами различных данных, в то время как вычислительных операций было мало. Язык LISP предназначался для программирования процессов эффективной обработки списков данных. PL/1 — попытка создать универсальный язык. Эта же задача ставилась Н. Виртом и при разработке языка PASCAL. В языке ADA отражено стремление создать универсальный язык для программирования алгоритмов, описывающих работу военных объектов.

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

  • 1) был бы легок для изучения начинающими;
  • 2) был бы легок для работы на нем специалистов;
  • 3) позволял бы легко читать программы;
  • 4) дисциплинировал бы программистов и заставлял их следовать хорошему стилю программирования;
  • 5) подходил бы для составления коротких программ;
  • 6) мог использоваться для составления программ среднего размера;
  • 7) подходил бы для составления длинных программ, содержащих десятки и сотни тысяч строк;
  • 8) мог использоваться для доказательства правильности программ;
  • 9) был бы легок для реализации на любой ЭВМ;
  • 10) позволял бы быстро выполнять программы.

Например, для BASIC главные критерии 1, 5, для PASCAL —

3, 4, 10, для Си — 10, 6, 2 в той последовательности, в которой они перечислены.

В настоящее время на персональных компьютерах используются преимущественно языки BASIC, PASCAL, Си и постепенно начинает применяться JAVA.

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

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

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

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

а б в г д

Рис. 2.11. Элементы структурной блок-схемы: а — композиции; б, в — выборов; г, д — циклов с предусловием и постусловием

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

Приведенных элементарных блок-схем, вообще говоря, достаточно для построения любых хорошо структурированных алго-

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

а

Элементарные блок-схемы разветвления по многим путям (а) и преждевременного выхода из цикла (б)

Рис. 2.12. Элементарные блок-схемы разветвления по многим путям (а) и преждевременного выхода из цикла (б)

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

Пример 2.6. Пусть дана прямоугольная матрица чисел А = ||я||, состоящая из т строк и п столбцов. Требуется найти

строку к, сумма ?* элементов которой максимальна, найти в этой строке минимальный ее элемент ак отметить номер соответствующего столбца / и вычислить сумму элементов этого столбца S,. В связи с тем, что в матрице А может оказаться несколько строк с одинаковыми максимальными значениями сумм их элементов, а строке принадлежать несколько равных минимальных элементов, для определенности следует искать первую из строк с максимальной суммой элементов, а в ней — самый первый из минимальных элементов. Кроме этого, матрица А должна содержать не менее двух строк и двух столбцов.

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

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

Среди алгоритмических языков высокого уровня возможностями параллельного программирования располагает FORTRAN, ADA, JAVA. Разработанный в свое время для этих целей язык MODULA-2 не нашел широкого практического применения.

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

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

Вычислить сумму Я; элементов 1-Й строки А

1

2

Да

з

Рис. 2.13. Последовательность блок-схем 1—5, отвечающих принципам структурного программирования сверху вниз (начало)

Ввести /7, /77, А

4

Рис. 2.13. (Продолжение)

1= 1,/=2

=*г~

5, = 0,7 = 1

I

7=7+1

и = Я

к = /, Я = и

2

I=1-и

5

Рис. 2.13. (Окончание)

 
< Пред   СОДЕРЖАНИЕ     След >