Vikont Home page
НОВОСТИ

20.01.05
Открылся сайт.


17.01.05
Ура! Сессия сдана досрочно и вполне успешно: матан 4, линейка 5, инфа 4; автоматы: история 5, ПАСИ 4.




СТАТЬИ

Подсистема управления процессами в различных реализациях ОС UNIX.

Сети. Характеристика ЛВС города Химки.

Сопоставление языков C и Pascal

...другие
HOME > MY WORK >

Сопоставление языков программирования C и Pascal по критериям читабельности, легкости создания программ, надежности и стоимость программного обеспечения.

На протяжении второй половины двадцатого века происходило широкомасштабное развитие языков программирования, которые являлись прямым следствием стремления ученых всего мира к достижению лаконичности, простоты и эффективности программирования для ЭВМ. История языков программирования неотрывно связана с историей развития теории программирования, которую разрабатывали такие ученые, как Джон Бэкус, Чарльз Хоар, Эдсгер Дейкстра, Никлаус Вирт, Брайана Кернигана, Бьорн Страуструп, Андерс Хейльсберг и д.р. Каждая новая теория дополняла общие концепции программирования, привносила изменения в лексическую структуру языков, ставила условия на логику управления программы, совершенствовала допустимые абстракции типов данных и расширяла модульность разработки программ.

Оценка концепций языков программирования, которые лежат в основе конструкций и возможностей этих языков, требует определенных критериев. Несомненно, выбор таких критериев будет спорным. Например, с точки зрения автора, тема сравнения языков программирования требует не просто сравнения синтаксиса и концепций языков, а сравнение именно эффективности языков для данной реализуемой задачи, которая выражается через быстроту объектной программы и требует знания основ assembler'а. С еще одной точки зрения, никто не будет спорить, что на данный момент эффективность программ, написанных на императивных языках программирования намного выше программ, написанных на функциональных языках; но кто сможет оценить возможный прорыв производительности в методологии разработки программного обеспечения на функциональных языках для многопроцессорных вычислительных систем (основываясь на свойствах перевода функциональных программ в форму графа)? Таким образом, мы можем сравнивать языки программирования лишь формально, и вместе с тем почти никогда не получим ясной картины приоритета одного языка над другим.

И все же существует ряд критериев, по которым языки программирования можно если не сравнивать, то полноценно сопоставлять. В своем реферете я имею целью последовательно рассмотреть каждый из критериев в концепциях языков программирования Pascal и C.

Читабельность

Свойство читабельности языка программирования основывается на легкости чтения и понимания программ, написанных на нем. Это свойство обособляется еще в 70 годах, когда разработка программного обеспечения в основном сводилась к созданию кода программы, и концепция жизненного цикла программы была только в перспективе. Важно отметить, что читабельность необходимо рассматривать только в контексте определенной прикладной области. Таким образом сразу же возникает вопрос о соответствии задачи и примененного для ее решения языка программирования - несоответствие означает сложную структуру программы и ее интуитивную “неестественность”. В свою очередь свойство читабельности распадается на простоту, ортогональность, множество управляющих операторов, множество типов и структур данных, а также на анализ синтаксической структуры языка программирования.

Простота

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

count=count+1;
count+=1;
count++;
++count;

Третьей потенциальной проблемой простоты является перегрузка операторов, т.е. наличие у символа, обозначающей оператор, нескольких значений. Но для стандартов Pascal и ANSI C эта проблема не возникает.

Ортоганальность

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

Ортогональность тесно связана с простотой: чем более ортогональной является структура языка, тем меньше исключений из правил. Меньше исключений-значит язык более систематичен, его легко изучать и не идти на компромиссы, сто “исключение из правил, подтверждает правило”.

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

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

Управляющие операторы

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

И язык С, и язык Pascal имеют в своем составе оператор безусловного перехода goto. В стандарте ANSI С даже рассматривается случай желательного применения этого оператора:

for (i = 0; i < n; i++)
	for (j = 0; j < m; j++)
		if (a[i] == b[i])
			goto found;
/* нет одинаковых элементов */
    ...
found:
/* обнаружено совпадение: a[i] == b[i] */

И все же на практике оператор безусловного перехода goto применяется редко.

Типы и структуры данных

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

sumIsToBig=1; /* C */

sumIsToBig:=true; { Pascal }

Анализ синтаксической структуры

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

  • Формы идентификаторов. В языке С существует различие между прописными и строчными буквами. Таким образом CurrentX и currentX это две разные переменные, смысл которых единообразен.
  • Специальные слова. На внешний вид программы и, следовательно, ее читабельность значительно влияет форма специальных слов языка. Особенно важное место, отводится способам образования составных операторов, или групп операторов. В Pascal для всех управляющих структур, кроме оператора repeat, используется пара begin-end. Также большое удивление вызывает оформление записей и оператора варианта, где в преть ортогональности используется только специальное слово end. В обоих языках существует недостаток единообразного завершения группы операторов, что ведет к снижению читабельности и требует от программиста выработки стиля оформления кода, повышающего читабельность.
  • Форма и значение. Облегчить чтение программы могут также операторы, само появление которых отчасти указывает на их цели. Но иногда, в зависимости от контекста, программы смысл и реализация операторов может меняться. Примером служит значение зарезервированного слова static в языке С. Если это слово заданно при определении некоторой переменной внутри функции, это означает, что данная переменная создается во время компиляции, если слово static используется при определении переменной вне всех функций, это означает, что она является видимой только в файле, содержащем данное определение.

Легкость создания программ

Легкость создания программ характеризует удобство языка для создания программ в выбранной области. Характеристики легкости создания программ и читабельности языка часто совпадают, что объясняется тем, что в процессе создания программы авторам часто приходиться обращаться к написанному коду. Здесь, также как и при оценки читабельности, сравнение следует проводить в контексте конкретной области применения языка. Не секрет, что язык Pascal создавался Виртом как учебный, а язык С изначально проектировался для системного программирования. С другой стороны язык С не слишком удобен для начала обучения программированию, что, как уже было сказано, вызвано его неортогональностью, и как будет показано дальше, нестрогостью типизации, что не способствует выработке хорошего, стабильного стиля программирования при обучении. Хотя при наличии опытных преподавателей последний пункт становится не слишком обоснованным.

На вручении Тьюринговской премии, Вирт так отозвался о своем детище: “Утверждалось, что Паскаль был разработан в качестве языка для обучения. Хотя это утверждение справедливо, но его использование при обучении не являлось единственной целью. На самом деле я не верю в успешность применения во время обучения таких инструментов и методик, которые нельзя использовать при решении каких-то практических задач. По сегодняшним меркам Паскаль обладал явными недостатками при программировании больших систем, но 15 лет назад он представлял собой разумный компромисс между тем, что было желательно, и тем, что было эффективно”. И конечно следует отметить, что дальнейшее развитие языков программирования так называемой швейцарской школы Вирта, чьими самими яркими представителями явились Modula-2 и Oberon-2, позиционируются именно как языки системного программирования (проект Лилит и Oberon System), хотя официальное описание языка Oberon-2 содержит лишь 16 страниц. Это стиль Вирта.

Поддержка абстракций

Абстракция – это возможность определять, а затем использовать сложные структуры или операции, игнорируя при этом многие детали. На современном этапе абстракция – это ключевая концепция разработки современных языков программирования. В контексте языков Pascal и C абстракции данных ограничиваются самой концепцией структурного программирования, а также введением записей (структур в С) и их использование вместе с базовыми типами. И сточки зрения языка Pascal, и сточки зрения языка С, лишь их потомки (Modula-2, Oberon -2, Component Pascal, C++, Java) обладали широкой абстракцией данных и операторов, основанных на концепции модульного и объектно-ориентированного программирования.

Выразительность

Выразительность языка может относиться может относиться к нескольким различным характеристикам. Одна из них заключается в возможности производить вычисления более удобными и менее громоздкими способами. В языке С запись count++ удобнее и короче записи записи count=count+1. Особым примером выразительности языка С является оператор цикла for - наиболее общий способ организации цикла. Он имеет следующий формат:

for ( выражение 1 ; выражение 2 ; выражение 3 ) тело

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

Схема выполнения оператора for:

1. Вычисляется выражение 1.

2. Вычисляется выражение 2.

3. Если значения выражения 2 отлично от нуля (истина), выполняется тело цикла, вычисляется выражение 3 и осуществляется переход к пункту 2, если выражение 2 равно нулю (ложь), то управление передается на оператор, следующий за оператором for:

int top,  bot;
char string[100],  temp;
for ( top=0, bot=100 ; top < bot ; top++, bot--){  
	temp=string[top];
	string[bot]=temp;
	}

В Pascal циклы со счетчиком проще создавать с помощью оператора for, чем с помощью оператора while – это тривиальный пример выразительности языка.

Надежность

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

Проверка типов

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

Одним из примеров того, как отказ от проверки типов при компиляции или при выполнении может привести к многочисленным ошибкам в программе, служит использование параметров подпрограмм в первоначальной версии языка С (1978). В С совпадение типов фактического и формального параметров не проверялось. Переменная типа int могла использоваться в качестве действительного параметра при вызове функции, ожидавшей формального параметра, имевшего тип float, и ни компилятор, ни система поддержки выполнения программ не могли выявить это несоответствие. Это, естественно, приводило к проблемам, источники которых было трудно определить. В UNIX даже была включена сервисная программа lint, выполнявшая проверку типов в программах, написанных на С.

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

Появления термина надежность не случайно. Этот термин можно применить ко всем без исключения результатам научной работы Вирта. В юности Клаус серьезно увлекался авиамоделированием, самостоятельно спроектировав и построив больше 20 моделей. Юношеское увлечение и профессиональная деятельность Вирта определила его интерес к встроенным системам управления летательных аппаратов. Безопасность летательных аппаратов должна обеспечиваться, начиная уже с уровня компиляторов, используемых для создания бортового программного обеспечения: жизнь пилотов и пассажиров нельзя ставить в зависимость от случайных "зевков" программиста. Многократно доказано, что такие "зевки" в больших проектах неизбежны - такова природа человеческого мозга. Единственный способ исключить подобные ошибки - использовать адекватные инструменты, т.е. надлежащим образом спроектированные языки программирования и компиляторы для них, построенные систематическими математизированными методами с возможностями строгой типизации.

История постоянно подтверждает правоту Н.Вирта. Например, взрыв в 1996 г. ракеты-носителя Ариан-5 стоимостью около 500 миллионов долларов через 40 секунд после старта произошел, как выяснилось, из-за сбоя программного обеспечения: одна из вспомогательных подпрограмм пыталась преобразовать длинное целое значение в короткое без проверки величины значения.

Неудивительно, что приемник языка Pascal язык Modula-2 широко использовался в европейской авиапромышленности и по праву считался лучшим языком для создания особо надежных программ (автоматизация, управление воздушным движением в аэропортах и т.п.).

Обработка исключительных ситуаций

Обработка исключительных ситуаций позволяет перехватывать ошибки и другие необычные ситуации во время выполнения программы, принимать соответствующие меры, а затем продолжать работу. Одним из способов обработки исключительных ситуаций, которые реально можно применить как в языке С так и Pascal, является пересылка вспомогательного параметра, который используется в качестве переменной состояния. Во многих библиотечных функциях С реализован именно этом подход. Другие способы обработки исключительных ситуаций получили распространение в потомках рассматриваемых языков: Ada, C++, Java.

Совмещение имен

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

Легкость чтения и использования

На надежность программы влияет как легкость ее чтения, так и легкость ее создания. Читабельность влияет на надежность программы и на стадии развития, и на стадии ее эксплуатации.

Стоимость

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

Во-первых, в стоимость языка входят затраты на обучение программистов данному языку (меня еще в начале семестра терзали смутные сомнения по поводу методологии надъязыкового обучения программированию у нас в МАИ и отсутствия курса по С/С++. Я конечно понимаю, что для хорошего студента изучение С/С++ самостоятельно не представит особого труда, но ведь именно С++ доказал всему миру огромные возможности объектно-ориентированного программирования, так же как и Pascal был средством выработки хорошего стиля. Делаем вывод – обучение программированию в МАИ не ортогонально).

Во-вторых, в сумму входит стоимость создания программ на данном языке.

Третьим фактором, влияющим на стоимость языка программирования являются затраты на компиляцию программ.

Четвертым фактором является стоимость выполнения программы, написанной написанной на данном языке. Она значительно зависит от структуры языка. Например программы, написанные на Pascal, требуют многочисленных проверок типа во время выполнения, т.е. каков бы хорош не был используемый компилятор быстрота выполнения программы остается под вопросом.

Итак, после появления языка С (1972 Д. Ритчи, Б. Керниган) начинается одна из самых интересных эпох в истории программирования, которая охарактеризована идеологической борьбой любителей С и Pascal; даже сейчас тема противостояния двух этих языков программирования не утихает, но за частую поднимают ее на обсуждение люди некомпетентные в программировании, что выражается в ограниченности формулировок плюсов одного языка над другим. На самом деле высказывания в пользу того или иного языка из этой пары звучали еще раньше, самые интересные и исчерпывающие из которых вошли в статью Брайана Кернигана “Почему Паскаль не является моим любимым языком программирования”. В 1981 г. она появилась на свет в виде препринта AT&T Bell Laboratories. Поскольку ряд авторитетных журналов отказались ее публиковать, она стала расходиться “нелегальными” путями. В широкой печати ей довелось выйти лишь в 1984 г. в сборнике “Comparing and Assessing Programming Languages” (Prentice-Hall, 1984).

Как известно, Керниган вместе с Ритчи готовил подробное описание языка Си, а потому его мнение было особенно интересно. Началось все с того, что Керниган решил адаптировать исходные тексты своей книги “Software Tools” с С для Pascal. К работе над примерами из книги, как пишет Керниган, он приступил весной 1980 г. и завершил ее лишь в январе 1981 г.

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

Керниган пишет: “Паскаль может быть превосходным языком для обучения новичков тому, как писать программы... Он определенно оказал воздействие на проектирование новых языков, из которых Ада, пожалуй, является наиболее важным. Но в своем стандартном виде (как нынешнем, так и предлагаемом) Паскаль не подходит для написания реальных программ”.

Но вот что сказал в присутствии Вирта Деннис Ритчи, автор языка С (1993): “Я утверждаю, что Паскаль очень близок языку Си. Одни, быть может, этому удивятся, другие - нет... Даже интересно, насколько они близки друг другу. Эти языки больше расходятся в деталях, но в основе своей одинаковы. Если вы взглянете на используемые типы данных, а также на операции над типами, то обнаружите очень большую степень совпадения... И это несмотря на то, что намерения Вирта при создании Паскаля весьма отличались от наших в языке Си. Он создавал язык для обучения, а потому преследовал дидактические цели. И, как я заметил это по Паскалю и по его более поздним языкам, Вирт был во власти своего стремления ограничить выразительные средства как можно сильнее...”

P.S.

Керниган немного лукавил, говоря о недостатках Pascal и умалчивая о работах Вирта, направленных на их устранение. А ведь к весне 1980 г. Вирт и его коллеги не только уже завершили работы по компьютеру Лилит и языку Модула-2, но и опубликовали их результаты. К тому же в 1977 г. в известном журнале Software - Practice & Experience вышла статья Хоара, Уэлша и Снирингера с анализом проблем Паскаля.

Просчеты при создании Pascal были устранены в последующих языках швейцарской школы. Все они четко следовали основным тенденциям развития технологии программирования. Программирование структурное (Pascal), модульное (Modula-2), объектно-ориентированное (Oberon-2), компонентное (Component Pascal) - все это значительные шаги в индустрии ПО.

“Наша конечная цель, - пишет Вирт, - расширяемое программирование (extensible programming). Под этим я понимаю возможность конструирования таких иерархий модулей, когда каждый модуль добавляет новую функциональность в систему. Расширяемое программирование подразумевает, что добавление модуля возможно без необходимости вносить какие-либо изменения в существующие модули ? не должно быть необходимости даже их перекомпилировать. Новые модули не только добавляют новые процедуры, но, что более важно, добавляют также новые (расширенные) типы данных. Мы продемонстрировали практичность и экономичность этого подхода при проектировании Oberon System”.