C и C++ — культовые языки, на которых написано огромное количество кода. Но какой путь они прошли, чтобы стать таковыми? В этой статье расскажем о появлении C, начале его официальной стандартизации, а также о C with Classes и его окончательном превращении в C++.
1969 — 1973. C рождается на свет
1969 год. Аполлон-11 побывал на Луне. В это же время в лабораториях AT&T Bell Labs начали разработку языка, известного нам всем сегодня как С. Она шла параллельно ранней разработке операционной системы Unix.
Своё название C получил от языка B, поскольку основные особенности были подчёркнуты именно из него. Причины создания языка C связывают с разработкой операционной системы Unix, которая изначально была реализована на ассемблере и не имела даже компилятора для высокоуровневых языков.
В 1971 году компилятор C и некоторые утилиты на этом языке были включены во вторую версию Unix. А в 1973 году ядро Unix было в большей степени написано на C.
1978. Книга — первая спецификация
Деннис Ритчи и Брайан Керниган выпустили первое издание книги "C Programming Language" 22 февраля 1978 года. Эта книга стала первым широкодоступным материалом по языку C.
Рисунок N1 – Обложка первого издания книги "Язык программирования C"
С момента выхода и до первой официальной сертификации языка эта книга была фактически стандартом для разработки на C, который назвали K&R — по первым буквам фамилий авторов.
Именно в этой книге, кстати, была представлена программа "Hello World", иллюстрирующая минимальную рабочую программу. После 1978 года почти каждая книга, посвящённая языкам программирования, не обходилась без подобного, а сегодня вывести на экран "Hello World" в качестве своей первой программы — укоренившаяся традиция.
#include <stdio.h>
main()
{
printf("Hello, World\n");
}
Также в K&R было представлено несколько языковых функций. Например, структуры — совокупность нескольких переменных, сгруппированных под единым именем для удобства обращения, а также типы данных long int и unsigned int.
В K&R C также произошло изменение операторов сложения/вычитания с присваиванием. Ранее для увеличения значения на единицу нужно было написать a =+ 1, однако такие операторы вводили в заблуждение компилятор C, да и разница между a =+ 1 (увеличение на единицу) и a = +1 (присваивание) с точки зрения человека выглядела довольно хрупкой. Именно в K&R C эти операторы превратились в привычные нам – a += 1.
Примеры кода в книге были оформлены в едином стиле, который также получил название по первым буквам фамилий создателей. Стиль K&R состоит в использовании восьми пробелов в качестве основного отступа (хотя чаще используются четыре пробела). Его также называют "kernel style", поскольку ядро Unix написано именно в таком стиле.
int foo(int is_bar)
{
if (is_bar) {
bar();
return 1;
} else {
return 0;
}
}
1979. C with classes
В мае 1979 года сотрудник Bell Labs Бьерн Страуструп начал заниматься разработкой системы, которая должна была стать первым Unix-кластером. Иными словами, началось создание системы распределённых вычислений, объединённой в общую сеть из нескольких компьютеров.
Проблема была в отсутствии подходящего для этого инструментария. Среди существующих языков программирования было два варианта, которые могли бы помочь решить эту задачу, но оба с нюансами. Язык BCPL был быстрым, но не подходил для крупных проектов, поскольку был довольно близок к языкам низкого уровня. Объектно-ориентированный язык программирования Simula, наоборот, подходил для задачи, но был довольно медленным. Поэтому Страуструп взялся за реализацию своего языка на основе C.
Уже в октябре 1979 года был готов препроцессор CPRE, добавляющий классы в C. Язык, на котором писался передаваемый на вход препроцессору код программы, получил название C with Classes. Однако он всё ещё рассматривался как расширение для C.
Помимо классов, в первой версии C with classes были добавлены:
производные классы, но пока без виртуальных функций;
контроль доступа;
конструкторы и деструкторы;
дружественные классы;
контроль и преобразование типов аргументов функции.
1983. ANSI C
Популярность языка возрастала:
C начал вытеснять Basic с позиции ведущего языка для программирования микрокомпьютеров;
появлялись компиляторы, созданные людьми, которые не участвовали непосредственно в разработке языка.
Но даже при такой популярности у C был только негласный стандарт K&R. Множество изменений, которые вносились в язык разработчиками компиляторов, были нестандартными. Поэтому начала нарастать проблема переносимости кода. С такой проблематикой и начал зарождаться первый стандарт языка C.
В 1983 году Американский национальный институт стандартов (ANSI) сформировал комитет для разработки спецификации. Процесс стандартизации закончился только в 1989 году, когда первый стандарт для языка был утверждён под названием "Язык программирования C" ANSI X3.159-1989. В том же году вышло второе издание книги "Язык программирования C", описывающее язык в том виде, в котором он представлен в стандарте. Через год этот стандарт с небольшими изменениями будет принят Международной организацией по сертификации (ISO).
Рисунок N2 – Обложка второго издания книги "Язык программирования C"
Помимо надмножества изменений в языке, произошедших со времён K&R, в стандарт вошли и абсолютно новые возможности, например, прототипы функций и усложнённый препроцессор. Также в стандарте появилось описание состава стандартной библиотеки, а также более точное определение средств, для которых подобного определения ранее дано не было.
1983. C with classes становится C++
В 1982 году Бьерн Страуструп начал работать над изменениями, которые должны были вывести C with classes из состояния, сформулированного самим создателем как "средний язык". С этого момента в течение года был разработан компилятор. Cfront увидел свет в 1983.
Его особенностью стало то, что он преобразовывал код C with classes в C. Сам же преобразованный код уже передавался компилятору языка C. Такой вариант давал возможность пользоваться компилятором большему количеству людей, а также использовать его на уже имеющейся Unix-инфраструктуре.
Язык сменил своё название несколько раз в процессе обновления. Сначала он назывался C84, поскольку в сообществе его периодически называли просто C. Однако это название тоже вводило в заблуждение, так как было больше похоже на новый стандарт C, чем на надмножество языка. Да и от проблемы прошлого названия это не избавляло. В итоге язык стал именоваться C++, то есть C и оператор инкремента.
При переходе от C with classes к C++ в языке появилось ещё некоторое количество новых возможностей:
виртуальные функции;
перегрузка функций и операторов;
ссылки;
константы;
улучшенный контроль типов;
контроль над распределением свободной памяти со стороны пользователя.
Также именно на этом этапе развития языка обосновался стиль комментариев, который используется и по сей день, причём не только в C++.
До начала официальной сертификации C++ жил в основном стараниями его создателя, который довольно оперативно отвечал на запросы программистского сообщества, а стандартными описаниями языка были выпускаемые Страуструпом печатные работы.
1985. "Язык программирования C++"
В феврале 1985 года вышла первая распространяемая версия C++. В октябре этого же года создатель языка Бьерн Страуструп выпустил первое издание книги "Язык программирования C++".
Рисунок N3 – Обложка первого издания книги "Язык программирования C++"
Она стала первым негласным стандартом для языка C++ подобно книге "Язык программирования C" Кернигана и Ритчи, и была им до первого официального стандарта.
В книге Страуструп рассказал о возможностях языка, а также о вопросах проектирования с точки зрения C++, приправляя каждый пункт большим количеством примеров кода.
С этого момента книга была переиздана ещё трижды, каждый раз дополняясь обновлениями языка, а последнее на момент написания статьи 4-е издание включает стандарт C++11.
1989. С++ 2.0
В июле 1989 года вышла версия компилятора Cfront 2.0, который был существенно переработан по сравнению с предыдущей версией, а также привнёс новые возможности в язык C++.
В это время уже вовсю обсуждались шаблоны, и никто не сомневался в том, что они будут реализованы, поскольку это не было большой сложностью. Но вот другая возможность языка — множественное наследование — была настоящим вызовом для создателей со стороны программистского сообщества. Многие говорили, что реализация множественного наследования в C++ невозможна, а в языке Simula, например, аналогичное предложение было отвергнуто из-за неизбежного усложнения сборщика мусора. Однако необходимость в добавлении множественного наследования действительно была, поскольку это сильно упростило бы разработку библиотек, код которых без него был довольно громоздким.
В результате множественное наследование всё-таки было добавлено, и именно в Cfront 2.0. Это дополнение языка вызвало полемику. Например, некоторые люди говорили, что, если в языке Smalltalk, олицетворяющем объектно-ориентированное программирование, нет множественного наследования, то в C++ его уже точно не должно быть. Причиной этой полемики создатель языка назвал слишком серьёзное отношение к множественному наследованию.
Также в этой версии C++ появились:
указатели на члены, позволявшие создавать указатели на нестатические поля и функции;
статические и const функции-члены;
модификатор доступа protected;
типобезопасное связывание;
абстрактные классы;
специфичные для классов new и delete;
1990. Borland C++
В 1990 году компания Borland выпустила интегрированную среду разработки Borland C++ для разработки программ на C и C++ (удивительно). Изначально она предназначалась для разработки под DOS, но позднее появилась и поддержка Windows.
Рисунок N4 - Скриншот из Borland С++
Исторически Borland C++ является потомком Turbo C, но имеет отличительные черты C++ в лице поддержки объектно-ориентированного программирования.
В каждой версии среды был свой компилятор, поддерживающий собственные стандарты. А за время своего развития Borland C++ получила множество специализированных библиотек для быстрой разработки приложений.
Последняя версия Borland C++ IDE вышла в 1997 году, после чего её заменила серия Borland C++ Builder. Финал же всего Borland C++ наступил в 2000 году с релизом Borland C++ 5.5 в комплекте с компилятором.
Borland C++ был тёплым ламповым редактором для DOS. Даже какая-никакая подсветка синтаксиса была.
Юрий Минаев, архитектор ядра C и C++ анализатора PVS-Studio
Через четыре года после Borland свою интегрированную среду разработки Visual C++ выпустят Microsoft.
1990. Шаблоны и механизм обработки исключений в C++
В 1986 году Бьерн Страуструп написал статью "What is Object-Oriented Programming?", в ходе которой озвучил три изъяна C++:
отсутствие поддержки параметризированных типов;
отсутствие стандартных механизмов обработки исключений;
отсутствие множественного наследования.
Множественное наследование было добавлено в 1989 с версией C++ 2.0, а остальные изъяны были исправлены уже в 1990 году новыми возможностями языка. Они были описаны Страуструпом в книге "Annotated C++ Reference Manual".
Столь близкое расстояние между появлением шаблонов и исключений на оси времени (шаблоны были утверждены в Сиэтле в июле, а механизм обработки исключений — в Пало Альто в ноябре) обусловлено тем, что оба эти нововведения решают схожие проблемы. По мнению самого создателя языка, шаблоны позволяют сократить количество ошибок во время выполнения программы, расширяя возможности статической системы типов, а исключения позволяют обработать оставшиеся ошибки.
Параметризованные типы рассматривались ещё в первой редакции C++, но их пришлось отложить из-за нехватки времени на достаточное изучение вопросов проектирования и реализации. По той же причине откладывались и исключения. И, видимо, не зря, ведь в итоге их проектирование длилось с 1984 по 1989 год.
Также в "Annotated C++ Reference Manual" были впервые представлены пространства имён и возможность идентификации типов во время исполнения.
Обобщённое программирование начинало набирать обороты в тот момент. C++ с шаблонами не был первопроходцем. Скорее наоборот, при создании шаблонов черпали идеи из языков программирования ML, CLU и Ada.
Однако можно с уверенностью сказать, что C++ стал популяризатором этой парадигмы. Шаблоны позволили во многих случаях писать код не через макросы и меньше стрелять себе в ноги. Ложка дёгтя — функционал, упрощающий работу с обобщённым программированием, полноценно появился только к C++20. Но даже это не остановило программистов, когда они узнали, что при помощи шаблонов можно унести вычисления на этап компиляции...
Исключения же в C++ не столь однозначны. К сожалению, они не решили всех проблем с обработкой ошибок по сравнению с ранее существовавшими подходами. Поток управления при их использовании становится нетривиальным. Есть куча способов выстрелить себе в ногу с ними: приватно отнаследоваться от исключения, захватить исключение по значению, расположить в неверном порядке catch-блоки и т.д. Как результат, некоторые стандарты кодирования запрещают их использование, например, Google C++ Code Style.
Филипп Хандельянц, тимлид направления разработки C и C++ анализатора PVS-Studio
"Annotated C++ Reference Manual" стал некоторым преддверием начала официальной стандартизации C++, описав все нововведения, которые уже в скором будущем стали стандартом для языка.
В следующей серии...
В этой статье мы рассмотрели события из истории C и C++ c 1969 по 1990 год. В следующей части охватим историю с 1991 и до наших дней:
книга Бьерна Страуструпа о том, как был создан C++;
C++98 — первый официальный стандарт;
C99 — C продолжает движение;
Technical Report 1 — новые функции С++ на бумаге;
Релиз Viva64, позднее — PVS-Studio;
C фиксирует достигнутое (C99, C11), а C++ продолжает движение (C++11, C++17);
C++20 и C++23.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Valerii Filatov. History of C and C++. Part one: emergence and standardization of C language. C with Classes becomes C++.
Комментарии (20)
vadimr
21.11.2024 07:54Стиль K&R состоит в использовании восьми пробелов в качестве основного отступа (хотя чаще используются четыре пробела). Его также называют "kernel style", поскольку ядро Unix написано именно в таком стиле.
Очень сомнительное утверждение. В то время не редкостью были терминалы не только по 80, но и по 40 символов в строке, на которых делать отступы по 4 и тем более 8 пробелов было бы безумием. Тем более, что это лишние байты на хранение. Там были споры, один или два пробела ставить.
Под "стилем K&R" подразумевается определённое выравнивание скобок и других лексических элементов относительно друг друга, а не конкретное количество пробелов.
Ну и тему Objective C надо было бы раскрыть в таком изложении.
kreofil
21.11.2024 07:54Тогда обычно пробелы не ставились, а ставилась табуляция. Редакторы также не заменяли табуляцию пробелами. Поэтому был только один символ. Ну и привычка. Специально посмотрел свой код 1992 года. Да. Везде табуляция. Правда сейчас она у меня отобразилось в смещение, равное двум пробелам.
vadimr
21.11.2024 07:54Табуляция понималась далеко не во всех системах, но во многом вы правы. Но 1992 год – это уже сильно после описываемых событий.
kreofil
21.11.2024 07:54В 1990 году появился Turbo C++ 1.0. Если память не изменяет Turbo сменилось на Borland где-то в районе третьей версии.
Explorus
21.11.2024 07:54Первый коммерческий компилятор C++ (если не считать Cfront) создала компания Zortech ( в октябре 1988), которую потом купила Symantec и продавала под своим брендом
Далее
май 1990 - Borland Turbo C++ 1.0
февраль 1991 - Borland C++ 2.0 (версии 1.0 не было, просто выровняли версии)
ноябрь 1991 - Borland C++ 3.0
февраль 1992 - Borland Turbo C++ 3.0 (версии 2.0 не было)
март 1992 - Microsoft C/C++ 7.0 (первый компилятор С++ от M$)
июнь 1992 - Borland Turbo C++ for Windows 3.1
июнь 1992 - Borland C++ 3.1 (появился OWL)
сентябрь 1992 - Borland Turbo C++ for Windows 4.0
декабрь 1993 - Borland C++ 4.0
и т.д. может мне стоит накатать статью-таймлайн сред разработки и компиляторов С++?unreal_undead2
21.11.2024 07:54А под коммерческие юниксы в конце 80-х - начале 90-х кроме Cfront ничего не было?
jbourne
21.11.2024 07:54Да. Стоит. Это поможет делу сильно. Ибо давно это было и не все всё правильно понят.
И в целом - статья годная.
assad77
21.11.2024 07:54На тему истории c++ лучше всего прочитать "дизайн и эволюция языка c++". Те части этой статьи, которые касаются непосредственно компилятора, там есть. И много чего ещё.
А книга хорошо написана и переведена, читается на одном дыхании.
feeelin Автор
21.11.2024 07:54Спасибо за комментарий! В том числе для этого текста некоторые детали были подчёркнуты из этой книги, а во второй части будет целый пункт про неё :)
vic_1
21.11.2024 07:54Грозит ли jetbrains участь Borland...
x86chk
21.11.2024 07:54Интересно, кто их может купить? Пока не знаю, кто может стать условным Embarcadero, чтобы поглотить JetBrains.
ivge
21.11.2024 07:541) Jetbrains - это IDE, язык Kotlin. Borland - это IDE, компиляторы и всякие инструменты управления.
2) Разные эпохи: 1995-2008 - большая переходная эпоха с появлением новых языков,
2024 - более-менее устоявшаяся конфигурация
В итоге:
Borland - неверные выборы в условиях быстрого развития отрасли, неудачная конкуренция с MS, другие неудачные попытки расширить свою нишу
Jetbrains - более четкая локализация на удачных продуктах, охватывающих основные ЯП, в условиях более стабильного развития; удачная кооперация с Google.
Конечно, неожиданности есть всегда, но пока картина выглядит вот так.jbourne
21.11.2024 07:54Не уверен, что сейчас JB легче:
- Есть MS VS и VS Code и они делают свое дело в отьедании рынка
- есть MS GitHub
- есть ИИ фичи, которые наверное у MS будут сильнее
- есть состояние в мире, которое нельзя назвать более стабильным мне кажется
- у JB тоже много продуктов, и нам не видно, каковы они по успешности
- на одной из последних конф Оракла они говорили, что сильно обновили Java экстеншины для VS Code и далее будут прокачивать
- в общем мне кажется, что MS хочет сожрать весь дев (source control, IDE, cloud, AI, ...) и активно к этому прет.А JB берут именно качеством самого продукта как по мне. И пока этот уровень качества важен для их пользователей - они убудт успешны.
В Java JetBrains сильно популярней и важней для юзеров, чем в том же JS, TS, Python.
qweururu
Сомнительное утверждение. Шаблоны мало общего имеют с остальными подходами в обобщённом программировании, поэтому неясно, как идеи для шаблонов могли черпаться не из шаблонов. Или в мл/ада были шаблоны?
unreal_undead2
Дженерики в Аде очень похожи. ML действительно заметно отличается, там генерируется один код, обрабатывающий разные типы.
qweururu
Ну генерики никак не могут быть похожи на шаблоны. Это тоже один код для разных типов. Разве что генериками там называется что-то иное.
unreal_undead2
Я конкретно про реализацию в Аде - там их надо явно инстанциировать для конкретных типов, при этом насколько понимаю генерируется отдельный код.
qweururu
Значит это не генерики. Хотя возможно терминология просто давняя.
unreal_undead2
Называются именно так.
Jijiki
Симула тоже повлиял на С++