PDA

Просмотр полной версии : Почему Си - хорошо, а Си++ - плохо



iprog
12.07.2010, 13:51
Язык программирования Си создавался с конкретной целью: это язык для написания портируемых программ, при этом максимально эффективный в плане быстродействия, сопоставимый с ассемблером (который на всех аппаратных платформах разный). Удобство программирования в Си - это вещь третьестепенная, он действительно не очень удобен. Выполняются только те действия, которые ты прописал явно - именно поэтому строки и массивы реализованы так дубово и неказисто.
Если поставить перед собой такую задачу и писать в соответствии со стандартом ANSI C, то на Си можно написать портируемую программу или библиотеку функций. Эту программу возможно откомпилировать практически на любом типе процессора - компилятор Си есть везде. И она будет работать. Примеров неисчислимое количество (zlib, libpng и так далее).
Итак, Си - это язык низкоуровневого программирования, предназначенный для создания портируемых программ и библиотек функций. Его появление было настоящей революцией, потому что он позволил отвязать программы от конкретной процессорной архитектуры, практически ничего не потеряв в производительности и быстродействии, в сравнении с ассемблером.
Эту нишу он занял навсегда и пребудет в ней навсегда. Нет смысла придумывать что-то ещё.

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

Улучшайзер Бьёрн Страуструп и его Си++

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

Тем не менее, Си++ используют и пишут на нём программы. Ну просто человек ко всему привыкает.

BulldozerBSG
12.07.2010, 17:15
Боюсь что большинство программистов, которые говорят что пишут на "C++", на сам деле пишут на некой смеси двух языков C/C++. А вобще это святые войны. Лучше отвечу чужими словами:

Автор: 0xffa500
Представим что есть у нас 4 пипла. У каждого есть 3 доски: дубовая, сосновая, дсп.
У каждого есть гвозди и шурупы. У 1го человека - молоток. У 2го - отвертка, у 3го - механическая отвертка с прокруткой и у 4го электро-шуруповерт.
У всех задача ОДНА: вогнать в доски по гвоздю и шурупу.
Первый легко и быстро вгонит молотком гвоздь и шуруп во все доски, но при этом доска их дсп треснет.
Второй одинаково хорошо вгонит шурупы во все 3 доски, при этом он не сможет вогнать гвоздь.
3й Быстрее 2го вгонит шурупы, с гвоздями та же ситуация.
4й электрошуруповертом без особых усилий вгонит шурупы и быстрее предыдущих 2х, но при этом сам шуруповерт не сможет хорошо затянуть шуруп до конца без помощи человека, не хватит силы электромотора.

Каждый из них может одинаково хорошо делать свою работу своим инструментом, при этом у каждый из них может чего-то не мочь делать своим инструментом, ибо этот ИНСТРУМЕНТ НЕ ПРИСПОСОБЛЕН для данных целей, и каждый из них имея кривые руки испортит одинаково хорошо и материал и инструмент!
Мораль сей басни такова: языки програмирования - те же инструменты.

Не стоит обсирать инструмент, особенно если не до конца понимаешь зачем он создан!!!

НАдо за легкость расплачиваться. Не бывает идеала.

Только не забывай что каждый инструмент для своих задач, и писать на ASM офис ты не будешь...
Для некоторых хочу сказать что моей целью не было показаться "Шибко умным" и кого-то чему-то учить.
Я просто хотел донести до людей то, что многие реально не понимают.

Сионист
02.04.2014, 12:24
Беред. Си - язык высокого уровня, он, в отличие даже от паскаля, не имеет вообще ни одного низкоуровневого оператора, например, вызвать прерывание на нём можно только через встроенный ассемблер, а на паскале это делается помимо ассемблера. Язык ассемблера, наоборот, целиком низкого уровня, в нём не предусмотрено ни одной высокоуровневой мнемоники. Так что на язык ассемблера c можно только компилировать, а не сопоставить одно другому.

somewhere
02.04.2014, 17:09
Язык ассемблера, наоборот, целиком низкого уровня, в нём не предусмотрено ни одной высокоуровневой мнемоники.
Современные асматики пишут на масме, стиль написания на котором приближается к высокоуровневым языкам из-за частого использования макрокоманд. Тем более, в прилжениях Windows, где 80% кода сводится к общению с функциями WinAPI

Сионист
02.04.2014, 19:47
Макрокоманды ни чего общего не имеют с высокоуровневыми языками.

Сионист
02.04.2014, 19:51
И если 20% и даже 0,08% сводится к обращениям к системе, то прогу можно легко заменить, систему может и юзверь юзать.

