Правильная ссылка на эту страницу
http://az-design.ru/Support/SoftWare/Delphi/Pascal/001b1004.shtml

4. СРАВНЕНИЕ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ АДА, ПАСКАЛЬ, СИ

А.Эванз, мл.2{Фирма Tartan. Laboratories. Питтсбург, США}

4.1. ВВЕДЕНИЕ

       Несмотря на то, что в настоящее время программист, занимающийся системным программированием, располагает возможностью выбора языка программирования из весьма широкого спектра, у него зачастую не хватает информации для правильного осуществления такого выбора, поскольку в качестве такой информации он, как правило, использует слухи, циркулирующие среди достаточно узкого круга его коллег. Настоящая статья посвящена исследованию трех языков программирования высокого уровня и сравнению предоставляемых ими возможностей, а также оценке того, насколько хорошо они соответствуют требованиям, предъявляемым к языку программирования задачами системного программирования.
       Во введении кратко описаны рассматриваемые языки программирования в порядке уменьшения их размера и сложности. Несколько более подробно эти языки программирования описаны в разд.4.2 настоящей статьи, содержащем также ссылки на необходимые источники.
       Язык Ада. Язык Ада представляет собой очень большой язык программирования с большим набором возможностей, предоставляемых в распоряжение программиста. Язык Ада был разработан при финансовой поддержке Министерства обороны США для решения определенного класса задач, входящих в более широкий класс задач системного программирования. Уже объявлено о вводе в эксплуатацию нескольких трансляторов с языка Ада, обеспечивающих трансляцию программ, написанных на нем и использующих любые его возможности. Ряд аналогичных трансляторов с языка Ада находится в завершающей стадии реализации. Кроме того, существует большое число трансляторов с языка Ада, входным языком для которых является то или иное его подмножество.
       Язык Паскаль. Язык Паскаль был разработан в качестве средства обучения основным концепциям программирования. Язык Паскаль не только оказался удобным и надежным средством решения этой задачи, но и зарекомендовал себя как удобное средство для решения прикладных задач, существенно расширив таким образом предначертанную ему его автором область применения.
       Язык Си. Язык Си был разработан в качестве простого языка программирования, удобного для решения задач, системного программирования. Одной из идей, лежащих в его основе, была идея максимального приближения программиста к уровню используемых аппаратных средств с сохранением всех преимуществ языка программирования высокого уровня, что обеспечивает, с одной стороны, мобильность программного обеспечения, создаваемого на таком языке программирования, а с другой — эффективность этого программного обеспечения.
       Хотя рассматриваемые языки программирования сильно отличаются друг от друга, в том числе и до размеру, тем не менее каждый из них может рассматриваться в качестве претендента на роль языка программирования для решения широкого класса задач системного программирования.
       Обсуждение рассматриваемых в данной статье языков программирования осуществляется с точки зрения их использования в определенной области применения, а именно для решения задач системного программирования. Говоря об этой области применения, автор предполагает, что задачи системного программирования обладают большинством или даже всеми нижеперечисленными характеристиками:
       Большой размер. Большой размер задач в рассматриваемой области применения проявляется в двух аспектах:
       1. Размер решаемой задачи слишком велик для того, чтобы один человек был в состоянии охватить и понять всю задачу целиком (естественно, речь идет об уровне с высокой степенью детализации), что выдвигает на первый план вопросы взаимодействия программистов друг с другом.
       2. Размер решаемой задачи слишком велик для того, чтобы производить написание, а потом и трансляцию единого (единственного) исходного модуля, что выдвигает на первый план вопросы взаимодействия раздельно транслируемых исходных модулей.
       Влиянию, оказываемому большим размером решаемых задач, в настоящей статье уделяется особое внимание.
       Доступ к аппаратным средствам. Очень часто решаемая задача требует непосредственного доступа к используемым аппаратным средствам. Такой доступ может заключаться в.проверке или установке значений определенных ячеек памяти или определенных регистров, генерации соответствующих шаблонов и масок, в использовании структур данных, структура и местоположение которых в памяти диктуются внешними факторами, а не программистом, и т.д.
       Эффективность. Вся решаемая задача или по крайней мере некоторая ее часть должна быть реализована максимально эффективно, имея в виду функционирование используемых аппаратных средств. Например, возможность использования большого программного комплекса может определяться пропускной способностью (а значит, эффективностью реализации) его ключевого компонента.
       Мобильность. Поскольку может потребоваться перенос из одного окружения в другое, то необходимо минимизировать затраты на осуществление такого переноса1{Некоторые специалисты утверждают, что разумной целью разработки любого языка программирования высокого уровня является сведение этих затрат практически к нулю. Автор отвергает такую точку зрения как нереалистичную, понимая,что затраты эти нельзя свести практически к нулю, а можно свести лишь к некоторому разумному уровню. Абсолютно очевидно, что программы, взаимодействующие непосредственно с аппаратными средствами, потребуют внесения в них хоть каких-то изменений при переходе к использованию других аппаратных средств.}.
       Полнота. Максимально возможная часть решаемой задачи (в идеале — вся решаемая задача) должна быть реализована на языке программирования высокого уровня. При этом необходимо учесть, что может потребоваться работа реализуемого программного комплекса на "голой" ЭВМ, т.е. без операционной системы.
       Говоря о большом размере задачи, автор хотел бы подчеркнуть, что он имел в виду скорее очень большой размер задачи, такой большой, что для ее решения требуются десятки программистов, а размер результирующего программного комплекса измеряется многими сотнями тысяч строк. Автор также хотел бы отметить, что возможность и удобство разработки столь больших программных комплексов далеко не всегда является одной из целей, преследуемых разработчиками нового языка программирования (чем речь пойдет также в разд.4.2).
       В настоящей статье оцениваются три языка программирования высокого уровня, каждый из которых может быть использован для решения тех больших задач, о которых речь шла выше. Проводимое автором сравнение носит в значительной степени описательный характер, не являясь явным противопоставлением одного языка программирования (или каких-либо его возможностей) другому (или его возможностям). В тех местах статьи, где автор выражает свое личное мнение, он всегда говорит об этом явно.
       Теперь настала очередь сделать замечение по поводу термина "безопасность". Одной из важнейших целей, декларированных при разработке многих языков программирования, являлась минимизация вероятности сделать ошибки определенного типа в процессе использования языка программирования. Автор ничего не имеет против такой цели до тех пор, цока ее достижение не становится навязчивой идеей, начиная оказывать чрезмерное влияние на язык программирования. Например, на ранних этапах разработки языка Ада была сделана попытка полностью отказаться от возможности использования побочных эффектов. Позднее (и по мнению автора, к счастью) эта попытка была признана нецелесообразной, поскольку полный отказ от возможности использования побочных эффектов оказывает слишком плохое влияние на язык программирования в целом. Таким образом, автор не имеет ничего против поиска путей повышения безопасности языка программирования, а возражает лишь против чрезмерного уменьшения числа полезных возможностей, предоставляемых языком программирования в угоду попыткам обеспечить защиту от беспечности программиста.
       Сделанное выше замечание вынуждает автора объективности ради признать существование двух крупных программных комплексов, реализованных на языке Евклид [108], в котором в угоду безопасности запрещено использование побочных эффектов и ряда других средств, которые считаются необходимыми для решения задач системного программирования. Одним из примеров использования языка Евклид для решения задач системного программирования является транслятор с одного из языков программирования [186], другим — операционная система TUNIS [29]1{Использование языка Евклид для реализации операционной системы TUNIS описано также (и более подробно) в книге [Д13]. - Прим. ред.}. Необходимо отметить, что "ограничения" языка Евклид не были расценены использовавшими его программистами как серьезные недостатки.
       Оставшаяся часть этой статьи построена следующим образом: разд.4.2 содержит краткое описание истории создания каждого из рассматриваемых языков программирования, разд.4.3 содержит сравнение некоторых возможностей, предоставляемых рассматриваемыми языками программирования, разд.4.4 посвящен исследованию металингвистических вопросов, анализу целей и философии, определяющих, каким будет разрабатываемый язык программирования, наконец, разд.4.5 содержит выводы.

4.2. ИСТОРИЯ И ПРЕДПОСЫЛКИ

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

4.2.1. ЯЗЫК АДА

       Язык Ада безусловно представляет собой результат самого серьезного и крупного из проектов разработки нового языка программирования из предпринимавшихся когда-либо. В 1975г. Министерство обороны США наконец осознало, что его расходы на программное обеспечение встроенных систем2{Министерство обороны США определяет встроенную систему как аппаратно-программный комплекс, предназначенный для решения задач, отличных от проведения расчетов. Примерами встроенных систем могут служить системы управления, применяемые в авиации, системы управления артиллерийским огнем, цифровые системы передачи информации и т.д. Системы такого типа чрезвычайно широко используются Министерством обороны США.} огромны — составляют сотни миллионов долларов в год и продолжают быстро расти. Большая часть программного обеспечения встроенных систем в то время реализовывалась на языках ассемблера. Это приводило не только к высокой стоимости разработки и реализации этого программного обеспечения, но и к непомерно большим расходам на его сопровождение в течение достаточно долгого цикла его жизни, составляющего 10—15, а иногда и более, лет. Рабочая группа по языкам программирования высокого уровня, созданная для изучения возможности использования одного из языков программирования высокого уровня для решения задач разработки и реализации программного обеспечения встроенных систем, инициировала длительный процесс определения требований к такому языку программирования, завершившийся в 1978г. выпуском документа [158]. Незадолго до окончания работ по выработке требований к такому языку программирования был объявлен конкурс на язык программирования, отвечающий сформированным в документе [158] требованиям. Этот конкурс завершился появлением в июле 1980г. спецификаций языка Ада [154]. Язык Ада был разработан международным коллективом программистов фирмы Honeywell и ее французского филиала Cii-Bull, которым руководил Ж.Ишбиа. Тем, кто интересуется подробностями истории создания языка Ада, автор рекомендует познакомиться с работой [30], написанной коллективом специалистов, курировавших разработку языка Ада со стороны Министерства обороны США.
       Итак, впервые1{Впервые, если не считать предварительной версии [152]. — Прим. ред.} спецификации языка Ада были опубликованы в июле 1980 г., а в ноябре 1980 г. этот же документ был переиздан с исправлением большинства вкравшихся в него опечаток, но без существенного изменения его содержания. В результате дальнейших работ по развитию языка Ада в июле 1982г. появилась новая версия спецификаций языка Ада [155], которая была принята в качестве стандарта Министерства обороны США (MIL-STD1815). Окончательная версия спецификаций языка Ада появилась в феврале 1983г. [156]. Она послужила основой для нового стандарта Министерства обороны США (MIL-STD 1815А) и стандарта Американского национального института стандартов ANSI [4]. В процессе описанных выше работ по доработке спецификаций языка Ада нумерация разделов документа не изменялась.
       Далее в настоящей статье термин "язык Ада" используется для обозначения языка программирования, соответствующего спецификациям [156], стандарту Министерства обороны США (MIL-STD 1815A) и стандарту ANSI [4]. Документы [4] и [156] полностью совпадают.
       В настоящее время значительное число трансляторов с языка Ада находится в различных стадиях реализации. Реализация некоторых из них осуществляется по заказу Министерства обороны США (а зйачит, и финансируется им) с целью создания трансляторов с языка Ада, обеспечивающих трансляцию программ, написанных на нем и использующих любые его возможности. С ведущимися в этом направлении работами можно ознакомиться в ряде периодических изданий, таких, например, как Ada LETTERS и Ada TEC (ACM SIGPLAN Technical Committee on Ada2{В выпуске Ada LETTERS за июль-август 1983г. помещена информация о 27 реализациях транслятора с языка Ада, проводимых в США, и 13 реализациях транслятора с языка Ада, проводимых в других странах.}). Кроме того, Министерство обороны США инициировало разработку системы ACVC [61], предназначенной для верификации трансляторов с языка Ада с целью получения сертификата Министерства обороны США, наличие которого является необходимым условием для разработки и реализации программного обеспечения для Министерства обороны США с помощью некоторого транслятора с языка Ада. Ко времени написания настоящей статьи (октябрь 1983г.) три транслятора с языка Ада уже прошли верификацию с помощью системы ACVC и получили сертификат Министерства обороны США.

