"Ох волна моя, волна, ты как С++ мощна"
Почти Пушкин
Касательно мощности 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 раза) т.к.
Отсутствует логический тип данных.
Выраженние 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 *
constp3=&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\