Сионист
04.04.2014, 10:22
И уровень языка не связан с тем, сколько именно опкодов прячется под одной мнемоникой макрокоманды, она всё равно из них и состоит. Какая разница, соответствует ли
MOV AX, 40000 одному опкоду процессора, или COPY (40000, 16384, 6144) длинной цепочке, помещающей три числа в регистры и потом копирующей данные оперцией MOVS с репером? Уровень языка определяется уровнем абстракции данных, если мне не надо задумываться над тем, складывается ли uint32_t с помощью 32-х битных регистров за одну операцию сложения, за две операции сложения и сложения с переносом с помощью 16-ти битных регистров, или же для этого 4 раза юзаются однобайтные регистры, то уровень высокий. Макрокоманда определяет это однозначно, там, где определяется сама макрокоманда, а на c за это отвечает соответствующая конкретному процессору версия компилятора. А может сложение charов происходит в 128-ми битных регистрах, дополняемых копиями знаковых разрядов и только при доступе к памяти обрезаемых до одного байта? А может и не всегда обрезаемых, а при выравнивании struct с charом на границу 128-ми бит заполнители так и пишутся старшими байтами регистра? Мне опять таки не надо об этом задумываться на c, а на макроасме надо. И какой конкретно регистр будет использован для сложения я могу даже не знать, а при использовании макрокоманды я должен учитывать, что если такая то макрокоманда юзается в паре с той, юзая одни регистры, то разбивать пару вот этой макрокомандой можно, так как она сохраняет эти регистры не тронутыми, а этой нельзя, так как она их меняет для использования в паре вон с той. На c можно вообще не думать и даже не знать, какой аппаратурой будет исполнена такая то инструкция, в рамках достаточности оперативной памяти и быстродействия перекомпилировать программу хоть для z80, не меняя вообще ничего - не проблема и меня не волнует, что там меньше регистров в каждом банке, зато их два вместо одного. Нет компилятора? А кроскпомпилятор? Нет и его? Ну тогда асм в помощь для разработки компилятора, а потом можно про архитектуру забыть. А на макроассемблере придётся архитектуру учитывать при разработки каждой софтины, а не только самого ассемблера, а переносимость даже при пересборке поддерживается только в плане смены раскладки тех же операций на другие опкоды. И для того, чтоб выставить определённые цвета консоли и определённый вид каретки, мне не надо думать о том, в какие регистры что надо записать и какой номер прерывания вызвать, это делается оболочечной функцией, а в неё уже завёрнут кусок на асме, который уже сохраняет регистры в стек, раскладывает по ним параметры, прерывает, потом восстанавливает регистры из стека. Это уже делается на другом языке, а на самом c только вызов функции и она даже может быть системной, или библиотечной. Мне не надо на c думать, по каким смещениям от указателя стека лежат параметры функции и как зовут сам указатель. И в каком внутреннем формате закодированы floatы мне тоже думать не надо. Я могу не учитывать порядок не только битов, но и байтов и слов в тех числах, которые не влезают в регистр. Могу не знать, есть ли вообще в процессоре команда умножения и какие адресации поддерживает каждая команда. А на масме мне надо знать, что z80 умножать не умеет и сначала реализовать умножение другими операциями, а только потом юзать. На c же я risc от cisc могу не отличать и вообще думать, будто компьютер не имеет адресуемых регистров вообще, а процессор не содержит арифметико-логического устройства, а является лишь устройством управления, АЛУ же распределено по оперативе, в которой и выполняется вся обработка инфы. На масме этот бред не даст писать вообще, а на c не будет мешать, а лично мне даже приходится временно забывать архитектуру, заменяя знания архитектуры именно этим бредом, чтоб вообще иметь возможность писать на c/c++ и на бейсике, хотя при этом я ещё помню, что умножение и деление занимают больше тактов, чем сложение/вычитание и если стоит вопрос о том, сложить/вычесть ли несколько произведений с одним совпадающим множителем, или несколько частных с совпадающим делителем, или делимым, или же вынести умножение, или деление за скобки, получив произведение, или частное с участием всей суммы/разности, всегда выношу. А кое кому приходится объяснять даже разницу между декрементом и
a=a-1;.

Сионист
19.08.2015, 07:18
Первый легко и быстро вгонит молотком гвоздь и шуруп во все доски, но при этом доска их дсп треснет.Бред. Гвоздь он ещё забьёт, а шуруп у него никуда не залезет, в конце концов он сломает и молоток, и шурупы, и доски, купит новые гвозди, доски и шурупы, кое как забьёт гвозди кулаками и потратит десятки лет на вкручивание шурупов пальцами. А вот четвёртый и шурупы легко и быстро закрутит, и гвозди легко вставит пальцами в отверстия, так как шупруповёрт - это ещё и дрель. Правда гвозди у него также легко выпадут, но как то задача будет решена, при этом быстро, легко и просто, а для качественного решения нужен в пару к шуруповёрту молоток, либо гвозди надо заменить тоже шурупами, что будет ещё качественней.

