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

 

 

Введение

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

Синтаксис

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

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

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

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

Алгоритмические механизмы
(управляюшие структуры)

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

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

Оператор условия

Необходимость его присутствия в языке очевидна. Предоставляет возможность выполнить то или иное действие в зависимости от истинности некоторого логического условия (ЛУ). Практически во всех языках называется if.

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

if (a=4) a=a+1;
else a=b-2; //C++

Оператор множественного выбора

Некоторые алгоритмы требуют выполнения более двух раличных действий в зависимости от некоторого условия (всегда не логического; к примеру от значения некоторой переменной), то есть ветвления более чем на 2 ветки. Реализация такого ветвления с помощью условного оператора нецелесообразна (кто не верит - пусть нарисует). Как следствие в большинство ЯП включен оператор множественного выбора.

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

case c of
3: c: =8;
4: c:= b-3;
else b:= 4;
end; {Pascal}

Параметрический цикл

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

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

Do 100 I=3,8,1
*операторы тела цикла
100 Continue
* Fortran IV

Операторы цикла с условием

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

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

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

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

Do While Not EOF()
&& Тело цикла c предпроверкой
EndDo && FoxPro

Repeat
{Тело цикла с постпроверкой}
Until i=4; {Pascal}

Оператор безусловного перехода

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

157 GoTo 342 (BASIC)

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

Механизмы управления данными

Механизмы управления данными в общем случае делятся на три глубинно взаимосвязанных части: механизмы размещения данных в памяти, механизмы доступа к ним и механизмы пересылки. При этом если последние практически неотличимы в различных ЯП (как следствие простоты), то первые и вторые могут кардинально различаться как в синтаксисе, так и в реализации. Поэтому имеет смысл остановиться лишь на наиболее общих их чертах.

Механизмы пересылки данных

Пердоставляют программисту возможность перенести значение каким-либо образом размещенных в памяти данных в другие (тоже размещенные) данные. Попросту говоря позволяет присваивать одним переменным значение других переменных. Реализуются с помощью либо оператора (Pascal), либо операции (C++). Стоит отметить, что перемещение самих данных (изменение месторасположения переменных) не реализовано практически ни в одном ЯП.

a:= b; {Pascal}
a=b=c=d; //С++

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

Механизмы размещения данных

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

var
a: byte; {Pacal}

float c; // C++

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

struct Point {
int x,y;
char color;} //C++

D: Array[2..5] Of Word; {Pascal}

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

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

Различные языки программирования могут позволять (FoxPro, C++) или не позволять (Pascal) размещать переменные в процессе выполнения программы. Однако, большинство ЯП, в том числе и Pascal, имеют механизмы динамического распределения памяти (ДРП), позволяющие выделять (и освобождать по завершении использования) не занятую никакой программой память для временного использования. При этом доступ к выделенным таким образом участкам памяти производится с использованием указателей.

Механизмы доступа к данным

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

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

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

Point.x = Point.y-3; //C++

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

D[3]:= D[2]+D[4]; {Pascal}

Интерфейсные механизмы

Выполнение любых алгоритмов бесполезно, если оно не сопровождается демонстрацией результатов и вводом исходных данных. Поэтому каждый ЯП, за исключением, пожалуй, С и С++, содержит специальные операторы ввода-вывода. Кроме того, вместо использования стандартных операторов ввода-вывода языка, за счет управления аппаратными средствами ЦВМ, программист может производить ввод и вывод информации более "продвинутыми" способами. Соответственно, для этого необходимы

Механизмы управления аппаратурой

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

 

Механизмы структуризации

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

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

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