4.2.2. ЯЗЫК ПАСКАЛЬ

       Язык Паскаль был разработан в 1969г. Н.Виртом. Целью его разработки являлось создание "языка программирования, удобного для обучения программированию...". Впервые язык Паскаль был описан в 1971г. в статье [182], в которой Н.Вирт излагает свою точку зрения на проектирование языков программирования и описывает разработанный им новый язык программирования. В 1974г. была опубликована книга [91], которая на долгие годы стала "официальным" описанием языка Паскаль1{На самом деле здесь опечатка, так как ссылка [91] соответствует второму изданию, вышедшему отдельной книгой в 1978 г. Первое издание книги было опубликовано издательством Springer Verlag в 1974 г. в широко известной серии Lecture Notes inComputer Science. - Прим, ред.}2{К сожалению, существует несколько версий книга [91], датированных одним итем же годом, но несколько отличающихся содержанием. Так, в некоторых из нихничего не говорится об операторе dispose, а в некоторых - говорится.}. Описания языка Паскаль, приведенные в статье [182] и книге [91], существенно отличаются друг от друга.
       В 1982г. был принят стандарт ISO на язык Паскаль [5]. В основном этот стандарт соответствует описанию языка Паскаль, приведенному в книге [91], но обеспечивает более полное и строгое описание ряда моментов, обойденных молчанием в книге [91]. Далее в настоящей статье термин "язык Паскаль" исйользуется для обозначения языка программирования, соответствующего стандарту ISO на язык Паскаль [5].
       Поскольку целью Н.Вирта была разработка языка программирования, удобного для обучения программированию, то язык Паскаль был осознанно сделан, маленьким по своим размерам и в нем было использовано минимальное число концепций. Учитывая эти ограничения, поистине впечатляющим выглядит распространение языка Паскаль на большое число областей применения, не предусмотренных его автором. Примером этого может служить то, что трансляторы с языка Паскаль существуют практически для всех типов персональных ЭВМ. Язык Паскаль несколько проигрывает в сравнении с остальными двумя обсуждаемыми в настоящей статье языками программирования вследствие того, что он является самым старым из всех (тот факт, что язык Паскаль был разработан с целью замены языка Алгол 60, явно датирует время его разработки). В то же время язык Паскаль оказал существенное влияние на многие языки программирования. Особенно сильным было его влияние на язык Ада.

4.2.3. ЯЗЫК СИ

       Язык Си разработан в 1972г. Д.Ритчи в фирме Bell Labs. Язык Си представлял собой дальнейшее развитие языка Би [94], который в свою очередь представляет собой дальнейшее развитие языка BCPL [131]. Язык Си был разработан для программистов, привыкших к использованию языка ассемблера для мини-ЭВМ семейства PDP-11.
       "Официальным" описанием языка Си считается книга [101]. Автор считает, что книга [101] недостаточно полна, чтобы считаться "официальным" описанием языка Си. Термин "официальное" описание будет определен в §4.4.2 настоящей статьи, в котором вопрос о неадекватности книги [101] в качестве "официального" описания языка Си рассмотрен более подробно.
       Обоснование причин разработки языка Си приведено в статье [133]. После первой реализации транслятора с языка Си для мини-ЭВМ семейства PDP-11 в фирме Bell Labs были разработаны и реализованы трансляторы с языка Си для различных типов ЭВМ: Honeywell 600, IBM/370, Interdata 8/32. В настоящее время существует большое число реализаций трансляторов с языка Си для самых различных типов ЭВМ, работающих под управлением различных операционных систем.
       Существует еще один компонент программного обеспечения, который имеет прямое отношение к языку Си и который по этой причине обязательно должен быть упомянут в настоящем обсуждении. Речь идет о верификаторе lint [93], который, как сказано в книге [101] (с.13 русского издания), называется так "в частности, из-за того, что он разбирает программу "по ниткам". Программисты, использующие язык Си, быстро осознали, что практически все реализации трансляторов с языка Си имеют недостатки с точки зрения контроля типов (этот вопрос обсуждается в п.4.3.1.4) и с точки зрения диагностики ошибок. Верификатор lint обеспечивает выявление и диагностику множества настоящих и предполагаемых ошибок, выявление и диагностику которых большая часть трансляторов с языка Си даже не пытается произвести. По этой причине верификатор lint может считаться средством, оказывающим более сильное влияние на усиление строгости языка Си и его стандартизацию, чем трансляторы с языка Си.

4.2.4. ДОПОЛНИТЕЛЬНЫЕ ЗАМЕЧАНИЯ

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

4.3. СРАВНЕНИЕ ВОЗМОЖНОСТЕЙ ЯЗЫКОВ АДА, ПАСКАЛЬ, СИ

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

4.3.1. ТИПЫ ДАННЫХ

4.3.1.1. Базовые типы данных

       Во всех трех сравниваемых языках программирования поддерживается некоторый набор базовых типов данных, используемых как для описания скалярных типов данных, так и для построения агрегированньгх типов данных. Далее производится последовательное обсуждение базовых типов данных.
       Булевский тип. В языках Ада и Паскаль поддерживается отдельный тип данных — булевский, в котором имеется два значения — true и false. В языке Си в явном виде булевский тип отсутствует, а значения true и false обозначаются ненулевым и нулевым значениями соответственно. Такой подход лишает транслятор с языка Си возможности выявления определенного класса ошибок. Так, с точки зрения языка Си каждое из следующих двух выражений корректно и имеет значение true (т.е. ненулевое значение):

1 < 2 < 3
3 < 2 < 1

       Перечислимые типы. Перечислимые типы представляют собой важное средство современных языков программирования, которое было впервые введено в языке Паскаль, и обеспечивают возможность введения программистом выбранных им самим имен для обозначения небольших классов объектов, сопровождаемую автоматическим типовым контролем использования этих имен. Хотя близкий эффект может быть получен, например, в результате использования коротких целых значений, введение перечислимых типов обеспечивает дополнительно возможность выявления на этапе трансляции большого числа ошибок. В языках Ада и Паскаль поддерживаются перечислимые типы, для чего используются практически одинаковые синтаксические конструкции1{Что является следствием сильного влияния, оказанного языком Паскаль на язык Ада.}. Более того, в языках Ада и Паскаль имеется возможность использования поддиапазонов перечислимых типов. Программист, использующий язык Си, лишен всех этих возможностей. Используя препроцессор транслятора с языка программирования Си, можно лишь присвоить символические имена константам (т.е. имеется возможность использования символических констант)2{На самом деле в распоряжении программиста, использующего язык Си, имеются и другие возможности. В частности, следовало бы упомянуть о конструкции typedef, о которой можно прочитать в книгах [101, Д4] и которую автор упоминает на стр.251. - Прим. ред.}. Последние версии трансляторов с языка Си, разработанные в фирме Bell Labs, обеспечивают возможность использования перечислимых типов, однако не обеспечивают необходимого контроля типов3{См., например, [Д11]. - Прим. ред.}.
       Целые типы. В языках Ада и Си обеспечивается возможность использования целых типов различной длины. В языке Паскаль имеется целый тип лишь одной длины и есть возможность использования его поддиапазонов без возможности контроля размера, требуемого для размещения соответствующих значений в памяти.
       В языке Ада каждый из целых типов представляет собой поддиапазон. В стандартной "прелюдии" — предопределенном окружении — имеется возможность определять такие типы, как, например, short_integer или long_integer в соответствии с возможностями, предоставляемыми используемыми аппаратными средствами. В языке Ада также поддерживается возможность использования неотрицательных целых значений — natural и положительных целых значений — positive.
       В языке Си к описателю целого типа int может быть добавлено одно из уточнений — short или long, причем число бит, отводимых в памяти под размещение значений целых типов, определяется используемой аппаратурой и не зависит от желаний программиста. Вообще говоря, может оказаться, что два из трех или даже все три целых типа используют для размещения в памяти соответствующих им значений одно и то же число бит. Поскольку в языке Си отсутствует возможность использования поддиапазонов, то оказывается невозможным выявление ряда ошибок на этапе трансляции.
       Языки Ада и Си предоставляют в распоряжение программиста хоть какие-то средства для управления тем, сколько места в памяти будут занимать значения целых типов. И хотя возможности такого рода, предоставляемые этими языками программирования, весьма существенно отличаются друг от друга по своему уровню (если в языке Ада имеется практически полный контроль, то средства управления в языке Си весьма грубы), тем не менее они адекватны большинству практических приложений. Язык Ада позволяет заложить в программу достаточно информации для того, чтобы гарантировать безопасность и безболезненность переноса программ, а язык программирования Си позволяет заложить в программу достаточно информации для того, чтобы безопасность и безболезненность переноса была очень высоко вероятной в подавляющем большинстве случаев.
       Словосочетание "очень высоко вероятной в подавляющем большинстве случаев", использованное в предыдущем предложении, предполагает, что существуют случаи, когда это не так. Чтобы убедиться в этом, рассмотрим следующий пример, иллюстрирующий сложности, которые возникают при переносе программ, написанных на языке Си, с ЭВМ с более длинным машинным словом на ЭВМ с более коротким машинным словом. Пусть имеется .следующий фрагмент программы, написанной на языке Си:

unsigned int FUNNY;
int X, Y;
. . .
. . .
FUNNY = 0177777;
. . .
X= 0177777 / 3;
Y = FUNNY / 3;

       На первый взгляд кажется очевидным, что значения переменных X и Y, приобретаемые ими в результате выполнения приведенного выше фрагмента программы на языке Си, будут одинаковы. Это действительно так, если используете мини-ЭВМ семейства VAX-11. Однако в случае использования мини-ЭВМ семейства PDP-11 значения переменных X и Y будут отличаться друг от друга. Основная трудность таких ситуаций заключается не в трм, что имеется явная машинная зависимость, и не в том, что сложно добиться соответствующего предупреждающего сообщения от транслятора с языка Си, а в том, что большинство программистов, использующих язык Си, не считает проблемы такого рода сколько-нибудь серьезными. "Все будет работать правильно в подавляющем большинстве случаев" — таков наиболее типичный ответ программиста, использующего язык Си. Это наиболее яркий пример моих опасений, связанных с использованием языка Си.
       Вещественные типы. Во всех трех сравниваемых языках программирования поддерживается возможность использования вещественных чисел. В языке Ада вещественные числа могут использоваться в форме как с плавающей точкой, характеризуемой относительной точностью (число цифр мантиссы), так и с фиксированной точкой, характеризуемой абсолютной точностью. Ни в языке Си, ни в языке Паскаль нет возможности использовать вещественные числа в форме с фиксированной точкой. Из всех трех сравниваемых языков программирования только язык Ада позволяет вычислять интервал погрешности результатов вычислений1{В предположении, что реализация транслятора с языка Ада и исполнительной системы является "корректными".}. В стандартной "прелюдии" имеется возможность определять такие типы, как, например, short_float или long_float, в соответствии с возможностями, предоставляемыми используемыми аппаратными средствами.
       В языке Си в зависимости от требуемой точности и возможностей, предоставляемых используемой аппаратурой, для описания данных, представляющих собой вещественные числа в форме с плавающей точкой, может быть применен один из двух описателей - float или double. В языке Паскаль для этой цели может быть использован единственный описатель real.
       Хотя возможности, предоставляемые в этой области языком Ада, и впечатляют, автор тем не менее сомневается в необходимости наличия столь мощных средств для решения задач системного программирования. С другой стороны, можно отметить определенные недостатки языков Си и Паскаль как языков для программирования встроенных систем, поскольку в таких системах часто возникает необходимость работы с аналого-цифровыми преобразователями и цифро-аналоговыми преобразователями, с которыми намного удобней работать, используя вещественные числа в форме с фиксированной точкой.
       Символьные типы. Во всех трех сравниваемых языках программирования поддерживается возможность использования символьных данных. Имеется также возможность использования строк символов, представляющих собой замаскированные массивы символов. Недостатком языка Паскаль в этой области является отсутствие возможности отличить одиночный символ от строки символов длиной в один символ. Так, в языке Паскаль 'abc' представляет собой строку символов длиной в три символа, а 'х' - строку символов длиной в один символ2{Речь идет о таком различии как между одиночным целым и массивом целых,состоящим из одного элемента.}.
       В языке Ада используется код ASCII, имеющий большое преимущество по сравнению с кодом EBCDIC. В языке Си может быть применен как код ASCII, так и код EBCDIC. Хотя в целях обеспечения возможности использования любых кодов в языке Паскаль ничего не сказано о том, каким именно способом кодируются символы, тем не менее с этой точки зрения язык Паскаль обеспечивает более низкую мобильность по сравнению с двумя другими, рассматриваемыми в настоящей статье языками программирования. В языке Паскаль запрещено использовать строчные буквы латинского алфавита.
       Логический тип. Ни в одном из сравниваемых языков программирования не поддерживается возможность использования данных логического типа (противопоставляемого булевскому типу), таких, например, как битовые строки и операции И, ИЛИ, НЕ и сдвига вправо или влево над ними. В языке Си имеется возможность выполнения всех вышеназванных операций, но они выполняются над данными целого типа. В языке Ада имеется возможность использования массивов булевского типа и применения к элементам этих массивов операций И и ИЛИ.
       Заключение. Язык Ада предоставляет в распоряжение программиста богатый набор базовых типов данных, использование которых не таит в себе никаких потенциальных опасностей. Отсутствие в языке Паскаль средств, эмулирующих или заменяющих использование логических типов-существенный недостаток с точки зрения решения задач системного программирования. Язык Си предоставляет в распоряжение программиста все необходимые для решения задач системного программирования средства, но, обеспечивая возможность выполнения широкого класса операций над данными целого типа (т.е. не выделяя ряда отдельных типов данных), создает потенциально опасные ситуации. Работа с битами в языке Паскаль крайне затруднена или даже совсем невозможна (последнее касается определенных операций).