Сионист
19.08.2015, 07:20
4й электрошуруповертом без особых усилий вгонит шурупы и быстрее предыдущих 2х, но при этом сам шуруповерт не сможет хорошо затянуть шуруп до конца без помощи человека, не хватит силы электромотора.Во-первых у мотора нет силы, а есть момент. Во-вторых нужен как раз момент, а не сила. В-третьих у мотора он больше, поэтому как раз возможности человека затяг и ограничат: вместо дальнейшего затягивания будет проворачиваться рука. В четвёртых этого затяга как раз достаточно.

Сионист
19.08.2015, 07:23
Боюсь что большинство программистов, которые говорят что пишут на "C++", на сам деле пишут на некой смеси двух языков C/C++. А вобще это святые войны. Лучше отвечу чужими словами:На смеси не пишет ни кто. Или человек знает c++ и пишет на нём, или он его не знает и пишет на общем подмножестве. Третьего не дано, это всё таки не русский с польским и даже не немецкий с ивритом.

Сионист
19.08.2015, 17:34
На c++ можно писать всё тоже самое, что и на c и с не меньшей эффективностью, так как весь ООП-нутый синтаксис - надстройка над тем же c. Но при этом:
1. Удобнее.
2. Безопаснее.
А вот если ты из-за многочисленных неудобств потратишь усилия на борьбу с компилятором, то об эффективности c-замены виртуальных функций от c-замены безымянных экземпляров полиморфных классов можешь сразу забыть даже в сравнении с каким нибудь гипотетическим бейсик-подобным интерпретируемым ООП-нутым языком твоего же изобретения и с интерпретатором на том же c писанном. На c++ же она давно реализована оптимальным образом и отлажена. И это не единственный пример.

Сионист
19.08.2015, 18:11
Итак, Си - это язык низкоуровневого программирования,Бред. Низкоуровневый язык - это паскаль, а c - язык высокого уровня. не веришь - попробуй на c помимо встроенного ассемблера вызывать программное прерывание, записать, или прочитать регистр процессора. Это не возможно. Попробуй хотя бы любую другую процессорную операцию, гарантированно на любой архитектуре выполняемую именно за одну операцию процессора.
char a=2;
...
a++;? Не факт, что процессор реализует арифметические операции с отдельными байтами, возможно под этим подразумевается
PUSH AX
PUSH BX
MOV BX, WORD PTR[a]
MOV AX
INC AX
MOV AH, BH
MOV WORD PTR[a], AX
POP BX
POP AX.
short int a=2;
...
++a;? А не факт, что это не
MOV A, [a]
INC A
JNC LC
MOV [a], A
MOV A, [a+1]
INC A
MOV [a+1], A
JMP LE
LC:
MOV [a], A
LE:.

int *a;
...
*a=2;? Не факт, что это не

PUSH A
MOV A,2
MOV [a],A
MOV A,0
MOV [a+1], A
POP A.

char *a;
...
*a=2;? Это может быть
PUSH AX
PUSH BX
PUSH ESI
MOV ESI [a]
MOV BX, WORD PTR [ESI+1]
MOV AX, 2
MOV WORD PTR[buf], AX
MOV WORD PTR[buf+1], BX
MOV AX, WORD PTR[buf]
MOV WORD PTR[ESI], AX
POP ESI
POP BX
POP AX. Вообще нет гарантии исполнения именно в одну операцию процессора ни одной операции c. Именно потому он и портируем: любая низкоуровневая операция может не поддерживаться на какой нибудь ахритектуре.

Сионист
19.08.2015, 18:20
Ну, про "объектно-ориентированность" я напишу как-нибудь потом, это отдельный разговор, а в целом получили следующее: в язык введены неявные скрытые механизмы, которые не работают одинаково на разных платформах, портируемые программы писать невозможно, и в то1. На c++ нет ничего, что не работало бы на какой либо платформе.
2. На c++ нет вообще ничего неявного, а если это слово не по назначению использовать для обозначения того факта, что одной закорючке на высоком уровне может соответствовать несколько операций низкого уровня, то гарантированно всех архитектурах густо нафаршированы "неявными" операциями все циклы, условные операторы и операторы множественного ветвления (switch/case), вызов функций с параметрами, возврат из функций любого значения, кроме void и много чего ещё в первую очередь именно на c, не говоря уж о структурах, а тем более непосредственно не адресуемых ни на одной архитектуре битовых полях.

