C++.Бархатный путь

Элементы программного модуля


Мы переходим к описанию синтаксиса элементов программного модуля, но, прежде всего, определим ещё одну категорию спецификаторов объявления.

СпецификаторОбъявления ::= fctСпецификатор

::= ***** fctСпецификатор ::= inline ::= virtual

fctСпецификатор используется при объявлении и определении функций. Их назначение ещё будет обсуждаться в дальнейшем.

ЭлементПрограммногоМодуля ::= СписокИнструкцийПрепроцессора

::= СписокОператоров

СписокОператоров ::= [СписокОператоров] Оператор

Оператор ::= ОператорОбъявления

::= *****

ОператорОбъявления ::= Объявление

Объявление ::= ОбъявлениеФункции

::= ОпределениеФункции

::= *****



ОбъявлениеФункции ::= [СписокСпецификаторовОбъявления] Описатель

[СпецификацияИсключения];

ОпределениеФункции ::= [СписокСпецификаторовОбъявления] Описатель

[ctorИнициализатор] [СпецификацияИсключения] ТелоФункции

Описатель ::= ИмяОписатель

::= ptrОперация Описатель

::= Описатель (СписокОбъявленийПараметров) ::= Описатель [[КонстантноеВыражение]] ::= (Описатель)

ИмяОписатель ::= Имя

::= *****

ptrОперация ::= * [СписокCVОписателей] ::= [СписокCVОписателей]

СписокCVОписателей ::= CVОписатель [СписокCVОписателей]

CVОписатель ::= const | volatile

ctorИнициализатор ::= *****

СпецификацияИсключения ::= *****

О последних двух нетерминалах позже. КонстантноеВыражение ::= УсловноеВыражение

Свойства константного выражения мы также обсудим позже.

УсловноеВыражение ::= *****

СписокОбъявленийПараметров ::= [СписокОбъявленийПарам] [...] ::= СписокОбъявленийПарам, ...

Следует обратить особое внимание на последнюю БНФ. В ней зафиксировано различие между двумя нетерминалами. Так что СписокОбъявленийПараметров - совсем не то, что СписокОбъявленийПарам. Здесь нет никаких опечаток или ошибок. Первый нетерминал по смыслу шире второго. Три точечки, заключённые в круглые скобочки (...) уже в определённом контексте можно рассматривать как СписокОбъявленийПараметров, но это никак не СписокОбъявленийПарам. Это как раз тот самый случай, когда к нетерминалам имеет смысл относится как к СИМВОЛАМ, а не как к последовательностям подчёркнутых слов.


СписокОбъявленийПарам ::= ОбъявлениеПараметра

::= [СписокОбъявленийПарам,] ОбъявлениеПараметра

ОбъявлениеПараметра ::= СписокСпецификаторовОбъявления Описатель

::= СписокСпецификаторовОбъявления

Описатель

Инициализатор

::= СписокСпецификаторовОбъявления

[АбстрактныйОписатель] [Инициализатор]

АбстрактныйОписатель ::= ptrОперация [АбстрактныйОписатель] ::= [АбстрактныйОписатель] (СписокОбъявленийПараметров) [СписокCVОписателей] ::= [АбстрактныйОписатель] [[КонстантноеВыражение]] ::= (АбстрактныйОписатель)

БНФ, раскрывающая смысл нетерминала АбстрактныйОписатель, также проста, как и все прочие БНФ. Достаточно беглого взгляда, чтобы понять, что в роли этого самого абстрактного описателя могут выступать операции *, , даже пара символов [], между которыми может располагаться константное выражение. Абстрактный описатель можно также поместить в круглые скобки.

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

ТелоФункции ::= СоставнойОператор

СоставнойОператор ::= {[СписокОператоров]}

Фигурные скобочки - характерный признак составного оператора.

СписокОператоров ::= Оператор

::= СписокОператоров Оператор

Оператор ::= ОператорОбъявления

::= *****

СписокИнструкцийПрепроцессора ::= [СписокИнструкцийПрепроцессора] ИнструкцияПрепроцессора

ИнструкцияПрепроцессора ::= # ::= Макроопределение

::= ФункциональноеМакроопределение

::= *****

Макроопределение ::= #define Идентификатор СтрокаЛексем

ФункциональноеМакроопределение ::= #define Идентификатор (СписокИдентификаторов) СтрокаЛексем

СписокИдентификаторов ::= Идентификатор ::= СписокИдентификаторов, Идентификатор

СтрокаЛексем ::= Лексема ::= СтрокаЛексем Лексема

Составной оператор также называют блоком операторов (или просто блоком).

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

Сейчас мы рассмотрим структуру модуля. На содержательную часть этой "программы" можно не обращать никакого внимания. Сейчас важен лишь синтаксис.



СписокИнструкцийПрепроцессора

СписокОператоров

Макроопределение

Оператор

Оператор

Оператор

Оператор

#define Идентификатор СтрокаЛексем

ОбъявлениеПеременной

ОбъявлениеФункции

ОпределениеФункции

ОпределениеФункции

#define IdHello "Hello…" int *pIntVal[5]; /* Объявлена переменная типа массив указателей размерности 5 на объекты типа int с именем pIntVal. */ СпецификаторОбъявления Описатель; СпецификаторОбъявления Описатель ТелоФункции

СпецификаторОбъявления Описатель ТелоФункции

#define IdHello "Hello…" int *pIntVal[5]; int Описатель (СписокОбъявленийПараметров); float Описатель (СпецификаторОбъявления Имя ) ТелоФункции

unsigned int MyFun2 (int Param1, ...) СоставнойОператор

#define IdHello "Hello…" int *pIntVal[5]; int MyFun1 ( СпецификаторОбъявления , СпецификаторОбъявления АбстрактныйОписатель Инициализатор, ); float MyFun2 (СпецификаторОбъявления ИмяОписатель) ТелоФункции

unsigned int MyFun3 (int Param1, ...) {СписокОператоров}

#define IdHello "Hello…" int *pIntVal[5]; int MyFun1 (float, int *[5] = pIntVal); /* Объявление функции. В объявлении второго параметра используется абстрактный описатель - он описывает нечто абстрактное, а, главное, безымянное, вида *[5]. Судя по спецификатору объявления int, расположенному перед описателем, "нечто" подобно массиву указателей на объекты типа int из пяти элементов (подробнее о массивах после). И эта безымянная сущность инициализируется с помощью инициализатора. Сейчас нам важно проследить формальные принципы построения программного модуля. Прочие детали будут подробно обсуждены ниже. */ float MyFun2 (char chParam1) { СписокОператоров

} unsigned int MyFun3 (int Param1, …) {СписокОператоров}

#define IdHello "Hello…" int *pIntVal[5]; int MyFun1 (float, int *[5] = pIntVal); // Объявление функции. // Определены две функции… float MyFun2 (char chParam1) { extern int ExtIntVal; char *charVal; } unsigned int MyFun3 (int Param1, …) { const float MMM = 233.25; int MyLocalVal; }



Только что на основе БНФ было построено множество предложений, образующих программный модуль. Фактически, наша первая программа ничего не делает. Всего лишь несколько примеров бесполезных объявлений и никаких алгоритмов. Тем не менее, этот пример показывает, что в программе нет случайных элементов. Каждый символ, каждый идентификатор программы играет строго определённую роль, имеет собственное название и место в программе. И в этом и состоит основная ценность этого примера.

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

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

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


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