4.3.1.2. Структурированные типы данных

       Во всех трех сравниваемых языках программирования обеспечивается возможность использования двух структурированных типов данных — массивов и записей1{В языке Си для обозначения того, что в языках Ада и Паскаль называется записями, используется термин "структура", унаследованный из языка BCPL. Везде далее в этой статье будет использоваться только термин "запись", в том числе и для обозначения структур в языке Си.}. В языке Паскаль имеется возможность использования еще одного структурированного типа данных — множеств. Кроме того, во всех трех сравниваемых языках программирования имеется возможность применения в той или иной форме механизма указателей.

Массивы
       Во всех трех сравниваемых языках программирования поддерживается возможность использования массивов объектов произвольных типов, поддерживаемых в каждом из этих языков программирования, включая массивы массивов и массивы записей. В языке Ада имеется возможность использования собственно многомерных массивов. В языках Си и Паскаль возможность использования многомерных массивов обеспечивается за счет возможности использования массивов массивов, т.е. многомерные массивы трактуются как массивы массивов. Наибольший интерес при рассмотрении массивов представляет вопрос о так называемых динамических массивах, т.е. массивах, в которых одна или обе (верхняя и нижняя) границы не известны до момента выполнения программы. Использование таких динамических массивов может потребоваться в одном из трех следующих случаев:
       в качестве формального параметра подпрограммы (процедуры или функции) с установкой границ по значению фактического параметра;
       в качестве локальных данных с установкой границ при входе в область действия;
       в качестве "кучи" с установкой границ по мере выделения памяти для размещения объектов.
       В языке Ада обеспечивается возможность использования динамических массивов в любом из трех перечисленных выше случаев. Стандарт ISO на язык Паскаль [5] запрещает использование динамических массивов вообще, что является его существенным недостатком. В то же время в этом документе определяется уровень 1, на котором разрешается использование динамических массивов в качестве формальных параметров.
       В языке Си подход к использованию массивов привлекает своей оригинальностью. Если X является массивом, то значение X представляет собой адрес нулевого элемента массива X1{Этот подход был впервые применен в языке BCPL и затем перенесен из него в язык Си.} (в языке Си нумерация элементов массивов всегда начинается с нуля). Таким образом, если X есть массив, а N — целое выражение, то следующие четыре выражения эквивалентны и обеспечивают доступ к элементу массива с индексом N:

[X]N  (X+N)*  (N+X)*  [N]X

       Оператор '*' обозначает "содержимое", т.е. обеспечивает доступ по указателю (если Р — указатель, то *Р — это объект, на который этот указатель указывает) . Несколько необычна семантика знака '+' в приведенных выше операциях. Поскольку в этих операциях производится сложение указателя с целым значением, то перед выполнением операции сложения это целое значение умножается на размер объекта (в данном случае на размер элемента массива), так как результат выполнения этой операции сложения должен быть адресом (указателем).
       Хотя в языке Си и требуется указание границ локальных массивов уже на этапе трансляции, тем не менее в случае, если формальный параметр представляет собой одномерный массив, его границы могут быть заранее не указаны. Поскольку в языке Си использование "кучи" не является частью этого языка программирования, то работа с "кучей" в нем может быть реализована с помощью набора соответствующих подпрограмм. Этот набор подпрограмм несложно сделать таким, чтобы он обеспечивал возможность выделения памяти для размещения объектов произвольного размера. При этом, конечно, размер объекта, под который выделяется память, должен быть указан явным образом в качестве фактического параметра, поскольку транслятор с языка Си не имеет возможности вычислять этот размер.

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

Множества
       Хотя множества представляют собой средство, лишь ненамного превосходящее по своим возможностям те, которые могут быть получены в результате использования упакованных массивов булевского типа, тем не менее включение в язык программирования множеств может быть отнесено к существенным его достоинствам. В языке Паскаль поддерживается возможность использования множеств, а в языках Ада и Си — нет. Поскольку стандарт ISO на язык Паскаль [5] не устанавливает нижнюю границу максимального размера множества, который должен обеспечиваться любой реализацией транслятора с языка Паскаль, то при реализации трансляторов с языка Паскаль их авторы вольны в выборе максимально возможного размера множества и часто ограничивают этот размер числом, определяемым числом бит в машинном слове. А это явно недостаточно для многих применений.

Указатели
       Хотя во всех трех сравниваемых языках программирования обеспечивается возможность использования в той или иной форме механизма указателей, авторы языка Ада предпочли использовать вместо термина "указатель" термин "переменная типа ссылки" (variable of an access type). Причина этого, по-видимому, заключается в том, что, используя другое название, авторы языка Ада попытались избежать критики предлагаемого ими средства, поскольку все, что связано с термином "указатель", традиционно подвергается критике и подсознательно ассоциируется с чем-то "плохим".
       В языках Ада и Паскаль обеспечивается возможность использования "кучи" (области памяти, обслуживаемой специальной подсистемой, с помощью которой из этой области можно выделять область памяти для размещения различных объектов и возвращать в эту область память, освободившуюся в результате уничтожения этих объектов). А использование указателей ограничивается лишь возможностью указания на объекты, память для размещения которых выделена из "кучи". В языке Си использование указателей никак не ограничивается — указатели могут указывать на любые объекты. Существуют достаточно серьезные аргументы (имея в виду безопасность программирования) против разрешения использовать указатели для указания на любые объекты, хотя очень часто программисты и отстаивают необходимость такой возможности, апеллируя к практическим потребностям. Более подробно вопросы использования "кучи" обсуждаются в п.4.3.2.3. При использовании ЭВМ, архитектура которых такова, что имеется возможность адресоваться непосредственно к аппаратным регистрам (например, регистрам общего назначения или регистрам ввода-вьюода), возможности, предоставляемые указателями в языке Си, позволяют адресоваться к таким регистрам средствами языка Си (т.е. не прибегая к помощи подпрограмм, написанных на языке ассемблера). В языке Ада можно добиться тех же возможностей, используя спецификации представления — спецификации адреса.

4.3.1.3. Подпрограммы и метки

       Споры о необходимости (или отсутствии таковой) включения в языки программирования объектов типа подпрограмм и меток идут уже давно. Константы этих типов существуют — это те метки, с помощью которых мы привыкли помечать операторы, имена процедур и функций, используемые при их описании. Вопрос заключается в том, можно ли значения типа подпрограмм и меток передавать в качестве параметров или необходимо введение переменных, которые в качестве своих значений могли бы принимать значения типа подпрограмм и меток1{Термин "подпрограмма" соответствует "процедуре" или "функции". Термин "метка" соответствует метке, на которую может быть осуществлен переход с помощью оператора перехода goto, а не метке блока в языке Ада.}.
       Следуя работе [16], введем понятие "класса гражданства" для типов данных следующим образом:
       Первый класс. Тип данных обладает первым классом гражданства, если объекты этого типа данных могут передаваться в качестве параметров. Большинство типов данных обладают первым классом гражданства.
       Второй класс. Тип данных обладает вторым классом гражданства, если объекты этого типа данных могут передаваться в качестве параметров, но не существует переменных, которые в качестве своего значения могли бы принимать значения этого типа данных. Во многих языках программирования подпрограммы и метки обладают вторым классом гражданства.
       Третий класс. Тип данных обладает третьим классом гражданства, если объекты этого типа данных не могут быть использованы вообще. Разработчик языка программирования, планирующий введение в него подпрограмм и меток по первому или второму классу гражданства, сталкивается с двумя основными проблемами: необходимостью обеспечить эффективность и безопасность. Рассмотрим пример. Пусть имеется подпрограмма INNER, описанная в теле другой подпрограммы OUTER; Обычно в языке программирования с блочной структурой в процессе выполнения процедуры INNER обеспечивается доступ к переменным, описанным в охватывающей эту подпрограмму подпрограмме OUTER. Проблема, связанная с обеспечением эффективности, заключается в нахождении способа эффективной адресации к переменным, описанным в подпрограмме OUTER, в процессе выполнения подпрограммы INNER. Существующие пути обеспечения доступа к переменным, описанным в охватывающей подпрограмме, в процессе выполнения охватываемой подпрограммы в языках программирования с блочной структурой ведут к росту накладных расходов как на этапе трансляции, так и на этапе выполнения. Проблема обеспечения безопасности возникает, например, в той ситуации, когда подпрограммы вводятся в язык программирования по первому классу гражданства и значение INNER присваивается переменной, время жизни которой' превышает время жизни блока, содержащего подпрограмму OUTER. В таком случае оказывается возможным .переход к выполнению подпрограммы INNER в то время, когда стек подпрограммы OUTER уже не существует. В общем (Случае ошибки такого рода невозможно диагностировать на этапе трансляции, их можно диагностировать только на этапе выполнения, да и то лишь с очень большим трудом.
       Интересное решение обеих этих проблем, называемое "правилом области действия в языке BCPL", было предложено в языке BCPL. Заключается оно в том, что невозможно получить доступ к любым нелокальным данным, находящимся в стеке вместе с телом подпрограммы. Это любопытное ограничение, придуманное М.Ричардсом и встроенное им в язык BCPL [131], не требует никакой поддержки на этапе выполнения. Более того, такое решение позволяет ввести в язык программирования подпрограммы по первому классу гражданства, не опасаясь роста накладных расходов на этапе выполнения и возникновения опасных ситуаций типа той, что “описана в предыдущем абзаце. С другой стороны, такое решение приводит к тому, что вложенные подпрограммы становится писать существенно менее удобно.
       Контроль типов — это еще один аспект, который интересно рассмотреть в связи с передачей подпрограмм в качестве параметров (т.е. имея в виду введение подпрограмм в язык программирования по первому или второму классам гражданства). В этом случае наибольший интерес представляет вопрос, следует ли производить контроль типов параметров (аналогично тому, как это делается в языках программирования с сильной типизацией) при входе в подпрограмму, представляющую собой в свою очередь формальный параметр.

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

