Организация и функционирование компьютеров
bf1271d8

Компьютерные программы и языки программирования


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

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

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

Развитие программирования связано со слиянием двух ветвей - теорети­ческо­го и практического программирования - и появлением языков програм­миро­вания высокого уровня. Язык программирования высокого уров­ня позволяет отвлечься от конкретного машинного языка конкретного типа компьютеров и конкретного Ассемблера. Такой язык содержит правила записи программ, которые, с одной стороны, достаточ­ны и удобны для описания алгоритмов решения задач, а с другой стороны, толкуются совершенно однозначно и могут быть преобразованы в програм­мы в машинных кодах. Задача программиста заключается в том, чтобы подготовить правильный текст на языке программирования, а остальное возьмет на себя транслятор, который прочтет этот текст, проверит его на соответствие правилам языка и сформирует программу на машинном языке. Естественно, транслятор должен быть свой для каждого типа компьютера.

Языки программирования с описанными выше свойствами называются машинно-независимыми. Отметим, что при повсевместном переходе на языки программирования программой стал называться текст на языке программи­ро­вания, а оттранслированная программа на машинном языке стала назы­ваться машинным кодом или просто кодом.



Забегая вперед, следует сказать, что никто не составляет текст программы целиком вплоть до малейших деталей. В мире уже составлено огромное коли­чество программ, и очень многие из них имеют тождественные фрагменты, выполня­ющие одинаковые подзадачи. Наиболее типичные и употребитель­ные фрагменты уже написаны, отлажены и вставлены в трансляторы в форме так называемых стандартных процедур и функций. Из них составлены библи­о­теки, которыми программист может пользоваться (и обычно пользуется). Поэтому при трансляции программы, составленной программистом, не получается нормального кода: в нем нет тех внешних программ, которые хранятся в библиотеках. Чтобы отличить такой промежуточный продукт от работоспособной программы, он помещается в файл с расширением .obj (object), в то время как окончательный код получает расширение .exe (execute). Соответственно компиляцией называется преобразование текста программы в obj-файл, в то время как образование exe?файла путем сборки кода из нескольких фрагментов называется редактированием связей (линков­кой на жаргоне программистов, от английского Link).

Первыми языками программирования высокого уровня были COBOL, FORTRAN, затем ALGOL, BASIC, PL/1. Был накоплен определенный опыт в том, как эти языки должны быть устроены. Стало также ясно, что не может быть одного самого лучшего языка, и что при программировании различных задач удобнее использовать разные языки. В настоящее время языки програ­м­мирования делятся на специализированные и универсальные. Специали­зи­рованные используются для решения узкого класса задач. На универсальном языке можно запрограммировать любую задачу (вопрос об эффективности программирования и эффективности программы здесь не ставится). Универ­сальные условно делятся на простые и сложные. Простые имеют ограничен­ный набор средств и за счет этого проще в изучении и дают экономичный код (то есть откомпилированная программа занимает меньше места в памяти и быстрее выполняется).


Сложные имеют большее разнообразие синтаксичес­ких конструкций и зачастую сильно упрощают программирование, но сложны в изучении и дают менее экономичный код. Наиболее употребительными простыми языками являются PASCAL , C (более сложная версия - C++) и BASIC. В нашем курсе мы будем изучать программирование на основе языка PASCAL. Более сложные языки программирования – PL/1, ADA, MODULA-2.

Другое деление языков - деление на императивные и декларативные. Императивные позволяют формулировать алгоритм в форме схемы отдель­ных операций (согласно приведенному выше определению алгоритма). Декларативные языки позволяют формулировать сразу цель программы, а алгоритм ее решения строится автоматически. Естественно, такие языки пригодны не для всех, а только для определенного класса задач, для которых формализован процесс составления алгоритма в классическом смысле. В качестве примера декларативных языков можно привести языки PROLOG и PLANNER.

Язык программирования Паскаль придуман швейцарским ученым Никлаусом Виртом в 1970г. Паскаль вначале предназначался для учебных целей, однако оказался настолько удачным, что широко распостранился среди профессио­наль­­­ных программистов. Его достоинствами являются простота, естествен­ность, хорошая усваиваемость при обучении и эффек­тивность при реализа­ции программ. При этом неоднократно делались попытки улучшить Паскаль за счет полезных (с точки зрения улучшателей) нововведений. В результате для Паскаля, как и для других языков програм­мирования, стала актуальной проблема приведения языка к единому стан­дарту, иначе терялось главное достоинство языка высокого уровня - уни­версальность и переносимость. Этот стандарт был создан в 1983г (стандарт ISO 7185 - 83). В этом стандарте зафиксированы те конструкции и термины Паскаля, которые должны присутствовать в каждой реализации и не могут быть изменены.

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


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

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

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


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

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


Содержание раздела