Вебинар: Использование статических анализаторов кода при разработке безопасного ПО - 19.12
C и C++ — культовые языки, на которых написано огромное количество кода. Но какой путь они прошли, чтобы стать таковыми? В этой статье расскажем о появлении C, начале его официальной стандартизации, а также о C with Classes и его окончательном превращении в C++.
1969 год. Аполлон-11 побывал на Луне. В это же время в лабораториях AT&T Bell Labs начали разработку языка, известного нам всем сегодня как С. Она шла параллельно ранней разработке операционной системы Unix.
Своё название C получил от языка B, поскольку основные особенности были подчёркнуты именно из него. Причины создания языка C связывают с разработкой операционной системы Unix, которая изначально была реализована на ассемблере и не имела даже компилятора для высокоуровневых языков.
В 1971 году компилятор C и некоторые утилиты на этом языке были включены во вторую версию Unix. А в 1973 году ядро Unix было в большей степени написано на C.
Деннис Ритчи и Брайан Керниган выпустили первое издание книги "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 года сотрудник Bell Labs Бьерн Страуструп начал заниматься разработкой системы, которая должна была стать первым Unix-кластером. Иными словами, началось создание системы распределённых вычислений, объединённой в общую сеть из нескольких компьютеров.
Проблема была в отсутствии подходящего для этого инструментария. Среди существующих языков программирования было два варианта, которые могли бы помочь решить эту задачу, но оба с нюансами. Язык BCPL был быстрым, но не подходил для крупных проектов, поскольку был довольно близок к языкам низкого уровня. Объектно-ориентированный язык программирования Simula, наоборот, подходил для задачи, но был довольно медленным. Поэтому Страуструп взялся за реализацию своего языка на основе C.
Уже в октябре 1979 года был готов препроцессор CPRE, добавляющий классы в C. Язык, на котором писался передаваемый на вход препроцессору код программы, получил название C with Classes. Однако он всё ещё рассматривался как расширение для C.
Помимо классов, в первой версии C with classes были добавлены:
Популярность языка возрастала:
Но даже при такой популярности у C был только негласный стандарт K&R. Множество изменений, которые вносились в язык разработчиками компиляторов, были нестандартными. Поэтому начала нарастать проблема переносимости кода. С такой проблематикой и начал зарождаться первый стандарт языка C.
В 1983 году Американский национальный институт стандартов (ANSI) сформировал комитет для разработки спецификации. Процесс стандартизации закончился только в 1989 году, когда первый стандарт для языка был утверждён под названием "Язык программирования C" ANSI X3.159-1989. В том же году вышло второе издание книги "Язык программирования C", описывающее язык в том виде, в котором он представлен в стандарте. Через год этот стандарт с небольшими изменениями будет принят Международной организацией по сертификации (ISO).
Рисунок N2 – Обложка второго издания книги "Язык программирования C"
Помимо надмножества изменений в языке, произошедших со времён K&R, в стандарт вошли и абсолютно новые возможности, например, прототипы функций и усложнённый препроцессор. Также в стандарте появилось описание состава стандартной библиотеки, а также более точное определение средств, для которых подобного определения ранее дано не было.
В 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++. В октябре этого же года создатель языка Бьерн Страуструп выпустил первое издание книги "Язык программирования C++".
Рисунок N3 – Обложка первого издания книги "Язык программирования C++"
Она стала первым негласным стандартом для языка C++ подобно книге "Язык программирования C" Кернигана и Ритчи, и была им до первого официального стандарта.
В книге Страуструп рассказал о возможностях языка, а также о вопросах проектирования с точки зрения C++, приправляя каждый пункт большим количеством примеров кода.
С этого момента книга была переиздана ещё трижды, каждый раз дополняясь обновлениями языка, а последнее на момент написания статьи 4-е издание включает стандарт C++11.
В июле 1989 года вышла версия компилятора Cfront 2.0, который был существенно переработан по сравнению с предыдущей версией, а также привнёс новые возможности в язык C++.
В это время уже вовсю обсуждались шаблоны, и никто не сомневался в том, что они будут реализованы, поскольку это не было большой сложностью. Но вот другая возможность языка — множественное наследование — была настоящим вызовом для создателей со стороны программистского сообщества. Многие говорили, что реализация множественного наследования в C++ невозможна, а в языке Simula, например, аналогичное предложение было отвергнуто из-за неизбежного усложнения сборщика мусора. Однако необходимость в добавлении множественного наследования действительно была, поскольку это сильно упростило бы разработку библиотек, код которых без него был довольно громоздким.
В результате множественное наследование всё-таки было добавлено, и именно в Cfront 2.0. Это дополнение языка вызвало полемику. Например, некоторые люди говорили, что, если в языке Smalltalk, олицетворяющем объектно-ориентированное программирование, нет множественного наследования, то в C++ его уже точно не должно быть. Причиной этой полемики создатель языка назвал слишком серьёзное отношение к множественному наследованию.
Также в этой версии 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.
В 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 и до наших дней:
0