Язык Паскаль
       В языке Паскаль подпрограммы обладают вторым классом гражданства, причем при входе в подпрограмму, представляющую собой формальный параметр, осуществляется контроль типов параметров1{Контроль типов в этом случае является обязательным в соответствии со стандартом ISO на язык Паскаль [5], но обходится молчанием в книге [91].}. Метки в языке Паскаль обладают третьим классом гражданства.

Язык Си
       В языке Си подпрограммы обладают первым классом гражданства2{Точнее говоря, в качестве параметра может быть передан указатель на подпрограмму. Это различие весьма существенно для программиста, но не существенно в настоящем обсуждении.}. Поскольку в языке Си не допускается использование вложенных подпрограмм, то и решаемая с помощью "правила области действия в языке BCPL" проблема не возникает, а значит, нет и потери эффективности. Метки в языке Си обладают третьим классом гражданства, хотя значительное число трансляторов с языка Си позволяет использовать указатели на метки.

Заключение
       Существует большое число задач в различных областях применения, которые наиболее естественно и удобно решаются при использовании вектора Подпрограмм, что допускается только в языке Си3{Точнее говоря, в качестве параметра может быть передан указатель на подпрограмму. Это различие весьма существенно для программиста, но не существенно в настоящем обсуждении.}. И хотя существуют различные способы решения такого рода задач, все они менее удобны, чем использование вектора подпрограмм.
       Точка зрения автора заключается в том, что было бы целесообразно придать подпрограммам первый класс гражданства, но результатом этого ни в коем случае не должно быть сколько-нибудь существенное снижение эффективности и безопасности. Но, к сожалению, эти требования, как правило, противоречат друг другу,

4.3.1.4. Контроль типов

       Учитывая то обстоятельство, что практически все разработчики языков программирования считают, что наиболее предпочтительным является использование языков программирования с сильной типизацией, не удивительно, что все три сравниваемых языка программирования претендуют на то, чтобы считаться типизированными языками программирования. Однако степень типизации и подходы к трактовке типов данных в каждом из трех сравниваемых языков программирования весьма существенно отличаются. Введем понятия типизированного языка программирования и языка программирования с сильной типизацией1{По этому вопросу см. также с.20 статьи А.Фьюэра и Н.Джехани "Сравнение языков программирования Си и Паскаль", помещенной в настоящем сборнике и примечание на с.113 - Прим. ред.}:
       Язык программирования будем называть типизированным, если каждый объект этого языка программирования принадлежит к одному из существующих в нем типов, т.е. типу, известному транслятору с него. Язык программирования будем называть языком программирования с сильной типизацией, если транслятор с него в состоянии осуществить и осуществляет придание каждому объекту некоторого типа и гарантирует невозможность присваивания объекту значения другого типа, чем тип, к которому принадлежит этот объект.
       Все три сравниваемых языка программирования в соответствии с введенными выше определениями являются типизированными языками программирования, но лишь язык Ада может считаться языком программирования с сильной типизацией. Язык Паскаль очень близок к тому, чтобы его можно было считать языком программирования с сильной типизацией, однако все же таковым считаться не может, что обсуждается далее в этом разделе2{По этому вопросу см. также с.20 статьи А.Фьюэра и Н.Джехани "Сравнение языков программирования Си и Паскаль", помещенной в настоящем сборнике и примечание на с.113 - Прим. ред.}. Автор утверждает, что язык Си не является языком программирования с сильной типизацией.
       Некоторые специалисты считают, что сильная типизация не лвляется ни необходимым, ни желательным свойством языка. программирования, используемого для решения задач системного программирования. Автор глубоко убежден в следующем:
       1. Строгий контроль типов на этапе трансляции является обязательным требованием, предъявляемым к современному языку программирования. Более того, строгий контроль типов должен осуществляться и в случае раздельной трансляции, причем как для разделяемых данных, так и для параметров.
       2. Программистам иногда необходима возможность нарушить правила строгого контроля типов.
       3. Правильно спроектированный язык программирования должен обеспечивать специальный механизм для того, чтобы нарушить правила строгого контроля типов, использование которого требует от программиста явноуказать свои намерения.
       4. Каждое использование механизма, обеспечивающего нарушение правил строгого контроля типов, должно быть понятным для любого, кто захочет прочитать программу, в которой используется этот механизм.
       Сказанное выше имеет большое значение для последующего обсуждения.

Язык Ада
       Типовые возможности языка Ада очень богаты, очень мощны и очень сложны. В распоряжение программиста предоставлен большой набор механизмов, имеющих дело с типами (глава стандарта на язык Ада [156], посвященная типам данных, содержит 44 страницы). Разнообразие средств совмещения, которые, в частности, позволяют одной и той же подпрограмме выступать в различных ипостасях (под одним и тем же именем) в зависимости от типов фактических параметров, затрудняет понимание того, как будет использована эта подпрограмма в том или ином случае, и для программиста и для транслятора с языка Ада. Более того, средства совмещения (по крайней мере до некоторой степени) уничтожают некоторые преимущества сильной типизации, поскольку определенные выражения, которые в случае отсутствия средств совмещения, трактовались бы как ошибочные (вследствие несоответствия типов), трактуются при наличии средств совмещения как корректные. Автор считает, что язык Ада в этой его части излишне богат, по крайней мере, если рассматривать его с точки зрения решения задач системного программирования.
       В языке Ада обеспечивается возможность использования механизма явного указания на необходимость нарушения правил строгого контроля типов: предопределенная библиотечная настраиваемая функция UNCHECKED-CONVERSION, позволяющая транслятору с языка Ада рассматривать любое значение как значение любого из допустимых типов. Этот механизм представляет собой идеальное решение проблемы, поскольку обеспечивает требуемую функциональность, несколько громоздок в использовании (с длинными именами) и обеспечивает понимание намерений программиста для любого, кто захочет прочитать программу, в которой используется этот механизм.

Язык Паскаль
       В описании языка Паскаль Н.Вирт называет его языком программирования с сильной типизацией. Однако в языке Паскаль отсутствует механизм, обеспечивающий возможность явного указания намерения нарушить правила строгого контроля типов, хотя для этого и имеются готовые средства — записи с вариантами. Поскольку в языке Паскаль отсутствуют средства, обеспечивающие контроль типов меток вариантов, то поля записей с вариантами представляют собой потенциальную опасность. Эта проблема была рассмотрена Н.Виртом в статье [174], написанной в 1975г. В этой статье Н.Вирт пишет о потенциальной опасности отсутствия средств контроля типов меток вариантов, которое может стимулировать порочную практику программирования. Но обосновывает принятое решение необходимостью обеспечения эффективной реализации трансляторов с языка Паскаль. Автор подозревает, что теперь, восемь лет спустя1{Необходимо помнить, что статья написана в 1981г. — Прим. ред.}, Н.Вирт занял бы противоположную позицию, которую кстати занимает сам автор.
       Проблемы, связанные со строгим контролем типов при раздельной трансляции в языке Паскаль, удается избежать, запретив раздельную трансляцию. И хотя значительное число реализаций трансляторов с языка Паскаль допускает раздельную трансляцию, в каждом отдельном случае при этом используются различные пути достижения этого, обеспечивающие различную степень строгости проверки типов.

Язык Си
       Хотя в языке Си имеются типы данных (в отличие от его предшественника — языка BCPL) и большинство трансляторов с языка Си обеспечивает проведение в том или ином объеме контроля типов, тем не менее утверждать, что язык Си является языком программирования с сильной типизацией было бы неверно. Во всех трансляторах с языка Си проявляется беспокойство размерами объектов (т. е. тем, сколько байт занимает тот или иной объект в памяти), но очень редко в каких из них ощущается озабоченность каким-либо другим аспектом типизации данных. На самом деле основной функцией верификатора lint является выявление нарушений контроля типов, не выявляемых трансляторами с языка Си. Язык Си предоставляет в распоряжение" программиста множество возможностей для нарушения контроля типов. Некоторые из этих возможностей перечислены ниже. Первые две возможности присущи подавляющему большинству реализаций, но не самому языку Си, а две последние возможности присущи самому языку Си.
       При присваивании значений указателям не производится контроля за совпадением типа того объекта, на который указывает в результате этого указатель, и типа того объекта, на который в соответствии со своим описанием должен указывать указатель, поэтому, например, указателю на целое значение может быть присвоено значение указателя на действительное значение.
       При использовании выражений типа Р ->... , где Р является указателем на поле структуры, не производится контроля на совпадение типа поля и типа того объекта, на который в соответствии со своим описанием должен указывать указатель Р. Отсутствует контроль типов параметров функций.
       Использование объединений является еще одним способом нарушения контроля типов. В случае языка Си вопрос о том, как "обмануть транслятор с него", теряет смысл, поскольку большинство трансляторов с языка Си не надо "обманывать" — они просто пропускают огромное число нарушений, ничуть не заботясь об их выявлении.

Заключение
       Язык Ада обеспечивает проведение строгого контроля типов, что позволяет выявить значительное число ошибок на этапе трансляции. В частности, в языке Ада решена важная проблема строгого контроля типов в условиях раздельной трансляции (в языке Паскаль эта проблема отсутствует благодаря запрету раздельной трансляции). Более того, в языке Ада в распоряжение программиста предоставляется механизм нарушения правил строгого контроля типов путем явного указания на необходимость такого нарушения. Это делает намерения программиста понятными для любого, кто захочет прочитать программу, в которой используется, этот механизм.
       Автор убежден, что введение в язык Ада средств совмещения ведет к уничтожению (по крайней мере до некоторой степени) некоторых из преимуществ сильной типизации. Более того, наличие средств совмещения существенно усложняет проблему соответствия типов в целом. С другой стороны, средства совмещения предоставляют программисту возможность, которая оказывается весьма удобной при решении многих задач.
       Хотя язык Си и нельзя считать языком программирования с сильной типизацией, тем не менее программа, написанная на языке Си, в которой не выявлено ошибок верификатором lint, до-видимому, может считаться написанной на языке программирования с сильной типизацией. Это утверждение базируется на заявлении, имеющемся в книге [101] (с.13 русского издания):
       Программы, которые беспрепятственно прошли через "сито" верификатора lint, как правило, не содержат ошибок, связанных с типами, так же как их не могут содержать, скажем, программы, написанные на языке Алгол 68.
       Если это заявление соответствует истине и если считать верификатор lint частью "официального" описания языка Си, то с языком Си дело обстоит не так плохо, как утверждал автор выше. К сожалению, детали проверок, выполняемых верификатором lint, нигде и никак не документированы.

4.3.1.5. Описание переменных и области действия


       Под областью действия описания идентификатора автор понимает часть текста программы, в которой соответствующее описание управляет свойствами объекта с соответствующим идентификатором1{В языке Ада вводится термин "видимость". Понимание этого термина необходимо при изучении языка Ада, но не требуется в настоящем обсуждении.}. Все три сравниваемых языка программирования в значительной степени являются языками программирования с блочной структурой в том смысле, в каком этот термин был введен в работе "Пересмотренное сообщение об алгоритмическом языке Алгол 60" [124]. В языках Ада и Си обеспечивается возможность введения в область их действия идентификаторов извне, что требуется для раздельной трансляции.

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

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

Язык Си
       В языке Си не допускается использование вложенных подпрограмм. В результате отпадает необходимость использования "правила области действия в языке BCPL" (см. §3.1.3), поскольку не возникает проблем, для решения которых было предложено вышеназванное правило, и, соответственно, не происходит никакой потери эффективности. В языке Си область действия может определяться файлом, содержащим исходный текст программы. Статические данные, описанные вне всех подпрограмм, содержащихся в некотором файле, в качестве области действия имеют весь этот файл. Такие данные могут также экспортироваться в другие файлы, что придает им статус глобальности.
       Самое мягкое слово, которое автор может употребить по отношению к синтаксису, используемому для описания типов данных в языке Си, это — "катастрофа". Тип объекта указывается при описании этого объекта путем указания шаблона его использования. Более того, существует три несколько отличающихся друг от друга синтаксически способа описания типов данных: для описания переменных, для описания типов (с помощью конструктора typedef) и для явно указываемых преобразований типов. Не желая вдаваться в детали, автор отсылает читателей к статье [11], в которой содержится достаточно информации по этому вопросу, и в частности утверждается, что документация по языку Си в этом вопросе неполна и нецелостна, что различные трансляторы с языка Си существенно отличаются друг от друга и от документации.

