информационная безопасность
без паники и всерьез
 подробно о проекте
Rambler's Top100За кого нас держат?Сетевые кракеры и правда о деле ЛевинаSpanning Tree Protocol: недокументированное применение
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
 Умер Никлаус Вирт 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / библиотека / программирование
БИБЛИОТЕКА
вход в библиотеку
книги
безопасность
программирование
криптография
internals
www
телефония
underground
беллетристика
разное
обзор: избранное
конкурс
рейтинг статей
обсуждение




Подписка:
BuqTraq: Обзор
RSN
БСК
Закон есть закон




Процесс разработки ПО и ЯП
Костин Г.В.
Опубликовано: dl, 11.10.03 19:59

Все в этой книге может оказаться ошибкой.
Р. Бах. Иллюзии

Здесь привожу некоторые фрагменты из проведенного мной анализа ЯП (несколько сот страниц; с охватом от Assembler до Zhell, от архитектуры процессоров до языков сценариев и от Fortran до C#). Обратная связь приветствуется. Будут интересны мнения, ссылки на литературу, конструктивная критика... Я с удовольствием рассмотрю предложения об обучении в вашем ВУЗе /участие в конференции/ работе связанной с анализом ЯП/Информации о заинтересованных лицах. E - mail автора: georgii"Nospam"@pisem.net. Естественно, перед отправкой писем строчку "Nospam" необходимо убрать.

С полным оглавлением данного исследования можно ознакомиться здесь.

Что есть язык?

Для начала ответим на вопрос: Что есть язык ?

Язык - это интерфейс между двумя субъектами

Примеры:

  1. Человеческий язык - совокупность (слова, жесты, телодвижения, интонаций и мимики).
  2. Интерфейсы (мышка, пользователя, API)

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

Для начала введу понятие "карта". В психологии "перцептивная карта реальности" определенна как индивидуальная модель восприятия каждого человека, являющаяся результатом его предшествующего опыта. Т.е. что у человека в голове. Допустим, стол ассоциируется у русскоязычного человека со словом стол, у француза со словом table, у англичанина или американца со словом Desktop. Переводя на язык компьютеров, карта - это содержимое памяти компьютера. И человек и компьютер реагируют на внешние события (т.е. входную информацию) в соответствии с содержимым своей карты. Допустим, индонезиец слово "Кретин" воспримет как "кудрявый", а слово "сука" как "любить". О фразах "Хулиа, пидерас лас охуэлас?" - "Юля, ты попpосишь блинчиков?" и "Ун трахэ негро пара ми ньета" - "Черный костюм для моего племянника я уже не говорю. Аналогично, компилятор C++, встретив "{" воспримет ее как начало логического блока, а компилятор Паскаля - как начало комментария.

Для осуществления коммуникации (т.е. для передачи информации) между двумя субъектами (компьютеры, люди...) необходимо, чтобы их карты хотябы частично совпадали. Необходимая часть как раз и есть язык. Допустим, компьютер под Windows может общаться с NetWare сервером лишь говоря на его "языке"(интерфейс IPX/SPX).Работая с Unix, вы не сможете использовать команды Dos, а с Dos - Unix. Аналогично, общаться с иностранцами Вы сможете лишь на известном обоим языке. Более того для общения с профессором и со слесарем Вы вероятно тоже выберете разные "интерфейсы". В психологии подбор интерфейса под собеседника носит названия подстройка. На ней основан Эриксоновский гипноз и НЛП.С помощью её можно навести транс и вывести человека из комы.

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

Процесс разработки ПО и ЯП

На рисунке изображен процесс разработки ПО с точки зрения ЯП.

Здесь я рассмотрю разработку ПО как процесс последовательной детализации.

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

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

Детализация проводится либо автоматически, к примеру, использование генерации кода по спецификациям, допустим, Corba IDL или генерация кода программы по графическим спецификациям интерфейса в JBuilder, либо вручную, к примеру, кодирование в двоичных кодах подпрограммы по спецификации на ЯПВУ или реализация класса по спецификациям.

Естественно, первый вариант существенно повышает производительность труда разработчика. Т.е. допустим если разработчик на уровне технического задания тратит 5 минут то на уровне проектирования ему потребуется 50, а на уровне реализации 250. Именно поэтому эволюция ЯП да и программирования в целом движется в направлении от компьютера к человеку.

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

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

Касательно языка:

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

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

Её существование обусловлено лишь небезошибочностью работы человека и недопустимостью таких ошибок в программной инженерии.

Ширина прямоугольника ("горизонтальный размер языка") обозначает широту средств языка по уровням абстракции. Поясню на примере. C++ - это язык с очень большой "шириной": он взял в себя средства и от низкоуровнего ассемблера, и от ЯПВУ. На нем можно реализовать и драйвер, и АСУП. Притом в случае, если в язык будут вставлены высокоуровневые средства создания интерфейса пользователя, работы с БД и т.д., как это предлагает Страуструп, то он станет еще "шире". Java - язык значительно более "узкий". И драйвера на нем писать не удастся.

Длина прямоугольника ("вертикальный размер языка") обозначает широту средств языка по областям применения. Фактически спектр типов приложений, на которые этот язык рассчитан.

Поясню на примере - PL/1 и C++ одинаково годятся и для разработки финансового пакета, и для компьютерной игры. Они имеют большую "высоту" средств.

MatLab, JavaScript имеют маленькую "высоту". Их эффективно можно использовать лишь в той единственной области, для которой они созданы (в данном случае математика и Web - скрипты).

Как известно, длина, умноженная на ширину, даёт площадь. Это объем средств языка.

Есть мнение, что сложность и размер языка должны быть соразмерны с размерами задачи.

Хотя не все специалисты и ученые согласны с тезисом, что сложность и размер языка должны быть соразмерны с размерами задачи. К примеру, Вирт разработал ОС Oberon на очень простом языке Oberon с минимальными средствами.

Тут есть два крайних варианта:

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

Касательно сложности ЯП отмечу следующее:

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

Тут следует учесть и человеческий фактор.

Простота использования невысока у высокоуровневых средств лишь для задача для которых они предназначены. К примеру разработка простенькой БД в Accses значительно проще, чем Delphi. Но разработка серьезного клиент - серверного АРМ'а на Delphi может оказаться проще.

Человеческий фактор в ЯП

  1. "...что на изменение привычек и поведения людей всегда уходит гораздо больше времени, чем хотелось бы..." Бьорн Страуструп
  2. ...исследования психологов ясно показали, что люди постоянно ошибаются: пишут ли они программы, разрабатывают ли математические доказательства, управляют ли самолетом...
  3. Иногда разработчики занижают уровень языка. К примеру, разрабатывают на C++ программу, которую можно было разработать на Perl за в несколько раз меньшее время
  4. "..реагируют на эту мою позицию с неистовством, выходящим за рамки отношений, которые я считаю уместными при обсуждении языка программирования." Бьярн Страуструп
  5. Постоянный приток в сферу разработки ПО "программистов по случаю". Они добавляют популярность и таким средствам, как Perl,TCL,1C,PHP.
  6. Ограниченная размерность решаемых задач. Программист не может одновременно держать в голове подробно достаточно сложный фрагмент кода. Поэтому в ЯП включаются средства для декомпозиции ПО - подпрограммы, модули, классы.
  7. Не только ЯП формирует мышление, но и мышление - язык и даже "железо".

    Приведу пример.

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

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

  8. Лёгкость понимания программы человеком. Компьютер программу компилирует, а читает и модифицирует её человек. Успешность этого процесса в значительной степени зависит от легкости понимания программы. В частности, синтаксиса языка.
  9. Стиль мышления.

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

    Качество программы определяется качеством мышления.

    Резюмируя, отмечу, что разница между человеком и компьютером очень велика. Язык служит [промежуточным звеном] для преодоления этой разницы.

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

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

С/C++

С++ - Язык С, на котором поставлен крест. Дважды. Если серьезно - постинскремент от языка C. Хотя если сравнивать, к примеру, объемы руководств, то, как минимум, не инскремент, а умножение на два. Следует ли из этого, что C до инскремента было равно одному(т.к. C+1=C*2) ?

"Ох волна моя, волна, ты как С++ мощна"
Почти Пушкин

Касательно мощности C/С++.Да, действительно это очень мощные языки ,но их мощность заключается в их средствах низкого уровня,а они при разработке прикладных программ часто неприменимы.
Мощность - это, конечно, хорошо,но часто опасно.
Пример: #define int long синтаксически верно, но может привести к катастрафическим последствиям (хотя, к примеру, при переносе программ с одной платформу на другую это может понадобиться). Или оператор goto. То, что он не включен в Java, многие считают очень большим благом. Goto - это мощная возможность, но, как показал еще в семидесятых годах Дейкстра, мягко говоря, "не рекомендованная" к использованию. Тем не менее это возможность, и она может быть не лишней. Я считаю, что подобные низкоуровневые конструкции должны присутствовать, но ими не следует злоупотреблять. В тех редких случаях, когда лично я его использовал, это была попытка развития плохоспроектированной программы.

"Чтобы сделать карьеру программиста за границей нужно знать два языка: английский и C++"
Российский аспирант, побывавший в Вене.

"С точки зрения теоретического программирования язык си - это Фортран 80 - ых. (Против такого уничижительного определения не возражал и автор языка Си - Д. Ритчи). Этот язык, сочетающий в себе многие преимущества языка высокого уровня и ассемблера, дал программистам, по образному выражению некоторых, педаль газа, но заблокировал педаль тормоза. На Си компьютер может "мчаться" быстро, но рискованно. То есть Си, насаждая ссылочно - ассемблерное программирование, как бы имеет вектор в сторону, противоположную той, которая определяется теорией и методологией языков программирования.
Поэтому и Си++ - это довольно странное сочетание некоторых черт ООП (здесь и далее - объектно - ориентированное программирование) и процедурного программирования"
К.Т.Н. Соловьев А.Е.

"Cи++ представляет собой интересный эксперимент по адаптации возможностей объектной технологии к традиционному языку программирования. ... поскольку Си++ - язык, требующий весьма интенсивной критики. Он представляет собой не слишком удачную реализацию объектно - ориентированной технологии, и поэтому его недостатки просто необходимо подвергать критическому анализу... Си++ приносит всем колоссальное разочарование - он вобрал в себя все плохие и старые средства, а также привнес в объектную технологию абсолютно ненужную сложность. Зачем нужен язык, насквозь пропитанный низкоуровневыми конструкциями? ".
Ян Джойнер
Далее по тексту "Наиболее верный путь к успеху - использование чистого объектно - ориентированного языка, обладающего интерфейсом с языком Cи или с другими низкоуровневыми языками. Это обеспечит хорошие поддержку и сопровождение, переносимость и качество", что в принципе можно наблюдать на примере любого средства 4GL(того же Delphi) поддерживающего стандартные средства взаимодействия Windows такие как Dll,Dde, Ole. Простейший пример - обращение из Delphi программы к Win API(ведь Windows написан на Си(9X) и C++(NT)).

Изначально созданный для разработки ОС Unix, язык C получил широкое распространение. Это наложило очень сильный отпечаток на язык.

Как возник язык С? Вначале был CPL: он был создан в середине 60 - х годов.

Язык не получил широкого распространения, но в процессе его создания появилась масса идей. Язык был беcтиповый, как и положено ассемблеру (ещё PDP 7).

Новая упрощенная версия языка называлась Basic CPL или BCPL. На нем была реализована MULTICS. Но в результате ее размеров и раздутости ей потребовалась замена.
Часть идей из MULTICS была взята разработчиками Unix. К примеру, иерархическая файловая система. Замечу, что в прямом предшественнике DOS-CP/M её не было, как и в первой версии DOS.

Доработали язык - получился B.

Разработали ОС - получилась Unix

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

К примеру:

  • Отсутствует возможность напрямую работать с данными, не поддерживающимися процессором PDP (множества, строки, etc).
  • Присутствие явно PDP - ориентированных конструкций:

    Функция poly, сводящаяся к аналогичной команде ассемблера. Вычисление же полиномов не является задачей первостепенной важности языка.

    Постинскремент и прединскремент. На x86, к примеру, это ничего не дает. И A=B++ и A:=B; inc(b); приведут к командам ассемблера MOV a,b; inc b (естественно, непосредственно в коде это будут не имена переменных, а, к примеру, регистры).

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

scanf("%d %d",&n,&ar[n]);

Вы думаете, что будет введено целое n и n - ный элемент масива ar ?

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

Другая "особенность"

char     str[50]="qwertyuio";
int a=3;str[++a]=str[++a]=' ';
cout<<str<<"\n";
str[a++]=str[a++]=' ';
cout<<str<<"\n";

Результат:

qwe tyuio
qwert uio

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

int i=0,ar[2]={10,11};
i=ar[i++];// А кто сразу скажет чему равно значение i

Хотите еще ? Формат вывода зависящей от типа:

short int x=55;
printf("%d\n",x);

Если заменить short int на longt int, то придется менять и printf("%d\n",x) на printf("%D\n",x)

Пример: предположим, что вместо i<=100 разработчик написал i=100 при синтаксисе C/C++

   for (i=0; i=100; i++)

Цикл будет выполняться вечно (вместо 101 раза) т.к.

  1. Отсутствует логический тип данных.
  2. Выраженние i=100 равно 100(т.е. по не ноль - истина).

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

Конечно, у этой особенности есть и более достойное применение

if (сh=getchar()!=ESC)  {..}
Обобщу, что такие средств хоть и удобны в использовании и позволяют писать разработчику "красивые" программы, не способствуют безопасности этих программ и совсем не считаются простотой языка. Так что "Красиво"- не всегда хорошо.

Из минусов также следует отметить не слишком читабельный синтаксис. Подумайте, что больше говорит end loop в АДЕ или "}" в C. Конечно, краткость - это хорошо, но платить за нее такую цену....

Примеры:

Паскаль:

if Screen.Forms[I] is FormClass then begin

C++:

if (dynamic_cast<FormClass*>(Screen - >Forms[I])){
A=(!CL&&!RC)?0 : (!LC?RC:LC)//"Очень понятное выражение"
*++* agrv //"Еще одно очень понятное выражение, при том синтаксически верное"
"Интуитивно понятный" синтаксис прекрасно подчеркивает следующий пример:
  int i=5;
  int *  const p3=&i;//Указатель константу
  const int *  p3=&i;//Указатель на константу
 i=5;//Правильно
 *p3=5; Неверно! указатель на константу измененную в предыдущей строке.

char (*(*x2 ())[]) () //Срочно позовите криптоаналитика !!!
Или работа с перечислениями:
enum modes { LASTMODE , BW40 , C40, BW80, C80, MONO  } ;
 ..
 modes  e1=C80,e2;
 e1=e2*3;  //Очень осмысленный оператор на "ЯПВУ". 
           //Ведь для "ЯПВУ" нет разницы, что int, что enum, что  bool

А что может значить, по Вашему мнению, команда a=24[ar]; ?

При условии, что int ar[50]; int a; она полностью эквивалентна a:=ar[24];

Как совершенно справедливо замечают поклонники C/C++ эти языки позволяют писать чрезвычайно краткие и выразительные программы. На счет краткости - безусловно.

А вот к какой выразительности может привести краткость, я сейчас покажу:

#include <stdio.h>
#define Q r=R[*p++ - '0'];while(
#define B ;break;case
char*s="Qjou!s\\311^ - g\\311^ - n\\311^ - c\\::^ - q - ma%mO1JBHm%BQ - aP1J[O1HB%[Q<nbj\
o)*|gps)<<*txjudi)m*|aQdbtf!::::;sfuvso<aQefgbvmu;aQ<m,,a%CQ<csfbla%bQ<aN2!Q\
\ndbtf!aP2Q;m>aP2Q<a%!D12J!JGJHJOJQJFJSJJJMHS%HD12D12N3!N4\nJUJT%UQm>aP4HC%T\
Qs\\q,,^>m,2<m>aP4HC%SD12N1\nJNQm>s\\..q^aHC%NHb%GN1!D32P3%RN1UP1D12JPQUaP1H\
R%PN4\nQ<g\\(aP3Q(^>aP2Q,2<n\\(aP3Q(^>aP4Hb%OD12D12N2!N3\nJVP3Q,,<jg)aP3Q=>n\
\\(aP3Q(^*m>g\\(aP3Q(^<fmtf!m,,aHC%QN1!N1\nJ#Qqsjoug)#&e]o# - aP1Q*aHb%#Qqvut)\
aP1Q*aHb%FN1\nQm>::::aHC%VP3Q>bupj)hfut)c**aHb%JD12JON1!Qjg)a%LN1UP1D12JIQUa\
P1HL%IQ*m>aN2!N2\nP2Q<fmtf!m,,aHC%MN1!N2>P2Q>aN2\nP2Hbdd!b/d";int k;char R[4][99]
;main(c,v)char**v;{char*p,*r,*q;for(q=s;*q;q++)*q>' '&&(*q) - - ;{FILE*i=fopen(v
[1],"r"),*o=fopen(q - 3,"w");for(p=s;;p++)switch(*p++){B'M':Q(k=fgetc(i))!=EOF
&&k!=*p)*r++=k;if(k==EOF){fputs("}}\n",o);fclose(o);return system(q - 6);}*r=0
B'P':while(*p!='`')fputc(*p++,o)B'O':Q*r)fputc(*r++,o);p - - B'C':k=0;Q k<*p - '0'
)(*r++=fgetc(i),k++);*r=0 B'I':k= *p;if(**R==k)goto G B'G':k= *p;G:p=s;while(
*p!='$'||p[1]!= k)p++;p++B'N':R[*p - '0'][0]++;}}}