Сионист
19.08.2015, 18:35
И строки на чистом c такие не казистые не потому, что нет ничего неявного вроде проверки на выход за пределы строки, а потому, что если паскаль-строку в "лоб" расширить снятием ограничений на количество символов, то она не будет вполне портируема из-за того, что на разных платформах может не совпадать как разрядность size_t, так и порядок байт, а переворачивание в какой либо стандартный порядок означает снижение эффективности в том случае, если строка как есть на другие платформы ни в файлах, ни по сети не переносится. А нуль-терминальная строка универсальна.

Сионист
22.08.2015, 16:02
Современные асматики пишут на масме, стиль написания на котором приближается к высокоуровневым языкам из-за частого использования макрокоманд.Удаляется, так будет правильней. Макрокоманды не знают ни типов, ни границ полей, хоть битовых, хоть обычных, ни количества параметров каждой конкретной процедуры, ни даже соглашений о вызовах и устроены предельно тупо. Это синтаксический сахар, сокращающий запись часто используемых последовательностей команд, не более того. Например, вместо
DEC CX
JNC Loop можно набрать вроде как одну "мнемонику" с двумя операндами, под которой будут пониматься сразу и DEC, и JNC. А попробуй одной и той же командой заменить даже
if (i<n) goto Loop; и
if (*p=='\0') goto Loop;. Или, предположим, сложить два целых числа и конкатенировать две строки. Или умножить на -1 и целое число, и комплексное. Есть две процедуры с разным количеством параметров. Вызовите обе так, чтоб сам ассемблер проверил количество параметров и при ошибке подсказал, что вот такую процедуру надо вызывать с тремя параметрами, а не четырьмя. Этими ассемблерами для программирования "почти как на естественном языке" наелся ещё автор фортрана. Удобней оказалось его произведение. Даже его произведение, причём, первая же версия. И выше уровнем.

Romeo
22.08.2015, 19:38
Заглянул во флудилку и даже пожалел. Сионист, ты в своём репертуаре. Единственное, что делаешь, это придираешься к отдельным словам, вместо того, чтобы осознать пост целиком и увидеть в нём истину или хотя бы то, что человек им хотел донести. Ты просто сюда спорить ходишь? Время больше нечем занять?

Сионист
22.08.2015, 20:07
К словам здесь придираетесь Вы. Я, в отличие от Вас, читаю как раз целиком.

Сионист
22.08.2015, 20:10
Кстати, всякими там vcl, mfc и тому подобными не пользуюсь и пользоваться не собираюсь. Пишу на c++. Что я делаю не так? Или имеются ввиду стандартные классы вроде ofstream, ostream, wstringstrim? Ну та на c тоже есть функции ввода-вывода. Ради них стоит юзать язык? Не ради потоковых классов юзают c++, а сами потоковые классы юзают потому, что пишут на c++.

Сионист
22.08.2015, 20:21
Изначально создатели языка Си и системы Unix предполагали, что возникнут другие языки, более удобные для программистов (каждый язык со своей специализацией). Для каждого такого языка, на Си будет написан транслятор (не компилятор), который транслирует исходник с этого языка опять же на Си, а не в исполняемый код. И уже этот промежуточный Си-текст транслируется Си-компилятором в бинарный исполняемый файл. Такова была изначальная философия системы Unix: все программы портируемы и все языки надстраиваются над базовым языком Си.Трансляция программы целиком до её исполнения называется компиляцией не зависимо от целевого языка, то есть от языка, на который транслятор переводит программу с другого языка. Интерпретацией называется пооператорная трансляция в процессе исполнения, поэтому при интерпретации целевой язык неизбежно непосрядственно исполняемый. Язык c таким не является. Значит интерпретировать на c нельзя. В натив процессора можно, а на c нельзя. При этом результат интерпретации ещё и не существует целиком и не существует в течении сколь нибудь продолжительного времени. Он фрагментарен и существует в процессе исполнения конкретного оператора. Так какой же вид трансляции имелся ввиду? Остаётся как раз компиляция. Но можно и наоборот с языка c скомпилировать на паскаль, было бы чем. Или на бейсик. Результат будет хуже исходной c-программы? Эйси. Но заниматься такими глупостями ни что принципиально не мешает, мешает лишь тот факт, что ни кто не стал тратить время на разработку такого компилятора. Но пока не стал. Есть принцип, а есть вопрос реализации. Принципиальная же возможность компилировать с обоих языков друг на друга говорит о том, что возможность компиляции в определённом направлении - ещё не признак уровня языка. Уровень определяется абстракцией, а не тем, что фактически нет компилятора с чужых опкодов на c, сразу сшивающего программу сразу с эмулятором другого процессора, для которого те опкоды - натив.