Заключение
       Итак, в языке Паскаль не допускается использование. вложенных блоков, а в языке Си — вложенных подпрограмм. Разработка языка Ада, как это часто бывает, представляет собой попытку обеспечить возможность использования всех типов вложенности (т.е. и блоков и подпрограмм). Автор считает, что решение, принятое авторами языка Afta, является наиболее красивым и правильным.

4.3.2. РАСПРЕДЕЛЕНИЕ ПАМЯТИ И РАЗМЕЩЕНИЕ ДАННЫХ

       Одним из требований, предъявляемых к языку программирования, предназначенному для решения задач системного программирования, является обеспечение полного контроля за размещением данных в памяти. Под полным контролем автор понимает следующее: распределение памяти, размещение данных, механизм "кучи", разделяемые данные.

4.3.2.1. Распределение памяти


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

Язык Ада
       В языке Ада имеется возможность с помощью прагмы PACK дать указание транслятору с языка Ада1{Это указание может быть проигнорировано транслятором с языка Ада.} о необходимости минимизации расходов памяти для размещения полей в записях ценой увеличения времени доступа к ним. Если программист нуждается в более тонком инструменте для управления размещением полей в записях, он должен воспользоваться спецификацией представления для явного указания того, как он хочет разместить поля в записях. В языке Ада обеспечивается возможность полного контроля за размещением полей в записях.

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

Язык Си
       В языке Си программисту предоставлен полный контроль за размещением полей в записях.

Заключение
       В языках Ада и Си обеспечивается возможность достаточного (полного) контроля за размещением полей в записях. В языке Паскаль средства управления размещением полей в записях недостаточны.

4.3.2.2. Размещение данных

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

4.3.2.3. Механизм "кучи"

       Термин "механизм "кучи" используется в настоящем обсуждении для обозначения механизма, позволяющего программисту запрашивать память из некоторой области на этапе выполнения для размещения различных объектов. Для реальных приложений (в .отличие от учебных задач) помимо возможности выделения памяти из этой области необходима также возможность возвращения освободившейся памяти обратно в эту область. В языках Ада и Паскаль обеспечивается возможность использования механизма "кучи". В языке Си такая возможность обеспечивается стандартной библиотекой функций языка Си.
       - В языках Ада и Паскаль запросы на выделение памяти из "кучи" осуществляются с помощью специальных синтаксических конструкций, обеспечивающих обращение к соответствующей библиотечной функции, прямое обращение к которой из программы невозможно, так как в этом случае нельзя провести необходимый контроль типов1{Проблема заключается в том, что в качестве своего значения функция возвращает указатель на выделенную область памяти, а возможность описания "указателя на что угодно" отсутствует. В языке Ада выходом из этой ситуации может быть использование механизма настройки - создание настраиваемой функции.}. В языке Ада обеспечивается возможность инициализации выделяемой из "кучи" памяти, что является его существенным достоинством.
       В языках Ада и Паскаль для запроса на выделение памяти из "кучи" используется конструкция new. В языке Ада эта конструкция представляет собой функцию, возвращающую в качестве своего значения указатель на выделенную область памяти, а в языке Паскаль эта конструкция представляет собой процедуру, в которой указатель на выделенную память является возвращаемым параметром. Автор считает, что подход, используемый в языке Паскаль, значительно менее удачен.
       Более серьезное различие между языками Ада и Паскаль наблюдается в вопросе организации "сборки мусора", под которой здесь понимается процесс возвращения освободившейся (ранее запрошенной из "кучи") области памяти обратно в "кучу", чтобы эта область памяти могла быть впоследствии использована еще раз. Язык Ада явно предполагает проведение "сборки мусора", обеспечивая существование выделенных из "кучи" объектов до тех пор, пока они являются доступными, что, в частности, следует из приводимой ниже цитаты, взятой из разд.4.8(7) стандарта на язык Ада [4]:

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

       Таким образом, возможность повторного использования области памяти, выделенной из "кучи",- для размещения некоторого объекта после того, как этот объект становится недоступным, является необязательной. Но нужды практических приложений (особенно в случае работы в режиме реального времени) требуют, чтобы реализация языка Ада обеспечивала либо проведение "сборки мусора", либо возможность написания программ таким образом, чтобы необходимость проведения "сборки мусора" не возникала.
       В языке Ада имеется предопределенная настраиваемая процедура UNCHECKED_DEALLOCATION, обеспечивающая возможность освобождения области памяти, выделенной ранее из "кучи" для размещения некоторого объекта. Однако стандарт на язык Ада [4] не гарантирует, что область памяти, освобожденная в результате использования предопределенной настраиваемой процедуры UNCHECKED_DEALLOCATION, может быть использована повторно.
       Имеющаяся в языке Паскаль процедура dispose является практически полным аналогом предопределенной настраиваемой процедуры UNCHEC-KED_DEALLOCATION в языке Ада вплоть до того, что первая, так же как и вторая, не гарантирует, что область памяти, освобожденная в результате ее использования, может быть использобана повторно.
       По мнению автора решения, принятые в этой области при разработке языка Ада, являются ошибочными, поскольку требования, предъявляемые к языку программирования, предназначенному для решения задач системного программирования, не допускают использования "сборки мусора"1{Конечно, язык Ада был разработан для создания встроенных систем, а не для решения задач системного программирования. Однако утверждение автора об ошибочности решений, принятых при проектировании языка Ада, базируется, в частности, на уверенности автора в том, что "сборка мусора" неприемлема и для встроенных систем.}. Проблема заключается не в том, что в языке Ада обеспечивается "сборка мусора", а в том, что существенная часть мощности языка программирования теряется, если программист вынужден избегать использования тех его средств, которые требуют проведения последующей "сборки мусора". Автор убежден, что одним из .важнейших требований, предъявляемых к языкам программирования, предназначейным для решения задач системного программирования, является требование возможности осуществлять программирование таким образом, чтобы при выполнении программы не возникали дополнительные накладные расходы (ни по времени выполнения, ни по занимаемой памяти), связанные с автоматической "сборкой мусора", осуществляемой в этом языке программирования. Язык Ада предоставляет в распоряжение программиста средства для управления выделением памяти из "кучи" и возвращением в "кучу" освободившейся памяти (см. разд.4.8(8)-4.8(12) и 13.2(в) и 13.10.1 стандарта на язык Ада [4]), но у автора нет абсолютной уверенности в том, что из текста стандарта на язык Ада [4] следует, что сформулированное в предыдущем предложении требование выполняется. С другой стороны, автор вынужден признать, что он не в состоянии доказать, что это требование не выполняется в языке Ада.

4.3.2.4. Разделяемые данные

       Если некоторая область применения требует параллельной обработки (либо поддерживаемой языком программирования, либо реализуемой программистом), то язык программирования, используемый для решения задач в этой области, должен обеспечивать безопасное использование данных, разделяемых между параллельными процессами1{В языке Ада для использования разделяемых мевду процессами данных предусмотрен механизм рандеву. По этому поводу см. также § 4.3.6.}. Серьезной проблемой в этом смысле является обеспечение гарантии того, что два (или более) процесса не осуществят одновременно модификацию разделяемых данных. И хотя некоторые специалисты (в отличие от автора настоящей статьи) утверждают, что решение этой проблемы должно обеспечиваться самой структурой языка программирования, тем не менее ни в одном из трех сравниваемых языков программирования не было предпринято попытки решения этой проблемы2{Исключением является, пожалуй, лишь язык Ада.}. По мнению автора разработчик языка программирования должен по крайней мере предоставить в распоряжение программиста средства, позволяющие решать вышеназванную проблему.
       Для решения вышеназванной проблемы в распоряжение программиста должны быть предоставлены два вида средств. Во-первых, это средства синхронизации, необходимые, например, для того, чтобы два (или более) процесса могли гарантированно знать, что они не предпринимают одновременной попытки модификации разделяемых данных. (Этот процесс обсуждается также в §4.3.6.) Во-вторых, необходимы средства, обеспечивающие возможность передачи указаний транслятору с соответствующего языка программирования о том, как размещать различные данные. Дело в том, что при отсутствии таких средств современный оптимизирующий транслятор с некоторого языка программирования может, например, для достижения высокой эффективности объектного кода разместить одну из переменных в одном из регистров центрального процессора. А это может оказаться в противоречии с попытками программиста обеспечить безопасное использование этой переменной, являющейся представителем разделяемых данных. Аналогичная проблема возникает и в тех случаях, когда, например, требуется занесение по определенным адресам определенного шаблона (последовательности бит), как это, например, требуется для осуществления ввода-вывода в мини-ЭВМ семейства PDP-11 (а также в других ЭВМ, где ввод-вывод организован аналогично). Хороший оптимизирующий транслятор с некоторого языка программирования может удалить все, кроме одной, попытки занесения одного и того же шаблона (последовательности бит) по одному и тому же адресу, что, конечно же, ни к чему хорошему не приведет. Поэтому необходимо иметь возможность запрещать проведение оптимизации транслятором с используемого языка программирования.
       Как уже говорилось выше, из всех трех сравниваемых языков программирования только язык Ада содержит некоторые попытки решения проблем, связанных с использованием разделяемых данных. В языке Ада имеется прагма SHARED, указывающая транслятору с языка Ада на то, что все перечисленные в прагме SHARED переменные при использовании их в тексте программы нуждаются в синхронизации по доступу (см. разд.9.11 стандарта на язык Ада [4]). При использовании языков Паскаль и Си программист не имеет возможности дать аналогичные указания транслятору с соответствующего языка программирования. Конечно, в языке Паскаль нет средств для организации параллельной обработки и отсутствие такой возможности не является проблемой.

4.3.3. ВЫРАЖЕНИЯ

       Термин "выражение" используется для обозначения такой конструкции языка программирования, которая результатом своего вычисления имеет некоторое значение. Дополнительно к этому значению вычисление выражений может сопровождаться побочными эффектами. Поскольку существует несколько весьма важных отличий между тремя сравниваемыми языками программирования, связанных с тем, что может входить в выражения в каждом из них, автор ограничится лишь обсуждением вопросов о побочных эффектах.
       Если говорить коротко, то в языках Ада и Паскаль отношение к побочным эффектам крайне отрицательное — побочные эффекты не только нежелательны, но даже более того могут считаться чем-то "аморальным", и поэтому использования побочных эффектов следует избегать всеми доступными способами. В противоположность этому язык Си подталкивает к использованию побочных эффектов.
       Начиная с начала 60-х гг., когда было опубликовано "Пересмотренное сообщение об алгоритмическом языке Алгол 60" [124], появилась группа специалистов по языкам программирования, активно выступающих против использования побочных эффектов. Почти все, кому пришлось столкнуться с побочными эффектами, соглашались, что неограниченное использование побочных эффектов не может привести к хорошим результатам. Основные споры разгорелись вокруг вопроса о том, до какой степени структура языка программирования должна быть такой, чтобы затруднить или даже сделать вообще невозможным использование побочных эффектов. Изначально Н.Вирт планировал спроектировать язык Паскаль таким образом, чтобы побочные эффекты не возникали вообще, о чем, в частности, свидетельствует его заявление, сделанное им в статье [182], (с.43 русского издания):

Чтобы исключить побочные эффекты, присваивания нелокальным переменным внутри функций не допускаются.

       Это намерение Н.Вирта, по-видимому, несколько видоизменилось в процессе разработки языка Паскаль, о чем, в частности, свидетельствует следующая цитата из книги [91], (с.67 русского изданий):

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

       Как было отмечено в разд.4.1 настоящей статьи, попытки сделать использование побочных эффектов в языке Ада невозможным или по крайней мере крайне сложным, предпринимавшиеся на ранних этапах работы по созданию языка Ада, не увенчались успехом и были прекращены. В языке Ада сохранились некоторые следы от этих попыток, в частности сохранилось требование, чтобы параметры функций имели класс in и не могли иметь класс in out или класс out.
       Говоря о побочных эффектах в языке Си будет правильным отметить, что язык Си "подталкивает" к использованию побочных эффектов. Хотя в языке Си существует различие между выражениями и операторами, тем не менее конструкция, которая в большинстве языков программирования называется "оператором присваивания", в языке Си называется "выражение присваивания". Это означает, что оператор присваивания в языке Си трактуется как выражение, имеющее некоторое значение (а именно значение правой части оператора присваивания, преобразованное к типу операнда, стоящего в левой части оператора присваивания), причем это выражение (оператор присваивания) может быть использовано в любом контексте, где может быть использовано значение. В языке Си имеются оператор увеличения '++' и оператор уменьшения '--', провоцирующие программиста на написание операторов типа следующего:

a[j++] = b[k++]

       используемого в качестве тела цикла для копирования вектора в в вектор а. В приведенном выше примере в рамках одного оператора присваивания осуществляется сразу три действия: копирование очередного элемента вектора В в очередной элемент вектора а, сопровождающееся увеличением на единицу индекса к элемента вектора в и индекса j элемента вектора а.
       Именно здесь проявляется существующая разница в философиях сравниваемых языков программирования. Точка зрения автора на использование побочных эффектов заключается в следующем: поскольку решение задач системного программирования время от времени требует использования побочных эффектов, то полностью запретить их использование невозможно, однако оно должно стать редким исключением и ни в коем случае не повсеместной практикой. С такой точкой зрения, по-видимому, согласится большинство разработчиков языков программирования1{Честно говоря, автор еще раз вынужден вспомнить о языке Евклид, упоминавшемся им уже в разд.4.1, где, в частности, говорилось об использовании языка Евклид, в котором запрещено использование побочных эффектов, для реализации двух больших программных комплексов. Причем запрет использования побочных эффектов оказался безболезненным для программистов, решавших задачи системного программирования.}. Язык Си находится в серьезном противоречии с этой точки зрения. В конце 60-х гг., когда разрабатывался язык Би (предшественник языка Си), одной из преследовавшихся в этой разработке целей было достижение максимальной эффективности, при реализации на мини-ЭВМ семейства PDP-11. Имея в виду решение этой задачи, введение операций'++'и'--', обеспечивающих очень высокую эффективность при их реализаций на мини-ЭВМ семейства PDP-11, естественно, может быть расценено как красивое и правильное решение. Однако если такое решение могло быть оценено именно таким образом в конце 60-х гг., то с позиций сегодняшнего дня такое решение трудно защищать и оправдывать.

4.3.4. ОПЕРАТОРЫ И СТРУКТУРНОЕ ПРОГРАММИРОВАНИЕ

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

4.34.1. Структурное программирование

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

Язык Ада
       Имеющийся в языке Ада набор структурных операторов полностью соответствует философии структурного программирования.

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

Язык Си
       Язык Си содержит достаточно малый, но тем не менее вполне достаточный для соответствия философии структурного программирования набор средств. Хотя при использовании языка Си необходимость в использовании оператора перехода goto возникает чрезвычайно редко, но есть небольшое число ситуаций, когда без оператора перехода goto обойтись просто невозможно. Одной из таких ситуаций является необходимость преждевременного выхода из оператора цикла в том случае, когда имеется вложенный цикл произвольной глубины, а выход необходим на охватывающий этот вложенный цикл оператор из произвольного по глубине вложенности уровня этого вложенного цикла. Использование оператора завершения break в этой ситуации не помогает, поскольку оператор завершения break позволяет выйти лишь на предыдущий по глубине вложенности уровень этого вложенного цикла. Другой такой ситуацией является обнаружение ошибки при глубоком уровне вложенности обращений к подпрограммам (в некотором смысле эта ситуация аналогична предыдущей). В языке Ада первая проблема решается за счет использования меток блоков, а вторая — за счет возможности обработки исключительных ситуаций.

4.3.4.2. Исключительные ситуации


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

4.3.4.3. Дополнительные замечания


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

Метки
       Использование в языке Паскаль в качестве меток целых чисел, с одной стороны, является иллюстрацией крайне неудачного решения, принятого при проектировании языка Паскаль, а с другой — явно указывает на время, когда он был разработан.

Использование операторных скобок
       В языке Ада для всех конструкций уровня операторов, которые могут считаться открывающей скобкой, требуется использовать соответствующую уникальную закрывающую скобку. Так, для условного оператора if такой уникальной закрывающей скобкой является end if, а для оператора цикла while - end while. В языке Паскаль используются операторные скобки begin и end, а в языке Си - { и }. По мнению автора подход, используемый в языке Ада, отражает перспективные тенденции, направленные на улучшение "удобочитаемости" программ и на повышение удобства программирования.

4.3.5. СТРУКТУРА ПРОГРАММ

       Одной из характеристик задач системного программирования, о которой шла речь в разд.4.1, является очень большой размер этих задач, настолько большой, что, с одной стороны, один человек оказывается не в состоянии охватить и понять всю задачу (естественно речь идет об уровне с высокой степенью детализации), а с другой стороны, оказывается невозможным осуществление написания и последующей трансляции единого (единственного) исходного модуля. Чтобы иметь возможность решать столь большие задачи, необходимо предоставить в распоряжение программиста средства, позволяющие структурировать программу, разбивая ее на более мелкие программные единицы, каждая из которых оказывается настолько мала, что один человек может охватить и понять ее целиком, оказывается возможной ее трансляция в виде единого исходного модуля. В значительной степени возможность работы коллектива программистов над такими большими задачами определяется "экстралингвистическими" средствами, такими, как компоновщики, средства проверки зависимостей, менеджеры конфигураций, генераторы перекрестных ссылок и т.д. Рассмотрение таких "экстралингвистических" средств, конечно, выходит за рамки настоящей статьи. Поэтому автор рассматривает сами языки Ада, Паскаль и Си лишь с точки зрения того, насколько хорошо они помогают (или препятствуют) решению задач большого размера.
       Основным вопросом в этом смысле является вопрос организации контролируемого взаимодействия между раздельно транслируемыми программными единицами. При этом под термином "взаимодействие" автор понимает использование в одних программных единицах объектов, описанных в других программных единицах. Под термином "контролируемость" автор понимает строгий контроль типов (в том смысле, в каком строгий контроль типов определен в п.4.3.1.4) при взаимодействии между программными единицами.

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

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

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

4.3.6. ПАРАЛЛЕЛЬНАЯ ОБРАБОТКА

       Существуют приложения, которые более естественно описываются моделью, содержащей несколько параллельно выполняемых процессов. Для таких приложений в языке Ада предусмотрен мощный механизм задач. Языки Паскаль и Си не содержат средств, ориентированных на такие приложения1{При использовании языка Си в среде операционной системы UNIX в распоряжении программиста имеются средства для организации параллельной обработки, поддерживаемые соответствующими системными вызовами операционной системы UNIX. - Прим. ред.}.
       Для реализации параллельной обработки необходимы три вещи: концепция процесса, средства синхронизации (или взаимного исключения) процессов и управление разделяемыми данными. Модель вычислений без параллельной обработки предусматривает наличие единственного процесса, выполняющего все необходимые действия. Модель вычислений с параллельной обработкой предполагает наличие нескольких процессов (по крайней мере, концептуально), проводимых параллельно для выполнения всех необходимых действий в результате своей совместной работы. При наличии нескольких выполняющихся параллельно процессов необходимо иметь средства для их синхронизации или взаимного исключения, чтобы обеспечить безопасное использование разделяемых данных (т.е. гарантировать невозможность одновременной модификации разделяемых данных). Вопросы, связанные с использованием разделяемых данных, обсуждались уже в п.4.3.2.1.

Язык Ада
       В языке Ада реализована достаточно мощная модель вычислений с параллельной обработкой. Аналогом процесса в языке Ада является задача, а в качестве средств синхронизации процессов (задач) и их взаимного исключения используется механизм рандеву — единственное предназначенное для этих целей средство языка Ада. Тем, кто интересуется этой проблематикой, автор рекомендует подробно изучить гл. 9 стандарта на язык Ада [4].
       Автор считает предложенную в языке Ада модель вычислений с параллельной обработкой в высшей степени элегантной. Сторонники языка Ада доказали, что различные, ставшие уже классическими проблемы, связанные с использованием параллельной обработки могут быть решены с помощью языка Ада эффективно и естественно. Эти же сторонники языка Ада также продемонстрировали способы решения широкого спектра задач, соответствующих различным реальным приложениям. Единственное, что вызывает опасение автора — так это вопросы эффективности. Дело в том, что, поскольку единственным средством взаимного исключения процессов (задач) в языке Ада является механизм рандеву, этот механизм должен использоваться и в тех ситуациях, когда достаточно (да и более естественно) было бы воспользоваться одним из более примитивных механизмов, например семафорами или проверкой и установкой (последний механизм иногда называют занятым ожиданием)2{Очевидно, что решение вопроса о том, что является более естественным, - деловкуса. Разработчики языка Ада находят использование механизма рандеву вполне естественным, автор же не до конца уверен в этом.}. Однако, если окажется, что механизмы задач и рандеву могут быть реализованы в языке Ада с достаточно высокой эффективностью, опасение, высказанное автором вьйие, исчезнет. Подробное обсуждение этих и связанных с ними вопросов проведено в статье [135]. К сожалению, в основе статьи [135] лежит предварительное описание языка Ада [152], которое весьма существенно отличается от стандарта на язык Ада [4].

Язык Паскаль
       Стандарт ISO на язык Паскаль [5] не предусматривает наличия средств для организации параллельной обработки. Однако один из диалектов языка Паскаль, разработанный П.Хансеном и называемый Параллельный Паскаль [24], включает средства для организации параллельной обработки.

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

Заключение
       Из всех трех сравниваемых языков программирования язык Ада является единственным, в котором предусмотрены высокоуровневые средства для организации параллельной обработки. При проектировании языка Ада этому вопросу уделялось особое внимание. Автор обеспокоен тем, что для синхронизации процессов (задач) и их взаимного исключения в языке Ада имеются средства только очень высокого уровня (механизм рандеву), а примитивы более низкого уровня (на основе использования которых и реализован механизм рандеву) оказываются недоступными программисту. Если опыт покажет, что механизм рандеву — это "наиболее правильное" решение, то все в порядке. Если же это окажется не так, то необходимо будет реализовать примитивы более низкого уровня, а для этого придется воспользоваться языком ассемблера (пусть даже в не очень большом объеме). И хотя реализация примитивов более низкого уровня и не бог весть какая сложная задача, тем не менее можно отметить, что в этом отношении язык Ада ничем не лучше языка Си. Кроме того, необходимо отметить, что за сложность средств для организации параллельной обработки в языке Ада приходится расплачиваться ростом накладных расходов на этапе трансляции и, быть может, усложнением исполнительной системы.
       Таким образом, ключевой вопрос может быть сформулирован следующим образом: является ли введение механизма рандеву "наиболее правильным" способом для организации параллельной обработки? Автор не в состоянии ответить на этот вопрос. Более того, автор считает, что ответ на этот вопрос не может быть получен до тех пор, пока не будет накоплен большой опыт по использованию языка Ада вообще и механизма рандеву в частности в различных практических приложениях. Однако автор считает необходимым провести некоторую аналогию, показывающую, что его опасения могут быть ошибочны. Когда появились первые сведения о языке Алгол 60, то программисты и специалисты в области трансляторов с различных языков программирования пришли в ужас от того, насколько неэффективной виделась им реализация таких, например, средств, как стек, динамические массивы, рекурсия. Проведенные в течение нескольких последующих лет исследования привели к нахождению достаточно эффективных способов реализации большинства из таких средств. Причем оказалось, что присущая этим способам неэффективность может считаться вполне приемлемой ценой за предоставляемые в распоряжение программиста возможности и удобства. Поэтому автор не удивится, если и с механизмом рандеву произойдет нечто похожее, но пока, к сожалению, этого еще не случилось. Так что ответа на ключевой вопрос пока нет.