Эта программа всего в 17 строчках текстового режима VGA(25X80) представляет собой полнофункциональный интерпретатор языка Basic, который поддерживает:

  • Переменные(имена от A до Z), которые инициализируются нулевыми значениями при запуске.
  • Цикл FOR var = exp TO exp ..NEXT var
  • Подпрограммы GOSUB exp и RETURN
  • Естественно, оператор GOTO(какой же Basic без GOTO)
  • Условия IF exp THEN exp
  • Комментарий REM any text
  • Оператор конец программы END
  • Присвоение var = exp
  • Ввод INPUT variable
  • И вывод PRINT string PRINT exp

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

В части ясности синтаксиса антиподом C/С++ является язык АДА. Вместо бесконечных "}" пишется END LOOP, END IF, END CASE или END ИМЯ_ПОДПРОГРАММЫ/ПАКЕТА. Я уже не говорю, насколько "Переменная ТИПА целый" читабельней чем "цел Переменная". Логично предположить, что платой за краткость является читабельность программ, или платой за читабельность программ является отсутствие краткости.

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

Отмечу, что подобный синтаксис Microsoft положила в основу языка Visual Basic.

Ну и, конечно, отсутствие строгого контроля типов(хотя в этом есть и обратная сторона - в Pascal'е Вам придется для того, чтобы иметь возможность "прострелить себе ногу", явно указать на свое желание компилятору.

Распространенности C послужила:

  • Распространенность Unix.
  • Хорошее качество генерируемого кода. Особенно на PDP.
  • Достаточная "мощь" языка.
  • Сравнительная простота реализации компиляторов.
  • Краткость синтаксиса и его "красота" любима многими С/C++ - программистами, многие считают их чуть ли не основными преимуществами языка (то есть возможность написать ar[c++] вместо ar[c];inc(c) оказала большое влияние на популярность языка).
  • Широкие возможности разработки системного ПО(к примеру, адресная арифметика , битовые поля ). В этой низкоуровневости для многих разработчиков даже прикладного ПО есть некая прелесть, привлекающая к языку.
Справедливости ради скажу, что именно такие низкоуровневые средства часто делают C/C++ более предпочтительным для задач системного программирования по сравнению с Паскалем(Правда, уже в Modula 2 появилась адресная арифметика).
Позже сам Керниган советовал разработчикам не использовать битовые поля, так как они "слишком платформозависимы".

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

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

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

Да и потеря не столь велика. Я провел небольшой эксперимент со следующей программой и её аналогом на Pacal.

#include<stdlib.h>
#include<stdio.h>
#include<time.h>
void  main()
{ unsigned long int i;
  int  ar[10000];
  time_t t,t2;
  t = time(NULL);
   randomize();
//Многократное присвоение случайному элементу случайного значения
  for (i=1;i<429496;i++) 
		ar[random(1000)]=random (32767);
//Многократное заполнение    массива числом 2
for (i=1;i<429496;i++)  	ar[i%(1000)]=2;
  t2 = time(NULL);
  printf("Время выполнения= %d",t2 - t);
}

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

Выводы могут оказаться для кого - то неожиданными:

  1. Версия на C не показала более высокого быстродействия по сравнению с Pascal версией.
  2. Быстродействия Pascal версии при отключении всех проверок увеличивалось на 7 - 8%.

Что вполне логично, т.к. проверка у процессоров 80x86 реализуются на аппаратном уровне. А прирост в 7 - 8% может дать использование компилятора с хорошей оптимизацией.

По данным Дмитрия Беленко, основанным на проводимом им эксперименте, разница между Delphi и C++ Builder даже вычислительных задачах составляет 4 - 5% .

Замечу что существует и с специальный проект Cyclone. Его разработчики - Корнельский университет и AT&T. Язык фактически представляет Си с проверкой потенциально опасных ситуаций, таких как переполнение буфера. В дистрибутив входит программа преобразования программ из Си в Cyclone которая может отыскать и найти потенциально опасные места программы. Основная цель разработчиков - создать язык пригодный для программирования безопасных приложений.

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

В языке появились:

  • классы (classes);
  • шаблоны (templates);
  • пространства имен (namespaces);
  • перегрузка (overload);
  • потоки (streams);
  • исключения (exception)

ООП и современные компиляторы частично сгладили недостатки С. Появились классы для работы с такими типами данных, как строки и множества. В сомнительных(смотрите пример выше) ситуациях компилятор(далеко не любой) может выдать подсказку. Для лучшей читабельности IDE автоматически выровняет исходный текст(Первоначально этим занималась команда indent, которая существует и сейчас во многих клонах UNIX(в т.ч. Linux)). Специальные средства, к примеру, NuMega BoundsChecker позволяют отслеживать наиболее вероятные ошибки, такие, как выход за границы массива или утечка памяти. Программа lint(входит в Unix'ы) занимается исключительно ревизией исходного текста программы по поводу контроля типов.

Низкая скорость компиляции в результате отсутствие полноценного механизма модульности часто компенсируется механизмом предварительной компиляции заголовочных файлов.

"Software engineering - искусство программирования без умения это делать"
Эдсгар Дейкстра (Edsger Dijkstra)

"Использования C++ для разработки прикладного ПО и вспомогательных средств для нейтрализации недостатков языка подобно использованию решета для переноски воды и специальных средств для замыкания дыр в решете."
Автор

Хотя С++ частично сгладил недостатки(просто прикрыв их своими средствами, сами недостатки никуда не делись) к нему он прибавил и свои:

  1. Главный - черезмерная сложность и размер языка(это роднит его с Адой, хотя сложность Ады вызвана разнообразием средств, а не их дублированием). Даже реализация ООП подвергается интенсивной критике. Я для примера рассмотрю множественное наследование. Сам вопрос о необходимости включения множественного наследования в язык является достаточно спорным. В частности стоит проблема наличия одноименных методов у родителей класса. Возникает вопрос: что вызывать? Первый попавшийся ? Или все по очереди ? На практике это решается написанием у нового класса метода, который непосредственно реализует нужную логику, правда тогда возникает вопрос о адекватности самого множественного наследования. Ведь его смысл в автоматическом вызове методов (и доступе к полям) классов предков. Если же разработчику приходится указывать конкретный класс - предок, то множественное наследование в значительной степени теряет смысл. Остаются лишь такие частности как возможность обращения к protected элементов классов предков, но соответствующая модификация программы это не проблема. Реализацию множественного наследования тоже нельзя назвать тривиальным делом, она значительно сложней реализации "единичного наследования".
  2. Большая избыточность средств - т.е. для решения одной задачи я могу использовать большое количество идентичных средств.

    Кстати, многие средства языка, которые, по мнению его поклонников, появились в C++, были заимствованы из других языков. К примеру, аналог шаблонов - пакеты - были в АДЕ до создания C++. Обработка исключительных ситуаций присутствует в АДЕ и даже в PL/1.Перезагрузка операций также присутствовала в АДЕ.

Огромное разнообразие средств, которые дублируют друг друга , нравится многим поклонникам C++.Они считают, что это делает язык более удобным для использования. Считать так- это их право. Вероятно, для кого-то это действительно так.

    Приведу авторитетное мнение Кена Томпсона (одного из разработчиков C и Unix):

    "При работе с языками C++ и Java у меня возникало определенное беспокойство, когда я просил сделать что - либо, а в ответ получал: "Хорошо, вы делаете это так - то или можете сделать так - то". Совершенно очевидно, если вы в состоянии сделать что - то столь разнообразными способами и все эти способы более не менее эквивалентны, значит в системе заложено слишком много возможностей."

C тоже избыточен. Пример цикла без тела: for(a=1,b=70;a<50;ar[a++]=a+b,b--).По сути здесь for выполняет роль while.

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

  1. Средства поддержки совместимости с С.С одной стороны это большой плюс(очень много ПО написано на C), с другой, для обьектно - ориентированного программирования они не нужны.

    Пример: введение классов(при их использовании) делает ненужным записи(struct) и вариантные записи(union) , тем не менее по соображениям совместимости они присутствуют.

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

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

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

"Писать плохие программы на С++ значительно легче, чем хорошие."
Общепризнанный факт.

Громоздкость С++ Бьярн Страуструп объясняет следующим образом:

"Следовательно, универсальный язык программирования должен поддерживать различные способы мышления и стили программирования. Это разнообразие есть следствие, как разнородности решаемых задач, так и многообразия путей их решения. C++ поддерживает широкий спектр стилей программирования и поэтому более заслуживает название мультипарадигменного, нежели объектно - ориентированного языка...

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

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

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

А в разных ОС интерфейсы разработчика существенно различаются(сравните Win32 API и POSIX).Это тоже необходимо отобразить в языке.!!!

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

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

С++ вобрал в себя средства для поддержки очень широкого спектра технологий разработки и стилей программирования. На нем можно разрабатывать с почти одинаковой [не]успешностью, используя и линейное , и обьектно - ориентированное программирование.
Линейное программирование - архаичный способ разработки программ без использования таких структур языка, как циклы. Зато интенсивно используется оператор GO TO. Был вытеснен структурным программированием.

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

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

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

Процитирую один абзац из первой части.

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

А как удобно управлять таким самолетом ! Ведь средства управления он должен включить в себя от всех типов самолетов. Вот и придется пилоту постоянно переключаться с панели управления вооружением на панель регулировки температуры и влажности в салоне самолета.

Важно отметить, что в этом заключается одно из ключевых различий с Виртовской линией Pascal/Modula/Oberon. На Pascal'е можно очень хорошо использовать процедурную декомпозицию, но использовать линейное программирование неудобно.

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

Тем не менее, Оберон остается универсальным языком. Конечно, не таким универсальным, как C++(в смысле не мультипарадигменный). Но природа программ, да и программистов такова, что универсальных программ, да и программистов не существует. Представьте себе игру, которая занимается не только подсчетом денег заработанных игроками, но и чтением/записью сохраненных игр посредствам работы с портами ввода - вывода(т.е. то, чем занимается низкоуровневый драйвер на ассемблере).

Или разработчика, который поочередно разрабатывает то этот драйвер, то бухгалтерский АРМ. Конечно, обе программы можно при желании написать на C++, но используемые при разработке средства будут разные. Да и программы будут уступать аналогичным, написанным на ассемблере(работать будет быстрее) и, к примеру, Delphi (разработка займет меньше времени).

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

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

Если ориентироваться не на разработчиков, а на задачи, то мы увидим еще одно "НО". Разные технологии в разной степени применимы к разным задачам. К примеру, ООП не дает заметного выигрыша в вычислительных математических задачах. Но ведь и заметного проигрыша тоже нет ?

К тому же каждый язык имеет свою сферу применения. Никто не предлагает писать на Perl модули ОС. Да и программисты, пишущие на Perl, и программисты, пишущие модули ОС - обычно разные люди.
В случае языка АДА подобная всеобъемлемость имела практическую необходимость. Ведь АДА была призвана заменить (и заменила) все языки (а их было несколько сотен, используемые в US DOD(министерстве обороны США) и всех организациях, работающих на него. Соответственно, такая же универсальность нужна C++ лишь в том случае, если он призван вытеснить не меньше языков. Правда, US DOD нужен был единый стандартный язык, а кому нужен C++ ?

У Страуструпа есть такая идея: "Пользователь не обязан знать ничего, кроме того подмножества языка, которое он явно применяет для написания программы"

Идея хорошая, но, к сожалению, стопроцентно нереализуемая. Элементарные операции со строками требуют знакомства с STL и шаблонами. Конечно, можно писать в стиле C, но тогда исчезают преимущества C++ перед ним. Для использования массивов с проверкой на выход индекса из диапазона необходимо использовать STL и перегрузку операций шаблона.

В одном из интервью Страуструп приводит следующие потенциальные направления развития C++:

  • "Параллелизм: я сторонник библиотечной реализации потоков и параллельного выполнения операций без разделения памяти.
  • Отображение типов: неплохо было бы обеспечить библиотечную организацию интерфейса с информацией расширенных типов.
  • Типизация: хотелось бы, чтобы в библиотеку Standard Library были включены функции поддержки расширенных типов, однако конкретных предложений у меня нет.
  • Хеш - таблицы: конечно, необходимо интегрировать некоторые варианты популярной схемы hash_map.
  • Ограничения для аргументов - шаблонов: все это просто, универсально и элегантно реализуется в рамках существующего стандарта Си++.
  • Операторы контроля: многие из наиболее важных операторов контроля - верификация кода и обработка ошибок - можно было бы реализовать в виде шаблонов. Некоторые из них следует включить в библиотеку Standard Library.
  • Сопоставление с регулярными выражениями: хотелось бы видеть в стандартном варианте языка библиотеку определения соответствия шаблонам.
  • Сборка мусора: в стандарте Си++ нужно явно определить технологию, позволяющую игнорировать "скрытые указатели", а также конкретизировать порядок обработки деструкторов.
  • Графический интерфейс пользователя: хорошо было бы иметь стандартные конструкции GUI, но не знаю, насколько это реально в сложившейся ситуации.
  • Независимость от платформы: хотелось бы, чтобы Standard Library поддерживала более широкий набор интерфейсов с общими системными ресурсами, например, с каталогами и сокетами."

Еще одна проблема заслуживающая упоминания - это путь распространения C++. Очевидно, что тут повлияли распространенность Си, широта средств языка и областей его применения. Достаточно большой вопрос - насколько на распространение C++ повлияла рекламная поддержка ?

Вот что говорит сам Страуструп по этому поводу:

"Си++ приобрел массу приверженцев там, где это было нужнее всего: миллионы программистов используют его в качестве своего основного инструмента.

Удивительно, но этого удалось добиться, не имея <административного ресурса> и по существу без финансовых затрат. Однако, с другой стороны, сообщество сторонников Си++ оказалось разобщенным и крайне уязвимым для выпадов враждебной пропаганды. Мне кажется, все дело здесь в том, что хороший код часто остается незамеченным - даже его пользователями. Посмотрите на программы, написанные на Си++, например, на приложения Netscape или на Internet Explorer. Компании, разрабатывающие программы для решения реальных задач - в частности, для управления телекоммуникациями, контроля за различными механизмами и имитационного моделирования, - предпочитают не говорить, на каком языке написаны их приложения.
О том, что приложение написано не на C++, как правило, тоже никто не говорит. О количестве ошибок в упомянутых продуктов я не говорю.

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

Язык Си++ никогда не поддерживался каким - то одним лидером отрасли. Каждый крупный производитель дополняет (и так было всегда) стандарт Си++ своими собственными элементами. Си++ никогда не являлся предметом маркетинговой стратегии. Если какие - то маркетинговые мероприятия и проводились, то только организациями, продающими что - то другое (например, среду для разработки программного обеспечения), включающее в себя Си++ в качестве составляющего компонента. Сообщество Си++ скорее страдало от чрезмерной популярности этого языка: он постоянно становился <объектом нападок>, ведь в современном мире, живущем по законам коммерции, честная борьба - большая редкость.

У сообщества Си++ никогда не было координирующего центра, финансовые возможности которого позволяли бы ему заниматься популяризацией языка. Кто и из каких соображений готов голосовать за Си++? И как довести эту информацию до программистов, преподавателей, менеджеров? Буквально на днях я слушал выступление профессора перед студентами, в котором он категорически отрицал существование стандарта Си++! Жаль, что даже спустя два года после ратификации этого стандарта сплошь и рядом возникают подобные недоразумения."

Приведу и другую точку зрения.

Многие приверженцы C++ считают его языком для профессионалов. Это так.

Но они переходят дальше и производят ошибочное следствие, что единственный язык для профессионалов - это C++.

Яблоко - это фрукт. Но это не значит, что любой фрукт есть яблоко.

Поклонники Си даже сочинили стишок

"У любого ты спроси
Будь хоть трижды ламер
программируешь на Си
Ты - крутой программер"

Я cчитаю что определять крутость программиста по языку - по крайней мере некорректно. Можно писать хорошие программы на Basic и ужасные на C++. Кроме того, аргумент против C++ в религиозных войнах о его рекламной распространенности тоже появился, вероятно не с неба.

В заключение отмечу что:

  • В тоже время нужно признать, что C++ является наиболее мощным, вероятно за исключением Ad'ы языком из широко используемых.
  • Он же является на момент написания обзора наиболее популярным языком. Хотя это не бесспорно. Почему см. страницу XXX. Хотя последние годы его потеснила Java.Сейчас теснит и C#.
  • Он же является наиболее критикуемым языком.
  • Он же содержит огромное наибольшее количество недостатков и подводных камней нередко делающих программы на нем менее надежными.

P.S.

Хотелось бы знать мнения читателей по поводу:

  • доля того или иного языка в программной индустрии (измеренная в стоимости проектов, стоимости продаваемых программных продуктов, количестве программистов, может еще в чем...)
  • влияние языка на стоимость проекта, стоимость сопровождения, надежность.
  • принципы, по которым в индустрии выбираются языки
  • сферы, где существует конкуренция языков - и области, где "все поделено"
  • тенденции и прогнозы (например - "через N лет мы все будем в сфере A - писать на B, в сфере C - на D, и т.п."

Очень приветствуются аргументированные ответы ссылки на научные исследования,книги и статьи.

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

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

Интересен сравнительный анализ ЯП и компиляторов и вообще всё что с этим связанно. В частности связь с психологией. Очень интересно отличие "у нас" и " за бугром".

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

Copyright(c) Костин Г.В., 2003
All Rights Reserved

обсудить  |  все отзывы (15)

[22897; 126; 5.53]




Rambler's Top100
Рейтинг@Mail.ru





  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach