Теперь, когда мы установили YaBasic, пришло время разобраться в различиях между интерпретируемыми и компилируемыми языками программирования. Версия Basic, которую мы используем (YaBasic), является интерпретируемой, хотя позже мы посмотрим, можем ли мы применить трюк, позволяющий эмулировать поведение компилятора.
Давайте выясним разницу между интерпретируемыми и компилируемыми языками.
Сначала давайте поймём, что представляют собой низкоуровневые и высокоуровневые языки программирования.
Низкоуровневый язык: Машинные коды (самый первый и самый примитивный уровень):
В информатике программа — это набор инструкций, обрабатываемых компьютером. Эти инструкции выполняются посредством электронных схем, называемых «логическими цепями», работающих по законам булевой алгебры (использующей цифры 0 и 1). Существует два состояния: Истина и Ложь. Значению Истины соответствует напряжение (например, 5 вольт, представляющее цифру «1»). Ложь обозначается напряжением около 0 вольт (представляющим цифру «0»).
Логические цепи решают многие проблемы, выполняя простейшие операции, такие как перенос данных, арифметические действия, сравнения, прыжки к другой позиции в памяти и более сложные инструкции. Используя множество таких простых инструкций, можно составлять сложные программы.
Такой вид языка называют «машинным кодом», и это самый низкий уровень программирования. Термином «низкий» подчеркивается тот факт, что машина непосредственно понимает только бинарные значения (нули и единицы).
Однако для человеческого мозга такой подход крайне неудобен: запросто ошибиться, поставив лишний ноль или единицу, и тогда программа перестанет функционировать должным образом. Раньше для написания короткого фрагмента кода требовались глубокие познания математики и электроники. Более того, каждая модель компьютера имела собственный уникальный машинный код, сильно отличающийся от других моделей.
Пример небольшого участка машинного кода выглядит примерно так (это не реальный фрагмент):
Инструкция | Параметр №1 | Параметр №2 |
10001001 | 11101010 | |
11110011 | 00100001 | 00001111 |
00011100 | 10011100 | 01010100 |
10101100 | 10001100 | |
Первая колонка («Инструкция») определяет команду, которую должна выполнить машина. Например, первая команда (11101010) может означать сдвиг цифр параметра №1 влево, заполнив пустое пространство справа нулём (что эквивалентно умножению на два в двоичном исчислении). Результат помещался в особую область памяти, называемую регистрами. Необходимо учитывать множество деталей при составлении подобного кода.
Хотя сама машина прекрасно понимает такие инструкции, человек испытывает значительные трудности в понимании и написании подобного кода. Даже если специалист, писавший код, знает точно, что делает программа, посторонний человек, пытающийся разобраться в чужом проекте, столкнётся с огромными трудностями.
Ассемблер (низкий уровень, но чуть выше машинного):
Вскоре стало ясно, что машинный код существенно ограничивал развитие программирования. Между машиной и человеком возникла проблема взаимопонимания, которую требовалось решить. Так появился язык ASSEMBLER (ассемблер), служащий промежуточником между человеком и машиной. Он остался абстрактным и не особо удобным, но значительно упрощал дело. Вместо длинных последовательностей единиц и нулей теперь использовались мнемонические символы, соответствующие обычным словам, например, команда «суммировать» стала записываться как ADD, а операция перемещения данных называлась MOV.
Вот пример небольшого участка ассемблерного кода:
Здесь три инструкции:
- MOV копирует значение 31h (шестнадцатеричное число) в регистр AX.
- ADD добавляет к содержимому регистра AX значение 16h.
- INT вызывает специальную заранее прописанную процедуру в микропроцессоре (например, перехват нажатия клавиши, завершение программы, ошибка деления на ноль и т.д.)
Числа теперь отображались в шестнадцатеричной системе (основание 16), что удобнее, чем бинарная запись, хотя тоже сложно воспринимать визуально. Например, число 255 в двоичной системе записывалось как 11111111, а в шестнадцатеричной — просто как FF.
Высокоуровневые языки (Basic, Fortran, C, Java, Python, Cobol, Perl, Ada, Pascal, Delphi и др.):
Высокоуровневые языки широко распространены среди программистов и пользователей. Название «высокий уровень» может вводить в заблуждение, так как эти языки вовсе не являются чрезмерно сложными. Их главная особенность — лёгкость восприятия людьми.
Почему их называют «высокими»?Потому что они написаны человеческим языком. Человек способен понять хорошо составленную программу на таком языке, даже если совсем незнаком с программированием, особенно если он владеет английским языком (языком, на котором чаще всего создаются такие языки программирования).
Например, читая программу на одном из высокоуровневых языков, можно легко понять её общую идею. Однако возникает новая сложность: машина не способна понять инструкции на таком языке. Любая простая команда, такая как вывод символа "A" в позицию экрана (30, 20) на языке Basic:
— это далеко не одна простая команда для компьютера. Машина понимает только последовательность нулей и единиц, и эта одна строчка превращается в десятки или сотни низших инструкций, таких как рисование пикселей на экране.
Поэтому необходима
ТРАНСЛЯЦИЯ из высокоуровневого языка (человеческого языка) в машинный код (понятный машине).
Именно тут становится очевидной разница между интерпретируемыми и компилируемыми языками.
Интерпретируемые языки:
Трансляция «человек → машина» выполняется всякий раз при выполнении программы, инструкция за инструкцией, в режиме реального времени. Результаты трансляции действуют только временно и исчезают после завершения работы программы. Подобно человеческой речи, интерпретация происходит мгновенно, без сохранения результата на бумаге.
Примером интерпретируемого языка является YaBasic, который мы используем. Расширение файлов для таких программ — .Yab, а иногда используется расширение .Bas.
Преимущества интерпретируемых языков:
- Проще в использовании.Недостатки:
- Медленная работа, так как каждый раз при запуске программы требуется заново переводить каждую инструкцию, что замедляет исполнение.
- Требуют больше памяти, так как необходим дополнительный модуль-интерпретатор.
Компилируемые языки:
Трансляция осуществляется ДО запуска программы в отдельном процессе, называемом компиляцией. Используется специальная программа-компилятор, соответствующая конкретному языку и его версии. Процесс компиляции производится единожды, пока не произойдут изменения в исходном коде программы, и тогда компиляцию придётся повторить снова.
Исходник программы остаётся неизменным и называется «исходным кодом» или «source code». Перевод исходного кода в машинный язык компилятором называется «объектным кодом» или «binary executable».
Исполняемые программы обычно имеют расширение .EXE, означающее, что файл готов к выполнению.
Примеры расширения файлов для исходников:
- Basic: .Yab, .Bas (есть версии Basic, поддерживающие компиляцию)
- Pascal: .Pas
- C: .C
- Cobol: .Cob
Преимущества компилируемых языков:
- Исполняемая программа компактна и независима.
- Работает быстрее, так как не требует постоянной трансляции.
Недостатки:
- Необходимость предварительной компиляции, что увеличивает время подготовки программы к исполнению.
Преимущества и недостатки интерпретируемых и компилируемых языков:
Интерпретируемые языки:
+ Преимущества:
- Легче в разработке и тестировании.
− Недостатки:
- Замедляют выполнение программы, так как каждая инструкция транслируется заново при каждом запуске.
- Занимают больше памяти, поскольку одновременно требуется хранить интерпретатор.
Компилируемые языки:
+ Преимущества:
- Быстрая работа программы, так как трансляция была выполнена заранее.
- Нет нужды повторно транслировать программу при каждом запуске.
− Недостатки:
- Длительность начальной стадии компиляции программы, особенно крупных проектов.
- Необходимость повторной компиляции при внесении изменений в исходный код.
Недостатки компилируемых языков
Они немного сложнее в использовании по сравнению с интерпретируемыми языками. Сегодня это не столь актуально, так как современные компьютеры работают очень быстро. Однако раньше ситуация выглядела иначе: на компиляцию средней по размеру программы могло уйти полчаса... и ещё оставалось надеяться, что процесс завершится успешно, ведь ошибки были весьма частыми. В случае возникновения ошибки приходилось проделывать следующий цикл:
- Найти причину ошибки.
- Устранить ошибку.
- Повторно запустить компиляцию, тратя ещё половину часа...
Есть одно исключение, которое хочется упомянуть отдельно: компилируемый язык Pascal в версии Turbo Pascal 4.0 от компании Borland. Прошло уже 30 лет с момента его появления. Turbo Pascal работал настолько быстро, что мог компилировать целых 40 тысяч строк кода всего за десять секунд, создавая впечатление работы с интерпретатором. Это происходило на старых компьютерах с процессорами Intel 286 или 386 конца прошлого века. Современные компьютеры могли бы справиться с той же задачей в считанные секунды, компилируя сотни тысяч строк кода за то же время. Именно Turbo Pascal подарил незабываемые моменты продуктивной работы!
Примечание: Эти воспоминания относятся к временам, когда технология находилась на совершенно другом уровне. Благодаря быстрому развитию процессоров и совершенствованию компиляторов сегодня большинство проблем, связанных с долгой компиляцией, устранены.