4.3.7. ВВОД-ВЫВОД

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

Язык Ада
       В гл.14 стандарта на язык Ада [4] исчерпывающе описана обширная библиотека подпрограмм, реализующих операции ввода-вывода высокого уровня при работе с файлами, где термин "файл" определяется как внешний упорядоченный некоторым образом набор данных одного и того же типа. Именно то, что файл в языке Ада трактуется как набор данных одного и того же типа, делает невозможным использование подпрограмм из библиотеки, описанной в гл.14 стандарта на язык Ада [4], для осуществления ввода-вывода низкого уровня. В то же время необходимо отметить полную адекватность подпрограмм из этой библиотеки для осуществления ввода-вывода высокого уровня.
       В языке Ада имеется также пакет LOW_LEVEL_IO (см. разд.14.6 стандарта на язык Ада [4]), призванный обеспечить осуществление ввода-вывода низкого уровня. На счет возможностей пакета LOW_LEVEL_IO у автора есть определенные сомнения. Однако, как бы то ни было, наличие в языке Ада возможностей обеспечения доступа по физическим адресам памяти, запрета оптимизации и использования вставок, написанных на языке ассемблера, позволяет реализовать средства для осуществления ввода-вывода низкого уровня.

Язык Паскаль
       Стандарт ISO на язык Паскаль [5] определяет ограниченный набор средств для осуществления ввода-вывода высокого уровня. Этот набор средств похож на набор средств, имеющийся в языке Ада, но он намного менее амбициозен, чем последний. В языке Паскаль невозможно осуществить ввод-вывод низкого уровня и это приводит к ужасным последствиям.

Язык Си
       В язык Си не включено никаких средств ввода-вывода. Вместо этого существует стандартная библиотека ввода-вывода языка Си, описанная в гл.7 книги [101], которая обеспечивает осуществление ввода-вывода высокого уровня. Для реализации средств, обеспечивающих осуществление ввода-вывода низкого уровня, может потребоваться использование языка ассемблера (если необходимо выполнять входящие в состав системы команд ЭВМ команды ввода-вывода).

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

4.3.8. ПРОЧИЕ ВОЗМОЖНОСТИ

4.3.8.1. Настройка и макросредства

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

4.3.8.2. Инициализация

       Очень часто оказывается удобной (и при написании и при чтении программ) возможность инициализации переменной некоторым значением одновременно с ее описанием.
       В языке Ада некоторое начальное значение может быть ассоциировано с переменной при описании последней. Такое начальное значение может быть также ассоциировано с произвольным полем произвольной записи при описании последней. Более того, в языке Ада имеется возможность инициализации запрашиваемой для размещения некоторого объекта из "кучи" области памяти одновременно с этим запросом. В языке Паскалв>возможность инициализации отсутствует, хотя некоторые реализации транслятора с языка Паскаль и допускают инициализацию данных, описанных во внешнем блоке. В языке Си возможна инициализация переменных одновременно с их описанием.
       Возможность инициализации переменных одновременно с их описанием, безусловно, является весьма полезной чертой современного языка программирования, а если эту черту рассматривать с методологической точки зрения, то ее следовало бы назвать желательной чертой современного языка программирования. Автор убежден в том, что возможности современного языка программирования в вопросах инициализации не должны уступать возможностям, предоставляемым языком Ада. Язык Си, как обычно, предоставляет вполне достаточные с точки зрения практических приложений средства инициализации. Отсутствие возможности инициализации в языке Паскаль является достаточно серьезным его недостатком.

4.3.8.3. Утверждения

       Под термином "утверждение" будем понимать выражение, вставляемое в текст программы, которое должно иметь значение "истина" в тот момент, когда управление достигает той точки программы, куда вставлено это выражение. Использование утверждений может облегчить отладку программ, улучшить их "удобочитаемость", а в ряде случаев и обеспечить улучшение объектного кода, генерируемого транслятором с некоторого языка программирования. Ранние версии языка Ада включали возможность использования утверждений, но из окончательной версии языка Ада эта возможность исчезла. Опыт использования языка Евклид [186] показывает, что использование утверждений намного облегчает отладку программ.
       Хотя в языке Си отсутствует возможность использования утверждений, эту возможность легко реализовать с помощью препроцессора транслятора с языка Си. Опыт использования реализованных таким образом утверждений в языке Си показывает, что их использование несложно и облегчает отладку программ.

4.3.8.4. Используемый алфавит (прописные и строчные буквы)

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

4.4. МЕТАЛИНГВИСТИЧЕСКИЕ ВОПРОСЫ

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

4.4.1. РАЗМЕР ЯЗЫКА ПРОГРАММИРОВАНИЯ

       Автор считает полезным вкратце рассмотреть размер и мощность каждого из трех сравниваемых языков программирования. С точки зрения теории все три сравниваемых языка программирования обладают одинаковой мощностью, поскольку любая программа, написанная на одном из них, может быть написана на любом из двух других1{Столь сильное утверждение, вообще говоря, нуждается в доказательстве. -Прим. ред.}. Однако это утверждение, как и какая-нибудь теорема существования, не оказывает никакого влияния на практическое программирование. Например, в практическом программировании очень часто требуется обеспечить возможность доступа по физическим адресам памяти или к регистрам центрального процессора. Это требование лежит в функциональной плоскости, которая, вообще говоря, ортогональна плоскости тех требований, которые определяют мощность языка программирования. Необходимо отметить, что языки Ада и Си обеспечивают возможность доступа по физическим адресам памяти.
       Три сравниваемых языка программирования сильно различаются по своим размерам — от маленьких языков Паскаль и Си до очень большого языка Ада. По-видимому, наиболее интересный комментарий по поводу размеров трех сравниваемых языков программирования можно сформулировать следующим образом:
       Разработчики каждого из трех сравниваемых языков программирования считают, что выбрали "правильный" размер для разработанного ими языка программирования, исходя из его предполагаемых пользователей и возможных областей его применения, а также в соответствии с тем, как эти предполагаемые пользователи и области применения виделись разработчикам до завершения разработки.
       Рассмотрим вопросы, связанные с размерами трех сравниваемых языков программирования, с точки зрения их использования для решения задач системного программирования (обсуждение специфики задач системного программирования приведено в разд.4.1 настоящей статьи), которая была предусмотрена в качестве возможной области применения не для всех трех из них.

Язык Ада
       Автор глубоко убежден в том, что размер языка Ада слишком велик как для решения задач по созданию встроенных систем, так и для решения задач системного программирования. По мнению автора слишком большой размер языка Ада не может быть вменен в вину его разработчикам* а скорее является следствием тех требований к нему, которые были сформулированы как до начала его проектирования, так и в процессе его проектирования. Хорошо известно, что собой представляют различные комитеты. Когда необходимо создать лошадь, результатом работы комитета будет что-то отдаленно напоминающее верблюда. Язык Ада не стал жертвой этого "синдрома комитета", за что, кстати, надо в основном благодарить Ж.Ишбиа, осуществлявшего "жесткое" руководство проектом. Тем не менее перечень требований к языку Ада (имеется в виду документ [158]) оказался перегруженным из-за внесения в него слишком большого числа требований, являющихся результатом заявлений типа следующего:

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

       И хотя по мнению автора любой язык программирования, удовлетворяющий требованиям [158], обязательно будет иметь очень большой размер, возможно, что существует пример, опровергающий эту точку зрения. После завершения Фазы 1 разработки языка Ада группа сотрудников Университета Карнеги—Меллона, озабоченных слишком большим размером четырех языков программирования, представленных на конкурс, предприняла попытку разработать язык программирования небольших размеров, который удовлетворял бы требованиям [158]. Результатом этой попытки явилось появление языка Тартан [142], описание которого уместилось на 23 страницах. При этом язык Тартан в основном удовлетворял требованиям [158]. Необходимо отметить, что одной из целей разработки языка Тартан являлось создание такого языка программирования, описание которого было бы очень небольшим по размеру.
       Является ли создание языка Тартан контрпримером, демонстрирующим ошибочность уверенности автора в том, что любой язык программирования, удовлетворяющий требованиям [158], обязательно будет иметь слишком большой размер? Ответ на этот вопрос зависит от интерпретации словосочетания "в основном" в предложении, в котором говорится о том, что "язык Тартан в основном удовлетворяет требованиям [158]". С одним или двумя существенными исключениями (таким, например, как отсутствие типа с фиксированной точкой в языке Тартан) разработчики языка Тартац выдержали дух требований [158], но не следовали букве требований [158] В то время как участники и наблюдатели разработки языка Ада были в значительно большей степени озабочены следованием букве требований [158], чем обсуждением вопросов о том, что имели в виду разработчики требований, вводя то или иное требование. Поэтому автор продолжает настаивать на том, что слишком большой размер языка программирования вообще и языка Ада в частности является неизбежным результатом следования букве требований [158], что разработчики языка Ада считали необходимым следовать не духу, а букве требований [158] (точнее, этого от них требовали те, кто финансировал работы), что существенно меньший по своему размеру язык программирования в состоянии удовлетворять духу требований [158], Поскольку дальнейшее обсуждение этого вопроса может иметь лишь исторический интерес, то автор считает целесообразным обсуждение завершить.
       Теперь, абстрагируясь от причин, приведших к слишком большому размеру языка Ада, рассмотрим последствия этого. Можно отметить, по крайней мере три негативных последствия этого:
       1. Тому, кто приступает к изучению языка Ада, необходимо преодолеть "очень высокий порог", прежде чем он будет в состоянии писать на немпрограммы, имеющие практическую ценность. В частности, тому, кто собирается использовать язык Ада, необходимо знать все об этом языке программирования (т.е. нельзя ограничиться изучением на первом этапе лишь некоторого подмножества языка Ада), чтобы, например, случайно не воспользоваться одним из служебных слов в качестве идентификатора.
       2. Каждая из многочисленных возможностей языка Ада тесно связана со многими другими его возможностями. Такая комбинаторная сложность усложняет как деятельность программиста, так и транслятор с языка Ада.
       3. Трансляторы с языка Ада из-за слишком большого размера последнего обязательно будут слишком большими, а значит, трудноотлаживаемыми и дорогими.
       Последнее обстоятельство требует более подробного рассмотрения.
       При решении задач по созданию встроенных систем, так же, впрочем, как и при решении задач системного программирования, одним из требований к языку программирования (точнее, к самому языку программирования и к транслятору с этого языка программирования) является необходимость генерации "очень хорошего" (в смысле эффективности) объектного кода. А это возможно лишь при использовании высококачественных оптимизирующих трансляторов с тех или иных языков программирования. Создание и отладка простого (невырококачественного и неоптимизирующего) транслятора с языка программирования X, имеющего большой размер, по трудоемкости сравнимо с созданием высококачественного оптимизирующего транслятора с языка программирования Y, имеющего небольшой размер. Создание высококачественного оптимизирующего транслятора с языка Ада является поистине грандиозной задачей.

Язык Паскаль
       Хотя язык Паскаль и достаточно маленького размера, отсутствие в нем возможности проведения раздельной трансляции и доступа к аппаратным средствам делает бессмысленным дальнейшее обсуждение языка Паскаль с точки зрения решения задач системного программирования.

Язык Си
       По мнению автора язык Си имеет достаточно маленький размер, чтобы в связи с этим возникали какие-либо проблемы.

