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)


  1. qweururu
    21.11.2024 07:54

    C++ с шаблонами не был первопроходцем. Скорее наоборот, при создании шаблонов черпали идеи из языков программирования ML, CLU и Ada.

    Сомнительное утверждение. Шаблоны мало общего имеют с остальными подходами в обобщённом программировании, поэтому неясно, как идеи для шаблонов могли черпаться не из шаблонов. Или в мл/ада были шаблоны?


    1. unreal_undead2
      21.11.2024 07:54

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


      1. qweururu
        21.11.2024 07:54

        Ну генерики никак не могут быть похожи на шаблоны. Это тоже один код для разных типов. Разве что генериками там называется что-то иное.


        1. unreal_undead2
          21.11.2024 07:54

          Я конкретно про реализацию в Аде - там их надо явно инстанциировать для конкретных типов, при этом насколько понимаю генерируется отдельный код.


          1. qweururu
            21.11.2024 07:54

            Значит это не генерики. Хотя возможно терминология просто давняя.


            1. unreal_undead2
              21.11.2024 07:54

              Называются именно так.


    1. Jijiki
      21.11.2024 07:54

      Симула тоже повлиял на С++


  1. vadimr
    21.11.2024 07:54

    Стиль K&R состоит в использовании восьми пробелов в качестве основного отступа (хотя чаще используются четыре пробела). Его также называют "kernel style", поскольку ядро Unix написано именно в таком стиле.

    Очень сомнительное утверждение. В то время не редкостью были терминалы не только по 80, но и по 40 символов в строке, на которых делать отступы по 4 и тем более 8 пробелов было бы безумием. Тем более, что это лишние байты на хранение. Там были споры, один или два пробела ставить.

    Под "стилем K&R" подразумевается определённое выравнивание скобок и других лексических элементов относительно друг друга, а не конкретное количество пробелов.

    Ну и тему Objective C надо было бы раскрыть в таком изложении.


    1. kreofil
      21.11.2024 07:54

      Тогда обычно пробелы не ставились, а ставилась табуляция. Редакторы также не заменяли табуляцию пробелами. Поэтому был только один символ. Ну и привычка. Специально посмотрел свой код 1992 года. Да. Везде табуляция. Правда сейчас она у меня отобразилось в смещение, равное двум пробелам.


      1. vadimr
        21.11.2024 07:54

        Табуляция понималась далеко не во всех системах, но во многом вы правы. Но 1992 год – это уже сильно после описываемых событий.


  1. kreofil
    21.11.2024 07:54

    В 1990 году появился Turbo C++ 1.0. Если память не изменяет Turbo сменилось на Borland где-то в районе третьей версии.


    1. 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
      и т.д. может мне стоит накатать статью-таймлайн сред разработки и компиляторов С++?


      1. unreal_undead2
        21.11.2024 07:54

        А под коммерческие юниксы в конце 80-х - начале 90-х кроме Cfront ничего не было?


      1. jbourne
        21.11.2024 07:54

        Да. Стоит. Это поможет делу сильно. Ибо давно это было и не все всё правильно понят.

        И в целом - статья годная.


  1. assad77
    21.11.2024 07:54

    На тему истории c++ лучше всего прочитать "дизайн и эволюция языка c++". Те части этой статьи, которые касаются непосредственно компилятора, там есть. И много чего ещё.

    А книга хорошо написана и переведена, читается на одном дыхании.


    1. feeelin Автор
      21.11.2024 07:54

      Спасибо за комментарий! В том числе для этого текста некоторые детали были подчёркнуты из этой книги, а во второй части будет целый пункт про неё :)


  1. vic_1
    21.11.2024 07:54

    Грозит ли jetbrains участь Borland...


    1. x86chk
      21.11.2024 07:54

      Интересно, кто их может купить? Пока не знаю, кто может стать условным Embarcadero, чтобы поглотить JetBrains.


    1. ivge
      21.11.2024 07:54

      1) Jetbrains - это IDE, язык Kotlin. Borland - это IDE, компиляторы и всякие инструменты управления.
      2) Разные эпохи: 1995-2008 - большая переходная эпоха с появлением новых языков,
      2024 - более-менее устоявшаяся конфигурация
      В итоге:
      Borland - неверные выборы в условиях быстрого развития отрасли, неудачная конкуренция с MS, другие неудачные попытки расширить свою нишу
      Jetbrains - более четкая локализация на удачных продуктах, охватывающих основные ЯП, в условиях более стабильного развития; удачная кооперация с Google.
      Конечно, неожиданности есть всегда, но пока картина выглядит вот так.


      1. 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.