Глава 24
Языки высокие и низкие
Программировать в машинных кодах — все равно что есть
зубочисткой. Подхватывать ею кусочки еды столь сложно, а
сами они столь малы, что обед растягивается в вечность. Бай-
тами машинных кодов обозначены настолько простые вычис-
лительные задачи — загрузка числа из памяти в процессор,
сложение его с другим числом, запись результата обратно в
память, — что практически невозможно представить себе, как
из таких крохотных кирпичиков складывается целое здание.
Еще в начале главы 22 мы вводили двоичные данные в па-
мять переключателями, но с тех пор нам удалось здорово про-
двинуться вперед. В той же главе я рассказал, как с помощью
простых программ задействовать клавиатуру для ввода дан-
ных (например, машинных кодов) и просматривать их на эк-
ране монитора. После этого работать на нашем воображаемом
компьютере стало удобнее, но все возможные усовершенство-
вания нами еще не исчерпаны.
Как вы помните, байтам машинных кодов сопоставлены
короткие мнемонические коды MOV, ADD, CALL, HLT, благо-
даря которым программа по крайней мере отдаленно напоми-
нает набор указаний на обычном английском языке. Часто за
мнемокодом следует аргумент, уточняющий действие коман-
ды. Например, в системе команд процессора 8080 команда с
кодом 46h помещает в регистр В байт из ячейки памяти, адрес
которой записан в паре регистров HL. С помощью мнемокода
это действие выражается нагляднее:
440
Глава двадцать четвертая
MOV B,[HL]
Конечно, составлять программу на языке ассемблера гораздо
проще, чем писать ее в машинных кодах. Увы, процессор ас-
семблера не понимает. Поэтому программу в мнемокодах при-
ходится писать на бумаге. Когда вам кажется, что она готова к
запуску, вы вручную преобразуете операторы ассемблера в
двоичные машинные коды и вводите их в память.
Но почему не автоматизировать эту задачу? Для этого впол-
не достаточно компьютера с процессором 8080, работающего
под управлением ОС CP/M.
Для начала создайте текстовый файл PROGRAM1.ASM и
введите в него программу на ассемблере (тип ASM как раз и
означает, что в файле содержится ассемблерная программа).
Для создания и редактирования файла можно применить тек-
стовый редактор из комплекта CP/M — программу ED.COM.
Слишком сложную задачу перед собой ставить не будем:
ORG 0100h
LXI DE,Text
MVI C,9
CALL 5
RET
Text: DB 'Hello!$'
END
Почти все операторы этой программы нам знакомы. Команда
ORG, с которой мы раньше не встречались, не входит в систе-
му команд процессора 8080. Она просто указывает, что следу-
ющая команда должна располагаться по адресу 0100h. Как вы
помните, начиная именно с этого адреса, CP/M загружает про-
граммы в память.
Следующая команда — LXI — загружает 16-битовое значе-
ние в пару регистров DE. Здесь адрес значения указан меткой
Text, расположенной в конце программы перед оператором DB
(Data Byte, байт данных). Аргументами оператора DB могут быть
несколько байтовых значений, разделенных запятыми, или (как
у нас) текстовая строка, заключенная в апострофы.
Команда MVI помещает в регистр С число 9. Это номер
функции CP/M для отображения на экране текстовой строки,
начинающейся по адресу, записанному в регистрах DE, и за-
441
Языки высокие и низкие
канчивающейся значком доллара $ (немного странно, что окон-
чание строки обозначается именно значком $, но так уж уст-
роена CP/M). Функцию вызывает оператор CALL 5. Наконец,
команда RET останавливает выполнение программы и пере-
дает управление системе (остановить программу можно и ина-
че). Оператор END отмечает конец файла с программой на ас-
семблере.
Итак, у нас есть текстовый файл, содержащий 7 строк. Те-
перь его нужно ассемблировать, т. е. преобразовать операторы
ассемблера в машинные коды. До сих пор мы делали это вруч-
ную. Но в CP/M входит специальная программа ASM.COM, ко-
торая выполнит это преобразование автоматически. Достаточ-
но ввести после приглашения системы команду:
ASM PROGRAM1.ASM
Программа ASM находит на диске файл PROGRAM1.ASM и
создает файл PROGRAM1.COM, в который помещает машин-
ные коды, соответствующие операторам ассемблера (честно
говоря, я пропустил один этап процесса ассемблирования, но
для понимания сути происходящего он не важен). После это-
го программу PROGRAM1 можно запускать из командной
строки CP/M. Она отобразит на экране строку «Hello!» и за-
кончит работу.
Файл PROGRAM1.COM содержит следующие 16 байтов:
11 09 01 0E 09 CD 05 00 C9 48 65 6C 6C 6F 21 24
Первые 3 байта — это команда LXI, следующие 2 — команда
MVI. 3 байта занимает команда CALL 5, и 1 — команда RET.
Последние 7 байт — это ASCII-коды букв Hello, восклицатель-
ного знака и знака $.
Работа программы-ассемблера, например, ASM.COM, сво-
дится к чтению файла с программой на языке ассемблера (про-
граммисты часто называют его файлом с исходным кодом) и
созданию исполняемого (executable) файла, содержащего ма-
шинные коды. По сути своей, ассемблеры — очень простые
программы, поскольку в основе их работы лежит взаимно од-
нозначное соответствие между мнемоническими и машинны-
ми кодами. Ассемблер считывает из файла строку, разделяет
ее на мнемокод команды и аргументы, а затем находит в спис-
ке машинный код, соответствующий такому сочетанию коман-
442
Глава двадцать четвертая
ды и аргументов (в списке должны быть все возможные соче-
тания). В результате сравнения появляется последовательность
машинных кодов, в точности соответствующая последователь-
ности команд.
Заметьте: вместо метки Text в код подставлен ее адрес 0109h.
Именно там окажется текстовая строка после того, как про-
грамма будет загружена в память, начиная с адреса 0100h.
Обычно программисту, пишущему программы на ассембле-
ре, не приходится беспокоиться из-за подстановки конкрет-
ных адресов — это делает ассемблер.
Программисту, написавшему первую программу-ассемб-
лер, конечно, пришлось переводить ее в машинные коды вруч-
ную. Следующую, улучшенную версию ассемблера для того
же компьютера, уже можно ассемблировать автоматически с
помощью первого ассемблера. Новый ассемблер приходится
разрабатывать каждый раз при появлении нового процессора.
Но заниматься этим можно на компьютере со старым процес-
сором и старым ассемблером.
Конечно, программы-ассемблеры избавляют программи-
ста от самой неблагодарной части работы — ручного перево-
да операторов в машинные коды. И все же язык ассемблера
отягощен двумя крупными недостатками. О первом из них вы,
вероятно, уже догадались: программирование на языке ассем-
блера — задача весьма кропотливая. Непосредственно управ-
ляя действием микропроцессора, приходится беспокоиться о
бесчисленных мелочах.
Второй недостаток в том, что программа на ассемблере не
является переносимой (portable). Например, программу, напи-
санную на языке ассемблера для процессора Intel 8080, нельзя
запустить на компьютере с процессором Motorola 6800. Пред-
варительно ее придется переписать на ассемблере для этого
процессора. Это, конечно, будет несложно, поскольку основ-
ные организационные и алгоритмические проблемы вы реши-
ли, создавая исходную программу, но работы все же хватит.
В главе 23 я писал о том, что в современные микропроцес-
соры встроены команды для работы с числами с плавающей
точкой. Они, конечно, облегчают программисту жизнь, но не
особо. Хотелось бы совершенно забыть о командах процессо-
ра для выполнения элементарных арифметических операций,
443
Языки высокие и низкие
используя для математических выражений проверенную ве-
ками алгебраическую форму записи, например:
A
´ Sin(2 ´ PI + B) / C
где A, B и C — произвольные числа, а PI равно 3,14159.
Так в чем проблема? Если это выражение записано в тек-
стовом файле, мы вольны создать программу на языке ассем-
блера, которая будет читать строки из этого файла и преобра-
зовывать их в машинные коды.
Если бы вам нужно было вычислить это выражение лишь
однажды, вы обошлись бы калькулятором. На создание про-
граммы вас, вероятно, подвигла необходимость вычислять его
многократно, с различными значениями коэффициентов A, B
и C. Значит, кроме этого выражения вам понадобятся и дру-
гие, для вычисления коэффициентов.
Все это задачи для языка программирования высокого уров-
ня (high-level). Язык ассемблера считается языком низкого уров-
ня (low-level), поскольку он напрямую взаимодействует с обо-
рудованием компьютера. На практике языками высокого уров-
ня называют любые языки, кроме ассемблера, но это не значит,
что все они равнозначны. Уровень одних языков выше уровня
других языков. А знаете, что такое язык программирования
высочайшего уровня? Это когда президент компании набирает
на клавиатуре или, еще лучше, положив ноги на стол, диктует
секретарше: «Рассчитать прибыли и убытки за этот год, напи-
сать годовой отчет, распечатать 1 000 экземпляров и разослать
акционерам»! Конечно, настоящие языки программирования,
даже высокого уровня, очень далеки от этого идеала.
Человеческие языки формируются в течение сотен и ты-
сяч лет взаимопроникновения, случайных изменений и заим-
ствований. Даже в основу искусственных языков вроде эспе-
ранто положены реальные языки со всей их многовековой ис-
торией. Другое дело — языки программирования. Их созда-
ние всегда было продуманным и целенаправленным. Созда-
тель языка программирования воплощал в нем свое представ-
ление об общении человека и машины. В 1993 г. было подсчи-
тано, что с 1950-х годов разработано более 1 000 языков про-
граммирования.
Конечно, изобретение языка (т. е. разработка синтаксиса
— правил построения выражений) — только полдела. Нужно
444
Глава двадцать четвертая
также написать компилятор (compiler), т. е. программу, пре-
образующую операторы языка в машинные коды. Подобно
ассемблеру, компилятор символ за символом считывает файл
с исходной программой, разделяя его на слова, операторы и
числа. Написать компилятор, разумеется, гораздо сложнее, чем
ассемблер. Оператор ассемблера переводится в единственный
машинный код, тогда как оператор языка высокого уровня, как
правило, превращается во множество машинных команд. Раз-
работке компиляторов посвящена обширная литература.
У языков программирования высокого уровня есть свои
плюсы и минусы. Главное преимущество языка высокого уров-
ня в том, что он обычно намного превосходит ассемблер в лег-
кости изучения и использования. Программа на языке высо-
кого уровня короче и понятнее ассемблерной. Языки высоко-
го уровня в основном переносимы, т. е. одинаково хорошо
работают на компьютерах с разными процессорами. Это зна-
чит, что, создавая программу, не нужно вникать в тонкости
архитектуры компьютера, на котором она будет работать. Ко-
нечно, при этом для каждого процессора нужен собственный
компилятор. Созданный им исполняемый файл также будет
годиться только для этого процессора.
С другой стороны, программа на ассемблере практически
всегда эффективнее аналогичной программы на языке высо-
кого уровня. Это значит, что исполняемый файл, созданный
компилятором языка высокого уровня, занимает больше мес-
та и работает медленнее, чем функционально идентичная про-
грамма на ассемблере. Нужно отметить, что в последние годы
это правило уже не выполняется с прежней неизменностью.
Микропроцессоры стали более сложными, а создатели компи-
ляторов достигли больших успехов в оптимизации генериру-
емых кодов.
Язык высокого уровня облегчает использование процессо-
ра, но не делает его мощнее. Любые действия, которые процес-
сор в состоянии выполнить, могут быть запрограммированы
на языке ассемблера. Это значит, что программа на языке высо-
кого уровня не наделяет процессор никакими новыми возмож-
ностями. Более того, чтобы сделать программу переносимой,
от некоторых специфических возможностей данного процес-
сора в ней приходится отказываться.
445
Языки высокие и низкие
Приведу пример. Во многих процессорах есть команды для
выполнения побитового сдвига содержимого аккумулятора
влево или вправо. Аналогичных команд практически ни в од-
ном языке высокого уровня нет. Чтобы осуществить эту опе-
рацию, вам придется заменить ее делением или умножением на
2 (впрочем, во многих современных компиляторах для умно-
жения или деления на степень двойки используются именно
команды побитового сдвига). Нет во многих языках высокого
уровня и команд для выполнения с битами булевых операций.
Большинство прикладных программ для первых домаш-
них компьютеров писалось на ассемблере. В наши дни его прак-
тически не используют, разве что для решения каких-либо
специфических задач. Чем изощреннее архитектура процес-
соров, тем сложнее писать для них ассемблерные программы.
Одновременно все совершеннее становятся и компиляторы.
Немаловажную роль в снижении популярности ассемблера
сыграло и увеличение объема доступной оперативной и дис-
ковой памяти. Программистам более не нужно создавать коды,
которые обходились бы минимальным количеством памяти и
целиком умещались на одной дискете.
Проектировщики многих первых компьютеров пытались
формулировать задания для них с помощью алгебраической
записи, но создать действительно работающий компилятор
удалось только в 1952 г. Его написала для компьютера UNIVAC
Грейс Мюррей Хоппер (1906–1992), работавшая тогда в ком-
пании Remington Rand. Грейс Хоппер связала свою жизнь с
вычислительной техникой еще в 1944 г., но даже разменяв де-
вятый десяток, продолжала работать в компьютерной индуст-
рии, отвечая за связи с общественностью в корпорации Digital
Equipment.
Старейшим языком высокого уровня, не утратившим сво-
ей актуальности и сегодня, является ФОРТРАН (FORTRAN),
хотя от исходной версии в нем мало что сохранилось. Кстати,
названия многих языков программирования принято писать
прописными буквами, так как они являются сокращениями.
Например, название ФОРТРАНа составлено из словосочета-
ния «FORmula TRANslation» (трансляция формул). ФОРТРАН
разработан в IBM в середине 1950-х для компьютеров серии
704 и долгое время интенсивно использовался в научном и
инженерном программировании. Он особенно удобен для ма-
446
Глава двадцать четвертая
тематических расчетов благодаря обширнейшей поддержке
операций с плавающей точкой, включая возможность работы
с комплексными числами.
У каждого языка масса сторонников и противников. Спо-
ры их зачастую протекают весьма эмоционально. Стараясь
сохранить нейтралитет, я решил использовать в качестве при-
мера язык, который сейчас уже почти никто не применяет, —
АЛГОЛ (ALGOL). Его имя — тоже сокращение, составленное
из «ALGOrithmic Language» (алгоритмический язык). Иссле-
довать природу языков высокого уровня на примере АЛГОЛа
удобно еще и потому, что он во многих отношениях — пря-
мой предок многих распространенных языков, появившихся
за последние 40 лет. Даже в наши дни иногда приходится слы-
шать о «языках программирования типа АЛГОЛа».
Первую версию — АЛГОЛ 58 — разработал в 1957–58 гг.
международный комитет программистов. Два года спустя был
выпущен усовершенствованный вариант — АЛГОЛ 60. В кон-
це концов дело дошло и до АЛГОЛа 68, но в этой главе я расска-
жу о версии языка, описанной в документе «Переработанное
описание алгоритмического языка АЛГОЛ 60», работа над ко-
торым была закончена в 1962, а выход в свет состоялся в 1963 г.
Давайте напишем на АЛГОЛе небольшую программу. Бу-
дем считать, что у нас имеется компилятор этого языка
ALGOL.COM, работающий в CP/M или, скажем, MS-DOS. За-
пишем эту программу в файл FIRST.ALG (обратите внимание
на тип файла).
Программа на АЛГОЛе заключается между ключевыми сло-
вами (keywords) begin и end. Для начала выведем на экран одну
строку текста:
begin
print ('This is my fist ALGOL program!');
ende
Для запуска компилятора нужно набрать в командной строке:
ALGOL FIRST.ALG
В ответ компилятор сообщит о нераспознанном ключевом сло-
ве в третьей строке программы:
Line 3: Unrecognized keyword 'ende'.
447
Языки высокие и низкие
В вопросах правописания компилятор даст фору самому при-
дирчивому учителю английского языка. Набирая программу,
я неправильно ввел ключевое слово end. Компилятор не за-
медлил сообщить мне о синтаксической ошибке. Там, где сто-
ит слово ende, компилятор ожидал увидеть известное ему клю-
чевое слово АЛГОЛа.
Исправляем ошибку и запускаем компилятор снова. На
диске появляется исполняемый файл — FIRST.COM в CP/M
или FIRST.EXE в MS-DOS. Вообще-то чаще для преобразова-
ния файла с исходной программой в исполняемый файл нуж-
но выполнить не одно, а два действия, но я для простоты об
этом умолчу. Так или иначе, для запуска созданного исполня-
емого файла в командную строку нужно ввести его имя:
FIRST
На экране появятся слова:
This is my fist ALGOL program!
Знатоки английского укоризненно качают головами. В этой
фразе тоже есть ошибка: в слове «first» (первый) пропущена
буква r. Однако на нее компилятор никак не прореагировал,
да это и понятно. В задачу программы входит вывод на экран
заданной текстовой строки, и компилятор, конечно, не анали-
зирует ее содержимое.
Вы, наверное, уже догадались, что оператор print осуще-
ствляет вывод на экран некоей информации, в данном случае
— текстовой строки. В этом смысле показанная выше програм-
ма на языке АЛГОЛ эквивалентна программе на языке ассемб-
лера, приведенной в начале главы. Оператор print в офици-
альную спецификацию языка АЛГОЛ не входит, но мы будем
считать, что у нашего компилятора есть соответствующая
встроенная функция (built-in function). Большинство опера-
торов АЛГОЛа (не считая begin и end) должны заканчиваться
точкой с запятой. Вставлять отступ перед оператором print не
обязательно, но вообще отступы часто используют, чтобы
прояснить структуру программы.
Теперь напишем программу для умножения двух чисел. В
любом языке программирования имеется понятие переменной
(variable). Переменная обозначается буквой или сочетанием
букв, например, коротким словом. Хотя для хранения чисел
448
Глава двадцать четвертая
по-прежнему используются ячейки памяти, переменные по-
зволяют обращаться к ним без явного указания адреса, что
делает программу понятнее. Нам понадобятся переменные a,
b и c — для двух сомножителей и для произведения:
begin
real a, b, c;
a := 535.43;
b := 289.771;
c := a
´ b;
print (‘Произведение ', a, ' и ', b, ' равно ', c);
end
Оператор real используется для описания переменных, т. е.
для введения их имен и указания типа. В данном случае, пере-
менные a, b и c предназначены для хранения вещественных
чисел, т. е. чисел с плавающей точкой (для описания целых
переменных в АЛГОЛе служит оператор integer). Практически
во всех языках имена переменных могут включать цифры, но
начинаться должны обязательно с буквы. Применение пробе-
лов и специальных символов, как правило, не допускается. Во
многих компиляторах ограничена и длина имени. Я в приме-
рах будут использовать имена переменных, состоящие из од-
ной буквы.
Если наш компилятор АЛГОЛа поддерживает стандарт IEEE
для хранения чисел с плавающей точкой, для любой из трех
переменных понадобится 4 байта с простой точностью и 8 байт
— с удвоенной.
Следующие три выражения являются операторами присва-
ивания (assignment). В АЛГОЛе их легко распознать по двоето-
чию и знаку равенства (в других языках используется только
знак равенства). В левой части оператора присваивания стоит
имя переменной, в правой — выражение. Переменной при-
сваивается значение, полученное в результате вычисления
выражения. В первых двух операторах присваиваются конк-
ретные численные значения. В третьем операторе в перемен-
ную c записывается произведение переменных a и b.
449
Языки высокие и низкие
В современных языках символ
´ не используется, так как
его нет в кодировках ASCII и EBCDIC. Вместо него применя-
ется символ
*
. Деление в АЛГОЛе обозначается косой чертой
(/),
¸ обозначает деление нацело, а символ , также не входя-
щий в кодировку ASCII, — возведение в степень.
Для отображения переменных и констант служит опера-
тор print. Отдельные элементы списка вывода разделяются за-
пятыми. Отображение на экране текста — не великая заслуга,
но если вы захотите вывести на экран значение числовой пе-
ременной с плавающей точкой, оно будет автоматически пре-
образовано в ASCII:
Произведение 535.43 и 289.771 равно 155152.08653
Выведя эту строку, программа завершает работу и передает
управление ОС.
Чтобы найти произведение двух других чисел, вам придется
отредактировать программу, перекомпилировать ее и запус-
тить вновь. Чтобы не возиться с перекомпиляцией, вызовите
встроенную функцию read:
begin
real a, b, c;
print ('Введите первое число: ');
read (a);
print ('Введите второе число: ');
read (b);
c := a
´ b;
print ('Произведение ', a, ' и ', b, ' равно ', c);
end
Оператор read считывает введенные с клавиатуры ASCII-сим-
волы и преобразует их в числа с плавающей точкой.
Очень важной структурным понятием в языках высокого
уровня является цикл (loop), позволяющий многократно вы-
полнить один и тот же фрагмент кода. Допустим, вы хотите
написать программу, вычисляющую кубы чисел 3, 5, 7 и 9.
Выглядеть она будет примерно так:
450
Глава двадцать четвертая
begin
real a, b;
for a := 3, 5, 7, 9 do
begin
b := a
´ a ´ a;
print (‘Куб числа ', a, ' равен ', b);
end
end
В операторе for переменной a присваивается значение 3, а за-
тем выполняется оператор, следующий за ключевым словом
do. Если таких операторов несколько (как у нас), их нужно раз-
местить между ключевыми словами begin и end. Операторы
между двумя этими словами называются блоком (block). Те же
операторы выполняются для значений переменной a 5, 7 и 9.
У оператора for есть и другой вариант. Следующая програм-
ма вычисляет кубы всех нечетных чисел от 3 до 99:
begin
real a, b;
for a := 3 step 2 until 99 do
begin
b := a
´ a ´ a;
print ('Куб числа ', a, ' равен ', b);
end
end
В операторе for переменной a присваивается значение 3, а затем
выполняется блок операторов, идущий за ключевым словом do.
При следующем выполнении цикла значение переменной a уве-
личивается на значение, указанное после ключевого слова step,
т. е. на 2. Блок операторов выполняется с переменной a, равной
5. Затем значение переменной a увеличится еще на 2. Выполне-
ние цикла завершится, когда значение a станет больше 99.
Синтаксические правила языков программирования обыч-
но не допускают никаких исключений. В АЛГОЛе 60, например,
за ключевым словом for может идти только имя переменной.
Другой важный элемент языка программирования — услов-
ные структуры, позволяющие организовать выполнение опе-
451
Языки высокие и низкие
ратора или блока операторов только при соблюдении опреде-
ленного условия. В приведенном ниже примере используется
встроенная функция АЛГОЛа sqrt, вычисляющая квадратный
корень. С отрицательными числами она не работает, что учи-
тывается в программе.
begin
real a, b;
print ('Введите число: ');
read (a);
if a < 0 then
print('Извините, введено отрицательное число.');
else
begin
b := sqrt(a);
print (‘Квадратный корень из ', a, ' равен ', b);
end
end
Символ < соответствует математическому знаку «меньше».
Если пользователь ввел отрицательное число, выполняется
первый оператор print в структуре if. Если введенное число
больше 0 или равно ему, выполняется блок, содержащий вто-
рой оператор print.
До сих пор все переменные в этой главе использовались для
хранения одиночных значений. Но в одной переменной их мож-
но хранить и несколько. Такая переменная называется массивом
(array). В программе на АЛГОЛе массив описывается так:
real array a[1:100];
Здесь переменную a предполагается использовать для хране-
ния 100 чисел с плавающей точкой, называемых элементами
массива. Для обращения к первому элементу массива исполь-
зуется обозначение a[1], ко второму — a[2], к последнему —
a[100]. Число в скобках называется индексом (index).
В следующей программе вычисляются квадратные корни
всех чисел от 1 до 100. Результаты вычислений сохраняются в
массиве, а затем выводятся на печать.
452
Глава двадцать четвертая
begin
real array a[1:100];
integer i;
for i := 1 step 1 until 100 do
a[i] := sqrt(i);
for i := 1 step 1 until 100 do
print ('Квадратный корень из ', i, ' равен ', a[i]);
end
Помимо массива, в программе описана целая переменная
i. В первом цикле каждому элементу массива присваивается
значение квадратного корня из его индекса. Во втором цикле
значения элементов массива выводятся на печать.
В дополнение к типам real и integer в АЛГОЛе имеется так-
же тип Boolean. Переменные этого типа могут принимать лишь
два значения: true и false. Массив булевых переменных помо-
жет нам написать последнюю в этой главе программу, кото-
рая составляет список простых чисел с помощью алгоритма,
известного как решето Эратосфена. Эратосфен (около 276–196
до н. э.) был библиотекарем в знаменитой Александрийской
библиотеке и более всего прославился вычислением длины
окружности Земли.
Простыми называют целые числа, которые без остатка де-
лятся только на себя и на 1. Самое маленькое простое число —
2. Оно же — единственное четное простое число. Далее идут 3,
5, 7, 11, 13, 17 и т. д.
В методе Эратосфена рассматривается ряд целых чисел,
начинающийся с двойки. Сначала из списка вычеркиваются
все числа, кратные наименьшему простому числу (2), т. е. все
четные числа, кроме самой двойки. Следующее по порядку
простое число — 3. Вычеркиваем из списка все числа, кратные
ему. За тройкой идет 4, но это число уже вычеркнуто, так как
оно четно. 5 — опять простое число, и мы вычеркиваем из спис-
ка все числа, которые без остатка делятся на 5. По мере того
как мы будем продвигаться вперед, невычеркнутыми будут ос-
таваться только простые числа.
453
Языки высокие и низкие
В программе на АЛГОЛе для определения всех простых
чисел, меньших 10 000, используется булев массив, индекс ко-
торого принимает значения от 2 до 10 000.
begin
Boolean array a[2:10000];
integer i, j;
for i := 2 step 1 until 10000 do
a[i] := true;
for i := 2 step 1 until 100 do
if a[i] then
for j := 2 step 1 until 10000
¸ i do
a[j
´ j] := false;
for i := 2 step 1 until 10000 do
if a[i] then
print (i);
end
В первом цикле всем элементам булева массива присваивает-
ся значение true, т. е. изначально программа полагает, что про-
стыми являются все числа. Во втором цикле переменная i про-
бегает значения от 2 до 100 (квадратный корень из 10 000). Если
значение i — простое число (это значит, что a[i] равно true), во
вложенном цикле всем элементам массива с номерами, крат-
ными i, присваиваются значения false (эти числа простыми не
являются). В последнем цикле на печать выводятся номера всех
элементов массива, равных true, т. е. все простые числа.
Иногда приходится слышать споры о том, является про-
граммирование наукой или искусством. С одной стороны, вы
вспоминаете о приятеле, получившем степень магистра ком-
пьютерных наук, с другой стороны, на полке у вас стоит зна-
менитая книга Дональда Кнута (Donald Knuth) «Искусство
программирования». Физик Ричард Фейнман писал так: «Я бы
сказал, что программирование сродни машиностроению —
всего-то нужно заставить что-то сделать что-то».
Попросите 100 человек написать программу для печати
простых чисел, и вы получите 100 разных решений. Даже те,
454
Глава двадцать четвертая
что воспользуются для решения решетом Эратосфена, напи-
шут программу иначе, чем я. Если бы программирование было
наукой, вряд ли у одной задачи было бы так много правиль-
ных решений, а неверные решения были бы более очевидны.
Зачастую программирование связано с творческими озарени-
ями и интуитивными решениями, и это сближает его с искус-
ством. И все-таки процесс проектирования и создания про-
граммы не отличается существенно, скажем, от строительства
моста.
Первыми программистами были в основном ученые и ин-
женеры, которые умели формулировать свои задачи на язы-
ке математики, положенном в основу ФОРТРАНа и АЛГОЛа.
Однако на протяжении всей истории языков программирова-
ния неоднократно предпринимались попытки разработать
язык, который могли бы использовать и люди, не столь близ-
ко знакомые с математикой.
Одним из первых языков, специально предназначенных для
бизнеса, был КОБОЛ (COBOL), созданный в конце 1950-х ко-
митетом из представителей промышленности и Министерства
обороны США. КОБОЛ широко применяется и по сей день.
Его название расшифровывается как «COmmon Business
Oriented Language» (язык, ориентированный на общие коммер-
ческие задачи). Одно из основных требований, предъявляв-
шихся к КОБОЛу, заключалось в том, чтобы менеджеры, сами
не занимавшиеся программированием, могли хотя бы читать
программы и убеждаться, что они делают именно то, что дол-
жны делать (чего на практике, конечно, не бывает).
В КОБОЛе имеются обширные возможности по чтению
записей (records) и созданию отчетов (reports). Записью в про-
граммировании называется собрание взаимосвязанных сведе-
ний. Например, страховая компания может вести базу данных
с информацией о проданных полисах. Отдельные элементы
этой базы и есть записи, в которых хранятся имя клиента, дата
его рождения и другие сведения. Поначалу для хранения ин-
формации в программах на КОБОЛе использовали 80-столб-
цовые перфокарты IBM. Для максимальной экономии места в
номере года на картах зачастую указывались только две пос-
ледние цифры, что позже отчасти обусловило знаменитую
«проблему 2000 года».
455
Языки высокие и низкие
В середине 1960-х в IBM разработали для компьютеров
System/360 язык PL/I (Programming Language I, язык програм-
мирования №1). Предполагалось, что в PL/I будут объедине-
ны модульная структура программ на АЛГОЛе, обширный
математический аппарат ФОРТРАНа и средства КОБОЛа для
работы с записями. Но уровня популярности ФОРТРАНа или
КОБОЛа этот язык так и не достиг.
Компиляторы ФОРТРАНа, АЛГОЛа, КОБОЛа и PL/I созда-
вались и для домашних компьютеров, но ни один из них не
оказал на эти машины такого влияния, как БЕЙСИК.
Язык БЕЙСИК (BASIC, Beginner’s All-purpose Symbolic
Instruction Code, универсальный символьный программный
код для начинающих) разработан в 1964 г. Джоном Кемени
(John Kemeny) и Томасом Курцем (Thomas Kurtz) из Дартмут-
ского университета. Большинство студентов в Дартмуте не
были ни математиками, ни инженерами, поэтому их не сто-
ило заставлять возиться с перфокартами или сложными язы-
ками программирования. Вместо этого студент, сидя перед
терминалом, набирал простую программу прямо на экране.
Если строка начиналась с номера, она считалась строкой про-
граммы на БЕЙСИКе. Строка без номера считалась командой
для системы. Например, командой SAVE пользователь сохра-
нял программу на диске, командой LIST — выводил ее на эк-
ран, а RUN — компилировал и запускал. Самое первое печат-
ное руководство по БЕЙСИКу начиналось с такой программы:
10 LET X = (7 + 8) / 3
20 PRINT X
30 END
В отличие от АЛГОЛа в БЕЙСИКе программист не должен был
указывать тип переменной. Большинство переменных по
умолчанию считались вещественными.
Во многих последующих реализациях БЕЙСИКа исполь-
зовались не компиляторы, а интерпретаторы (interpreters). Я
уже говорил, что компилятор считывает файл с исходной про-
граммой целиком, а затем создает исполняемый файл. Интер-
претатор считывает программу оператор за оператором и сра-
зу выполняет их. При этом исполняемый файл не создается.
Разрабатывать интерпретаторы проще, чем компиляторы, но
работает интерпретируемая программа, как правило, медлен-
456
Глава двадцать четвертая
нее скомпилированной. На домашних компьютерах дебют
БЕЙСИКа состоялся в 1975 г., когда два приятеля Билл Гейтс
(Bill Gates) (род. 1955) и Пол Аллен (Paul Allen) (род. 1953) на-
писали интерпретатор БЕЙСИКа для компьютера «Альтаир
8800». Этот интерпретатор стал первым продуктом основан-
ной ими корпорации Microsoft.
Язык программирования Паскаль (Pascal) унаследовал
структуру АЛГОЛа и средства КОБОЛа для работы с запися-
ми. Он разработан в конце 1960-х швейцарским профессором
информатики Николасом Виртом (Niklaus Wirth). Среди про-
граммистов компьютеров IBM PC Паскаль был очень популя-
рен, правда, только в одной специфической реализации —
Turbo Pascal фирмы Borland. Эта программа, написанная Ан-
дерсом Хейлсбергом (Anders Hejlsberg) из Дании (род. 1960),
поступила в продажу в 1983 г. Она представляла собой интег-
рированную среду разработки (Integrated Development
Environment, IDE) — текстовый редактор и компилятор были
объединены в единую программу, что существенно облегчало
разработку кодов. На больших компьютерах интегрированные
среды использовались задолго до этого, но с Turbo Pascal на-
чалось их пришествие на персональные компьютеры.
На Паскале частично основан язык программирования Ада,
разработанный для Минобороны США. Он назван в честь Ав-
густы Ады Байрон, которую я упоминал в главе 18, рассказы-
вая об Аналитической Машине Бэббиджа.
И наконец — С (Си). Этот чрезвычайно популярный язык
был создан в 1969–1973 гг. в основном усилиями Денниса Рит-
чи (Dennis Ritchie) из Bell Telephone Laboratories. Часто спра-
шивают, откуда взялось название С. Ответ прост: его предше-
ственником был язык В, который в свою очередь был упро-
щенным вариантом BCPL (Basic CPL), основанного на CPL
(Combined Programming Language, комбинированный язык
программирования).
Помните, я говорил в главе 22 о переносимости ОС UNIX?
В те времена ОС, как правило, писались на языке ассемблера
для конкретного процессора. В 1973 г. UNIX была написана
(точнее, переписана) на С, и с тех пор язык и система идут по
жизни рука об руку.
Одно из основных качеств программы на С — краткость.
Например, для указания границ блоков используются не клю-
457
Языки высокие и низкие
чевые слова, как в АЛГОЛе или Паскале, а фигурные скобки:
{ и }. Или другой пример. В программах часто возникает необ-
ходимость прибавить к переменной некое число и записать
результат в ту же переменную:
i = i + 5;
В С этот оператор можно сократить:
i += 5;
Если значение переменной нужно увеличить на 1, оператор
становится еще короче:
i++;
На 16-разрядном или 32-разрядном микропроцессоре этот
оператор превращается в единственную машинную команду.
Чуть раньше я говорил, что в большинстве языков высо-
кого уровня нет операций побитового сдвига или булевых опе-
раций над битами. Язык С — исключение из этого правила.
Кроме того, важной особенностью С является поддержка ука-
зателей (pointers), т. е. фактически работы непосредственно с
адресами в памяти. Из-за этой близости к командам процес-
сора С иногда называют языком ассемблера высокого уровня.
Все языки типа АЛГОЛа, т. е. большинство распространен-
ных языков, предназначены для компьютеров с архитектурой
Неймана. Вырваться из пут неймановской модели при разра-
ботке языка нелегко, но еще сложнее убедить других людей
им пользоваться. Один из таких «не-неймановских» языков —
LISP (LIst Processing, обработка списков), созданный в конце
1950-х Джоном Маккарти (John MacCarthy) — используется
при работах в области искусственного интеллекта. Не менее
экзотичен, чем LISP, хотя и не похож на него, APL (A Prog-
ramming Language), созданный также в конце 1950-х Кеннетом
Айверсоном (Kenneth Iverson).
Так или иначе, пока в программировании доминируют язы-
ки типа АЛГОЛа, хотя в последние годы в них внесено несколь-
ко важных усовершенствований, результатом которых стало
появление объектно-ориентированных (object-oriented) язы-
ков. Эти языки очень удобны при работе в графических опе-
рационных системах, о которых речь пойдет в следующей (пос-
ледней) главе книги.
|