4.4.2. ДОКУМЕНТАЦИЯ И СТАНДАРТИЗАЦИЯ

       Плохо документированный язык программирования (так же как. любой компонент программного обеспечения) не может быть по-настоящему полезен. Существует три требования, которым должна удовлетворять хорошая документация:
       Точность: документация должна быть корректна. Если существуют различные диалекты некоторого языка программирования, то документация должна содержать полное и точное описание соответствующего диалекта.
       "Удобочитаемость": даже точная документация окажется бесполезной, если в ней трудно отыскать нужное место, а отыскав его, трудно понять смысл текста.
       Полнота: по существу, всякая документация преследует единственную цель — сделать так, чтобы тот, кто читает эту документацию, был бы в состоянии легко найти четкий ответ на вопрос: а что произойдет, если я...?.
       Хотя польза от легкочитаемых введений в те или иные языки программирования, ориентированных на начинающих программистов, не вызывает никаких сомнений, автора больше интересует вопрос о том, насколько "официальные" описания различных языков программирования и стандарты на них удовлетворяют сформулированным выше требованиям.
       К вопросам, связанным с документацией на различные языки программирования, тесно примыкают вопросы, затрагивающие проблемы стандартизации различных языков программирования, и в частности вопрос о том, насколько тот или иной язык программирования официально или не официально стандартизован. Принятый на некоторый язык программирования стандарт несет с собой ряд положительных моментов, среди которых можно отметить следующие: наличие полной и точной документации, наличие сообщества программистов, которые досконально знают этот язык программирования, наличие инструментальных средств для решения задач из различных областей применения с помощью этого языка программирования (очень важный момент), более высокая вероятность легкости переноса программ, написанных на этом языке программирования, на ЭВМ различных типов, более высокая вероятность того, что реализация транслятора с этого языка программирования соответствует документации и наоборот, и т.д. Три сравниваемых языка программирования существенно отличаются в этой области друг от друга.

Язык Ада
       Во-первых, необходимо отметить наличие стандарта на язык Ада [4], который по мнению автора является блестящим примером, иллюстрирующим, каким должно быть описание языка программирования. В то же время необходимо отметить, что стандарт на язык Ада [4] никак не может считаться чем-то вроде легкочитаемого введения в язык Ада, ориентированного на начинающих программистов. Существует также формальное описание языка Ада [83].
       Стандартизация языка Ада принесла с собой все перечисленные выше положительные моменты, которые обычно несет с собой стандартизация некоторого языка программирования. В частности, Министерство обороны США инициировало разработку проекта по созданию среды для поддержки языка Ада (APSE) [157], в состав которой должны войти различные инструментальные средства. После того как эта среда будет создана, ее наличие окажет существенное влияние на решение вопроса о выборе языка программирования.
       Более того, Министерство обороны США объявило о своем намерении осуществлять контроль за соответствием трансляторов с языка Ада стандарту на язык Ада [4]. Для этого Министерство обороны США осуществило два действия. Во-первых, была разработана система ACVC для верификации трансляторов с языка Ада [61], обеспечивающая их проверку на соответствие стандарту на язык Ада [4] с целью получения сертификата Министерства обороны США. Министерство обороны США объявило также о том, что для создания программного обеспечения по его заказу могут быть использованы только те трансляторы с языка Ада, которые успешно прошли проверку через систему ACVC и получили сертификат Министерства обороны США, оказывая тем самым прямое финансовое давление для достижения поставленной им цели. Во-вторых, Министерство обороны США утвердило термин "Ада" как свою зарегистрированную торговую марку для всех случаев, когда этот термин употребляется в отношении языков программирования, и даже объявило о своем намерении ограничить использование этого термина "способами, которые еще предстоит определить". Столь сильное стремление к установлению взаимно однозначного соответствия между описанием языка программирования и реализациями трансляторов с этого языка программирования должно привести в случае своего выполнения к существенным преимуществам такого языка программирования перед другими.

Язык Паскаль
       В течение многих лет роль "официального" описания языка Паскаль выполняла книга [91], что было совершенно неудовлетворительно. Книга [91] состоит из двух частей. Первая часть ("Руководство для пользователя", состоящее из 95 страниц) представляет собой относительно "дружественные" обзор языка Паскаль и описание транслятора с языка Паскаль для ЭВМ семейства CDC 6000. Вторая часть ("Сообщение", состоящее из 32 страниц) представляет собой "официальное" описание языка Паскаль. Обе части книги [91] недостаточно точны, и их изучение оставляет без ответа ряд вопросов, касающихся языка Паскаль.
       Многолетние усилия международного сообщества программистов, использующих язык Паскаль, увенчались наконец созданием стандарта ISO на язык Паскаль [5]. Как и большинство таких документов, стандарт ISO на язык Паскаль [5] хотя и точный, но очень трудно читается. По этой причине автор рекомендует вместо стандарта ISO на язык Паскаль [5] пользоваться книгой Д.Купера [34], которая при полном соответствии стандарху ISO на язык Паскаль [5] очень легко читается.

Язык Си
       Официальным описанием языка Си считается книга [101]. В более поздней статье [133] содержатся подробные сведения об истории создания языка Си и о мотивах, приведших к принятию тех или иных решений при проектировании этого языка программирования. Существует также большое число книг, представляющих собой легкочитаемые введения в язык Си.
       Исходя из перечисленных в начале настоящего раздела требований к хорошей документации, книга [101] не может считаться таковой, поскольку она не является ни полной, ни точной. При написании настоящей статьи автор оказался не в состоянии получить из книги [101] ответы на ряд возникших у него в отношении языка Си вопросов. Когда же автор обращался с этими вопросами к опытным программистам, использующим язык Си, то получал примерно такие ответы:

Ах, да, этого нет в книге, но все знают, что ...

       Недавно при содействии Американского института стандартов была начата работа по стандартизации языка Си, целью которой является стандартизация собственно языка Си, его окружения (в том числе и некоторых аспектов интерфейса с операционной системой) и его стандартной библиотеки. По общему мнению стандарт на язык Си будет очень мало отличаться от языка Си, описанного в книге [101], а обеспечит лишь более строгое и полное описание языка Си. В результате принятия стандарта на язык Си должно исчезнуть множество нечеткостей, имеющихся в настоящее время в языке Си, и> как надеется автор, сообщество программистов; использующих язык Си, сильно выиграет от этого.

4.5. ЗАКЛЮЧЕНИЕ

       Философия проектирования языка программирования, поддерживаемая его разработчиком, оказывает существенное влияние на конечный результат. В некоторых случаях разработчики языков программирования оставляют для последующего изучения основные положения использованной ими философии, а в некоторых случаях остается только догадываться о посылках, которые привели к принятию тех или иных решений в процессе проектирования некоторого языка программирования. Группа сотрудников Университета Карнеги—Меллона внесла существенный вклад в работу по сравнению языков программирования, проведя сравнение языков Фортран, Кобол, Джовиал и Ада (к сожалению, устаревшей версии языка Ада) не с точки зрения сравнения их возможностей, а с точки зрения заложенной в них философии [141].

4.5.1. ЯЗЫК АДА

       Основным фактором, оказавшим влияние на формирование языка Ада в процессе его проектирования, оказался набор требований [158]. Как уже отмечалось выше (см. §4.4.1), автор убежден, что язык Ада имеет слишком большой размер, что, в свою очередь, является результатом следования букве требований [158]. Поскольку формирование требований [158] в значительной степени не контролировалось разработчиками собственно языка Ада и поскольку эти вопросы (влияние требований [158] на язык Ада) уже обсуждались выше (см. §4.4.1), то автор предпочитает не затрагивать их в дальнейшем обсуждении.
       С учетом нескольких исключений язык Ада обнаруживает достойную похвалы попытку сохранить простоту и элегантность, присущие языку Паскаль, от которого он ведет свое происхождение. Причем эта попытка сопровождалась усилиями по корректировке недостатков, обнаруженных в языке Паскаль. В языке Ада, в частности, используются те же самые конструкции, которые используются и в языке Паскаль: case и when в записях с вариантами, оператор варианта case, when для описания реакции на исключительную ситуацию. Длительный процесс проектирования, перепроектирования и анализа принятых решений позволил избежать в языке Ада многих "острых углов и волчьих ям", имеющихся, как правило, в любом новом языке программирования. И что, может быть, наиболее важно, у коллектива разработчиков языка Ада был сильный лидер — Ж.Ишбиа, которому удалось обеспечить достойные похвалы ровность и целостность подход.

4.5.2. ЯЗЫК ПАСКАЛЬ

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

4.5.3. ЯЗЫК СИ

       Автор находит, что язык Си представляет собой довольно любопытную смесь различных идей и подходов. У автора складывается такое впечатление, что разработчики языка Си должны были платить из своего кармана по 1000 дол. за каждое вводимое или новое служебное слово и по 2000 дол. за каждую новую вводимую ими концепцию, а от программистов, использующих язык Си, требуется уплата 5 дол. за каждый символ в написанной ими программе. Основной же целью разработки языка Си являлась минимизация вышеназванных расходов.
       По мнению автора использовать такую метрику неправильно. Результат такого подхода заключается в том, что, хотя писать программы на языке Си достаточно легко, прочитать их потом (особенно не их автору) оказывается не всегда легко. А это уже существенный недостаток. Автор не в состоянии указать на нехватку каких-либо средств в языке Си - все, что может потребоваться в практическом программировании может быть тем или иным образом сделано при программировании на языке Си. Однако многие механизмы, имеющиеся в языке Си, автор оценил бы скорее как слабые, а не как удачные.
       Следует еще раз остановиться на вопросе о том, какую роль играет верификатор lint. Если рассматривать верификатор lint как часть "официального" описания языка Си, то боольшая часть "дырок" (но не все!) в языке Си исчезает. Верификатор lint был создан в той же среде, в какой был разработан и язык Си, но его создатель не входил в число разработчиков языка Си. К сожалению, верификатор lint не документирован в книге [101], хотя в ней и упоминается о его существовании. Такая нечеткость в смысле определения статуса верификатора lint хорошо коррелирует с нечеткостью описания самого языка Си.

4.6. БЛАГОДАРНОСТИ

       Настоящая статья представляет собой переработанный вариант статьи, написанной автором в мае 1981г. и опубликованной в виде отчета №4634 фирмой Bolt Berariek and Newman, Inc. (BBN). Написание этой статьи (отчета) было выполнено в соответствии с контрактом № 1493601, заключенным между Национальной лабораторией им.Лоуренса в г.Ливерморе . (LLNL) и фирмой BBN. Первоначальная версия статьи (отчета) включала обсуждение четвертого языка программирования — языка Праксис, разработанного фирмой BBN для LLNL. Поскольку автор настоящей статьи руководил работой коллектива программистов, разработавшего язык Праксис, то понятно, что одной из важнейших целей, преследовавшихся при написании первоначальной версии статьи (отчета), являлось сравнение разработанного языка Праксис с другими современными языками программирования.
       Настоящая статья имеет ряд отличий от первоначальной ее версии. Первое отличие заключается в то, что при обсуждении языка Паскаль автор опирается на стандарт ISO на язык программирования Паскаль [5], который еще не существовал к моменту написания первоначальной версии статьи (отчета). Второе отличие заключается в том, что при обсуждении языка Ада автор опирается на действующий стандарт на язык Ада [4], а не на устаревший документ [155], как это было сделано в первоначальной версии статьи (отчета).
       Автор благодарен Ч.Моргану за многочисленные дискуссии по многим из затронутых в настоящей статье проблемам, А.Немету и Д.Франклину за их долготерпение и точные ответы на многочисленные вопросы, касающиеся языка программирования Си.
       Автор также благодарен руководству фирмы Tartan Labs за предоставленную ему возможность завершить работу по подготовке настоящей статьи.




<<< Пред. Оглавление
Начало раздела
След. >>>

Дата последнего изменения:
Thursday, 21-Aug-2014 09:10:56 MSK


Постоянный адрес статьи:
http://az-design.ru/Support/SoftWare/Delphi/Pascal/001b1004.shtml