В каком году появился язык программирования c

Dennis M. Ritchie
Bell Labs/Lucent Technologies
Murray Hill. NJ 07974 США
dmr@bell-labs.com

АБСТРАКТНЫЙ

Язык программирования Си был разработан в начале 1970-х годов как язык реализации системы для зарождающейся операционной системы Unix. Выведенный из безтипного языка BCPL. Он развил структуру типов; созданный на крошечной машине как инструмент для улучшения скудной среды программирования. Он стал одним из доминирующих языков сегодняшнего дня. В данной статье исследуется его эволюция.

Введение

ПРИМЕЧАНИЕ: *Copyright 1993 Association for Computing Machinery, Inc. Эта электронная перепечатка предоставлена автором в качестве любезности.

Для получения дополнительных прав на публикацию свяжитесь с ACM или автором. Эта статья была представлена на Второй конференции по истории языков программирования, Кембридж. Массачусетс. Апрель 1993 года.
Затем она была собрана в сборнике трудов конференции: История языков программирования-II изд. Thomas J. Bergin. Jr. and Richard G. Gibson, Jr. ACM Press (New York) and Addison-Wesley (Reading. Mass), 1996; ISBN 0-201-89502-1.

Эта статья посвящена развитию языка программирования Си. Влиянию на него и условиям. В которых он был создан.

Для краткости я опущу полные описания самого языка С. Его родителя В [Johnson 73] и его прародителя BCPL [Richards 79]. А вместо этого сосредоточусь на характерных элементах каждого языка и на том. Как они развивались.

Язык Си появился на свет в 1969-1973 годах. Параллельно с ранним развитием операционной системы Unix; самый творческий период пришелся на 1972 год. Очередной всплеск изменений пришелся на период с 1977 по 1979 год. Когда была продемонстрирована переносимость системы Unix. В середине этого второго периода появилось первое широко доступное описание языка: Язык программирования Си, часто называют Наконец, в середине 1980-х годов язык был официально стандартизирован комитетом ANSI X3J11, который внес дальнейшие изменения. До начала 1980-х годов. Хотя компиляторы существовали для различных архитектур машин и операционных систем. Язык был почти исключительно связан с Unix; в последнее время его использование распространилось гораздо шире. И сегодня он входит в число языков. Наиболее часто используемых в компьютерной индустрии.

История: сеттинг

Конец 1960-х годов был бурной эрой для исследований компьютерных систем в Bell Telephone Laboratories [Ritchie 78] [Ritchie 84]. Компания выходила из проекта Multics [Organick 75]. Который начинался как совместное предприятие MIT. General Electric и Bell Labs; к 1969 году руководство Bell Labs и даже исследователи пришли к выводу. Что обещания Multics могут быть выполнены слишком поздно и слишком дорого. Еще до того. Как машина GE-645 Multics была удалена из помещения. Неофициальная группа. Возглавляемая главным образом Кеном Томпсоном. Начала изучать альтернативные варианты.

Томпсон хотел создать удобную вычислительную среду. Построенную по его собственному проекту. Используя любые доступные средства. Оглядываясь назад. Становится ясно. Что его планы включали в себя многие новаторские аспекты Мультитехники. В том числе явное представление процесса как локуса управления. Древовидную файловую систему. Интерпретатор команд как программу пользовательского уровня. Простое представление текстовых файлов и обобщенный доступ к устройствам. Они исключали другие. Такие как унифицированный доступ к памяти и файлам. На старте, к тому же. Он и остальные мы отложили еще один новаторский (хотя и не оригинальный) элемент Multics. А именно написание почти исключительно на языке более высокого уровня. PL/I, язык реализации Multics. Был не очень нам по вкусу. Но мы также использовали другие языки. Включая BCPL. И мы сожалели о потере преимуществ написания программ на языке выше уровня ассемблера. Таких как простота написания и ясность понимания. В то время мы не придавали большого значения портативности; интерес к этому возник позже.

Томпсон столкнулся с аппаратной средой. Тесной и спартанской даже для того времени: DEC PDP-7, на котором он начал работать в 1968 году. Был машиной с 8K 18-битными словами памяти и без какого-либо полезного для него программного обеспечения. Желая использовать язык более высокого уровня. Он написал оригинальную систему Unix на ассемблере PDP-7. На старте он даже не программировал на самом PDP-7, а вместо этого использовал набор макросов для ассемблера GEMAP на машине GE-635. Постпроцессор сгенерировал бумажную ленту. Считываемую PDP-7.

Эти кассеты переносились с машины GE на PDP-7 для тестирования. Пока не были созданы примитивное ядро Unix. Редактор, ассемблер. Простая оболочка (интерпретатор команд) и несколько утилит (например. Команды Unix rm, cat, cp). После этого операционная система стала самоподдерживающейся: программы можно было писать и тестировать. Не прибегая к бумажной ленте. И разработка продолжалась на самом PDP-7.

Ассемблер PDP-7 Томпсона превзошел даже DEC по простоте; он вычислял выражения и выдавал соответствующие биты. Не было ни библиотек. Ни загрузчика. Ни редактора ссылок: весь исходный код программы был представлен ассемблеру. И выходящий файл—с фиксированным именем—был непосредственно исполняемым. (Это название, a.out, немного объясняет этимологию Unix; это результат работы ассемблера. Даже после того. Как система получила компоновщик и средство явного указания другого имени. Оно оставалось исполняемым результатом компиляции по умолчанию.)

Вскоре после того. Как Unix впервые запустился на PDP-7, в 1969 году. Дуг Макилрой создал первый высокоуровневый язык новой системы: реализацию TMG МакКлюра [McClure 65]. TMG-это язык для написания компиляторов (в более общем смысле. Трансмогрификаторов) в стиле нисходящего рекурсивного спуска. Который сочетает в себе контекстно-свободную синтаксическую нотацию с процедурными элементами. Макилрой и Боб Моррис использовали TMG для написания первого компилятора PL/I для Multics.

Бросив вызов подвигу Макилроя в воспроизведении TMG. Томпсон решил. Что Unix—возможно. Он даже еще не был назван—нуждается в системном языке программирования. После быстро провалившейся попытки на Фортране он создал вместо этого свой собственный язык. Который назвал B. B можно представить как C без типов; точнее, это BCPL. Втиснутый в 8 тысяч байт памяти и отфильтрованный через мозг Томпсона. Его название. Скорее всего. Представляет собой сокращение BCPL. Хотя альтернативная теория утверждает. Что он происходит от Bon [Thompson 69]. Неродственного языка. Созданного Томпсоном во времена Multics. Бон, в свою очередь. Был назван либо в честь своей жены Бонни. Либо (согласно цитате из энциклопедии в ее руководстве) в честь религии. Ритуалы которой включают в себя бормотание магических формул.

Происхождение: языки

BCPL был разработан Мартином Ричардсом в середине 1960-х годов. Когда он посещал Массачусетский технологический институт. И использовался в начале 1970-х годов для нескольких интересных проектов. Среди которых операционная система OS6 в Оксфорде [Stoy 72] и части основополагающей работы Alto в Xerox PARC [Thacker 79]. Мы познакомились с ней потому. Что система MIT CTSS [Corbato 62]. Над которой работал Ричардс. Использовалась для разработки Multics. Оригинальный компилятор BCPL был доставлен как в Multics. Так и в систему GE-635 GECOS Раддом Канадеем и другими сотрудниками Bell Labs [Canaday 69]; во время последних мук жизни Multics в Bell Labs и сразу же после этого это был язык выбора среди группы людей. Которые позже будут вовлечены в Unix.

BCPL, B и C прочно вписываются в традиционное процедурное семейство. Типичное для Fortran и Algol 60. Они особенно ориентированы на системное программирование. Малы и компактно описаны и поддаются переводу простыми компиляторами. Они С меньшим успехом они также используют библиотечные процедуры для указания интересных управляющие конструкции. Такие как сопрограммы и замыкания процедур. В то же время их абстракции лежат на достаточно высоком уровне. Чтобы при осторожности можно было достичь переносимости между машинами.

BCPL, B и C синтаксически отличаются во многих деталях. Но в целом они похожи. Программы состоят из последовательности глобальных деклараций и деклараций функций (процедур). Процедуры могут быть вложены в BCPL. Но не могут ссылаться на нестатические объекты. Определенные в содержащих процедурах. B и C избегают этого ограничения. Вводя более строгое: никаких вложенных процедур вообще. Каждый из языков (за исключением самых ранних версий B) распознает отдельную компиляцию и предоставляет средства для включения текста из именованных файлов.

Некоторые синтаксические и лексические механизмы BCPL более изящны и регулярны. Чем у B и C. Например. Процедуры и объявления данных BCPL имеют более однородную структуру и обеспечивают более полный набор циклических конструкций. Хотя программы BCPL условно поставляются из неограниченного потока символов. Хитроумные правила позволяют большинству точек с запятой быть опущенными после операторов. Которые заканчиваются на границе строки. B и C опускают это удобство и заканчивают большинство операторов точкой с запятой. Несмотря на различия. Большинство утверждений и операторов BCPL отображают непосредственно в соответствующие B и C.

Некоторые структурные различия между BCPL и B проистекают из ограничений на промежуточную память. Например, декларации BCPL могут иметь вид

пусть P1-команда и P2 быть командой и P3 быть командой ... 

где текст программы. Представленный командами. Содержит целые процедуры. Субдекларации. Связанные и происходящие одновременно. Поэтому имя P3 известно внутри процедуры P1. Аналогично BCPL может упаковать группу объявлений и операторов в выражение. Которое дает значение, например

E1 := valof ( объявления ; команды ; resultis E2 ) + 1 

Компилятор BCPL легко обрабатывал такие конструкции. Сохраняя и анализируя разбираемое представление всей программы в памяти перед созданием вывода. Ограничения на хранение компилятора B требовали однопроходной техники. В которой вывод был сгенерирован как можно скорее. И синтаксический редизайн. Который сделал это возможным. Был перенесен в C.

Некоторые менее приятные аспекты BCPL были связаны с его собственными технологическими проблемами и сознательно избегались при разработке B. Например. BCPL использует механизм В этой схеме программист явно связывает имя каждой внешне видимой процедуры и объекта данных с числовым смещением в глобальном векторе; связывание выполняется в скомпилированном коде с помощью этих числовых смещений. Сначала Б избегал этого неудобства. Настаивая на том. Чтобы вся программа была представлена компилятору сразу. Более поздние реализации B и все реализации C используют обычный компоновщик для разрешения внешних имен. Встречающихся в файлах. Скомпилированных отдельно. Вместо того чтобы возлагать бремя назначения смещений на программиста.

Другие скрипки при переходе от BCPL к B были введены по вкусу. И некоторые остаются спорными, например. Решение использовать один символ = для назначения вместо :=. Аналогично, B использует /**/ для заключения комментариев. Где BCPL использует //, чтобы игнорировать текст до конца строки. Наследие PL/I здесь очевидно. (C++ возродил конвенцию о комментариях BCPL.) Fortran повлиял на синтаксис объявлений: объявления B начинаются со спецификатора. Такого как auto или static, за которым следовал список имен. И C не только следовал этому стилю. Но и украшал его. Помещая его ключевые слова типа в начале объявлений.

Не все различия между языком BCPL. Задокументированными в книге Ричардса [Richards 79] и B. Были преднамеренными; мы начали с более ранней версии BCPL [Richards 67]. Например, endcase, который ускользает от оператора BCPL switchon. Не присутствовал в языке. Когда мы изучали его в 1960-х годах. И поэтому перегрузка ключевого слова break. Чтобы вырваться из оператора B и C switch. Обязана дивергентной эволюции. А не сознательному изменению.

В отличие от всепроникающей синтаксической вариации. Которая произошла во время создания B. Основное семантическое содержание BCPL—его типовая структура и правила оценки выражений—осталось неизменным. Оба языка являются безтипными. Или, скорее. Имеют один тип данных. Память в этих языках состоит из линейного массива таких ячеек. И значение содержимого ячейки зависит от применяемой операции. + оператор, например. Просто добавляет свои операнды. Используя целочисленную команду add машины. И другие арифметические операции также не осознают фактического значения своих операндов. Поскольку память является линейным массивом. Можно интерпретировать значение в ячейке как индекс в этом массиве. И BCPL предоставляет оператор для этой цели. На языке оригинала оно писалось !, в то время как B использует унарный *. Таким образом. Если p-ячейка. Содержащая индекс (или адрес. Или указатель) другой ячейки, *p ссылается на содержимое указанной ячейки либо как на значение в выражении. Либо как на цель присваивания.

Поскольку указатели в BCPL и B — это просто целочисленные индексы в массиве памяти. Арифметика над ними имеет смысл: если p-адрес ячейки, то p+1 — адрес следующей ячейки. Это соглашение является основой для семантики массивов в обоих языках. Когда в BCPL пишут

пусть V = vec 10 

или в Б,

auto V[10]; 

эффект тот же: выделяется ячейка с именем V, затем выделяется другая группа из 10 смежных ячеек. И индекс памяти первой из них помещается в V. По общему правилу. В B выражение

*(V+i) 

добавляет V и iи ссылается на i-е место после V. И BCPL. И B каждый добавляют специальную нотацию. Чтобы подсластить такие обращения к массиву; в B эквивалентное выражение

V[i] 

а в BCPL

Ви!я 

Такой подход к массивам был необычен даже в то время; позже C ассимилирует его еще менее традиционным способом.

Ни один из BCPL. B или C не поддерживает символьные данные сильно в языке; каждый обрабатывает строки так же. Как векторы целых чисел. И дополняет общие правила несколькими соглашениями. Как в BCPL, так и в B строковый литерал обозначает адрес статической области. Инициализированной символами строки. Упакованными в ячейки. В BCPL первый упакованный байт содержит количество символов в строке; в B нет подсчета. И строки заканчиваются специальным символом. Который B пишется `*eЭто изменение было сделано отчасти для того. Чтобы избежать ограничения длины строки. Вызванного удержанием счетчика в 8 — или 9-битном слоте. А отчасти потому. Что поддержание счета казалось. По нашему опыту. Менее удобным. Чем использование терминатора.

Отдельные символы в строке BCPL обычно обрабатывались путем распространения строки в другой массив. По одному символу на ячейку. А затем переупаковки ее позже; B предоставлял соответствующие процедуры. Но люди чаще использовали другие библиотечные функции. Которые обращались к отдельным символам в строке или заменяли их.

Больше Истории

После того. Как версия TMG B работала. Томпсон переписал B сам по себе (шаг начальной загрузки). Во время разработки он постоянно боролся с ограничениями памяти: каждое добавление языка раздувало компилятор так. Что он едва мог поместиться. Но каждая перезапись. Использующая преимущества функции. Уменьшала его размер. Например, B ввел обобщенные операторы присваивания. Используя x=+y для добавления y к x. Запись пришла от Algol 68 [Wijngaarden 75] через Макилроя. Который включил ее в свою версию TMG. (В B и начале C оператор был написан =+ вместо += эта ошибка. Исправленная в 1976 году. Была вызвана соблазнительно простым способом обращения с первой формой в лексическом анализаторе Б.)

Томпсон пошел еще дальше. Изобретя ++ и-- операторы. Которые увеличивают или уменьшают; их префиксная или постфиксная позиция определяет. Происходит ли изменение до или после указания значения операнда. Они не были в самых ранних версиях B. Но появились по пути. Люди часто предполагают, что они были созданы для использования режимов автоматического приращения и автоматического декремента адресов, предоставляемых DEC PDP-11, на котором C и Unix впервые стали популярными. Это исторически невозможно, так как не было PDP-11, когда B был разработан. PDP-7, однако. Имел несколько ячеек памяти что косвенная ссылка на память через них увеличивает клетку. Эта особенность, вероятно. Подсказала Томпсону такие операторы; обобщение. Чтобы сделать их как префиксными. Так и постфиксными. Было его собственным. Действительно. Ячейки автоматического приращения не использовались непосредственно в реализации операторов. И более сильной мотивацией для инновации, вероятно. Было его наблюдение. Что перевод ++x был меньше. Чем перевод x=x+1.

Компилятор B на PDP-7 не генерировал машинные инструкции. А вместо этого Операции обычно—в частности. Для B—действуют на простой стековой машине.

В системе Unix PDP-7 только несколько вещей были записаны в B. Кроме самого B. Потому что машина была слишком маленькой и слишком медленной. Чтобы сделать больше. Чем эксперимент; переписывание операционной системы и утилит полностью в B было слишком дорогим шагом. Чтобы казаться возможным. В какой-то момент Томпсон облегчил хруст адресного пространства. Предложив компилятор Тем не менее. Появились некоторые утилиты. Написанные на языке B. Включая раннюю версию калькулятора переменной точности dc, знакомого пользователям Unix [McIlroy 79]. Самым амбициозным предприятием, которое я предпринял, был настоящий кросс-компилятор, который переводил B в машинные инструкции GE-635, а не потоковый код. Это был небольшой тур де форс: полный компилятор B. Написанный на своем собственном языке и генерирующий код для 36-битного мэйнфрейма. Который работал на 18-битной машине с 4K словами пользовательского адресного пространства. Этот проект был возможен только из-за простоты языка B и его системы времени выполнения.

Хотя мы время от времени думали о внедрении одного из основных языков того времени, таких как Fortran, PL/I или Algol 68, такой проект казался безнадежно большим для наших ресурсов: требовались гораздо более простые и небольшие инструменты. Все эти языки влияли на нашу работу. Но гораздо веселее было делать все самостоятельно.

К 1970 году проект Unix показал достаточно многообещающих результатов. Чтобы мы смогли приобрести новый DEC PDP-11. Процессор был одним из первых в линейке. Поставленной DEC. И прошло три месяца. Прежде чем появился диск. Выполнение программ B на нем с использованием потоковой техники требовало только написания фрагментов кода для операторов и простого ассемблера. Который я закодировал в B; вскоре dc стала первой интересной программой. Которая была протестирована. Прежде чем любая операционная система. На нашем PDP-11. Почти так же быстро. Все еще ожидая диск. Томпсон перекодировал ядро Unix и некоторые основные команды на ассемблере PDP-11. Из 24К байт памяти на машине самая ранняя система PDP-11 Unix использовала 12К байт для операционной системы. Крошечное пространство для пользовательских программ. А оставшуюся часть в качестве ОЗУ. Эта версия была только для тестирования. А не для реальной работы; машина отмечала время. Перечисляя закрытые рыцарские туры на шахматных досках различных размеров. Как только появился его диск. Мы быстро мигрировали на него после транслитерации команд ассемблера на диалект PDP-11 и переноса тех. Что уже были в B.

К 1971 году в нашем миниатюрном вычислительном центре появились пользователи. Мы все хотели. Чтобы создавать интересные программы было проще. Использование ассемблера было достаточно скучным. Чтобы B. Несмотря на проблемы с производительностью. Был дополнен небольшой библиотекой полезных служебных подпрограмм и использовался для все новых и новых программ. Среди наиболее заметных результатов этого периода была первая версия парсера-генератора yacc Стива Джонсона [Johnson 79a].

Проблемы B

Машины. На которых мы сначала использовали BCPL, а затем B. Были адресованы слову. И единственный тип данных этих языков. `ячейкаПоявление PDP-11 выявило несколько недостатков семантической модели В. Во-первых. Его механизмы обработки символов. Унаследованные с небольшими изменениями от BCPL. Были неуклюжими: использование библиотечных процедур для распространения упакованных строк в отдельные ячейки. А затем переупаковки или для доступа к отдельным символам и их замены. Стало чувствовать себя неловко. Даже глупо. На байт-ориентированной машине.

Во-вторых, хотя оригинальный PDP-11 не предусматривал арифметики с плавающей точкой. Производитель пообещал. Что вскоре он будет доступен. Операции с плавающей запятой были добавлены в BCPL в наших компиляторах Multics и GCOS путем определения специальных операторов. Но механизм был возможен только потому. Что на соответствующих машинах одно слово было достаточно большим. Чтобы содержать число с плавающей запятой; это не было верно на 16-битном PDP-11.

Наконец, модели B и BCPL подразумевали накладные расходы при работе с указателями: правила языка. Определяя указатель как индекс в массиве слов. Заставляли указатели быть представленными как индексы слов. Каждая ссылка на указатель генерирует преобразование шкалы времени выполнения из указателя в байтовый адрес. Ожидаемый аппаратным обеспечением.

По всем этим причинам, казалось. Что схема типизации была необходима. Чтобы справиться с символами и байтовой адресацией. А также подготовиться к появлению аппаратных средств с плавающей запятой. Другие вопросы. В частности безопасность типов и проверка интерфейса. Не казались тогда столь важными. Как стали позже.

Помимо проблем с самим языком. Метод многопоточного кода компилятора В давал программы настолько медленнее. Чем их аналоги на ассемблере. Что мы не учитывали возможность перекодирования операционной системы или ее центральных утилит на языке В.

В 1971 году я начал расширять язык B. Добавляя символьный тип. А также переписал его компилятор. Чтобы генерировать машинные инструкции PDP-11 вместо многопоточного кода. Таким образом. Переход от языка В к языку Си произошел одновременно с созданием компилятора. Способного создавать программы достаточно быстрые и маленькие. Чтобы конкурировать с языком ассемблера. Я назвал слегка расширенный язык NB. Что означает

Эмбриональный C

НБ существовал так недолго. Что полного его описания не было написано. Он предоставил типы int и char, массивы из них и указатели на них. Объявленные в стиле. Типизированном

int i, j; char c. D; int iarray[10]; int ipointer[]; чар каррей[10]; char cpointer[]; 

Семантика массивов осталась точно такой же. Как в B и BCPL: объявления iarray и carray создают ячейки. Динамически инициализируемые значением. Указывающим на первую из последовательности из 10 целых чисел и символов соответственно. Декларации для ipointer и cpointer опустите размер. Чтобы утверждать. Что хранилище не должно выделяться автоматически. В рамках процедур интерпретация указателей языка была идентична интерпретации переменных массива: объявление указателя создавало ячейку. Отличающуюся от объявления массива только тем. Что программист должен был назначить референт. А не позволять компилятору выделять пространство и инициализировать ячейку.

Значения, хранящиеся в ячейках. Привязанных к именам массивов и указателей. Были адресами машин. Измеренными в байтах. Соответствующей области хранения. Таким образом. Косвенное обращение через указатель не подразумевает никаких накладных расходов во время выполнения для масштабирования указателя от слова до смещения байта. С другой стороны. Машинный код для подписки на массив и арифметики указателей теперь зависел от типа массива или указателя: вычисление iarray[i] или ipointer+i подразумевало масштабирование надстройки i на размер объекта. На который ссылаются.

Эти семантики представляли собой легкий переход от Б. И я экспериментировал с ними в течение нескольких месяцев. Проблемы стали очевидны. Когда я попытался расширить нотацию типов. Особенно для добавления структурированных типов (записей). Структуры, казалось бы. Должны интуитивно сопоставляться с памятью в машине. Но в структуре. Содержащей массив. Не было подходящего места для хранения указателя. Содержащего основание массива. И не было никакого удобного способа организовать его инициализацию. Например, записи каталогов ранних Unix-систем могут быть описаны в C как

структура { int inumber; 

Я хотел, чтобы структура не просто характеризовала абстрактный объект. Но и описывала набор битов. Которые могут быть прочитаны из каталога. Где компилятор мог спрятать указатель на имя, которого требовала семантика? Даже если бы структуры мыслились более абстрактно и пространство для указателей можно было бы каким-то образом скрыть. Как бы я мог справиться с технической проблемой правильной инициализации этих указателей при выделении сложного объекта. Возможно такого. Который определял бы структуры. Содержащие массивы. Содержащие структуры на произвольную глубину?

Это решение явилось решающим скачком в эволюционной цепочке между безтипным BCPL и типизированным C. Он устранил материализацию указателя в хранилище и вместо этого вызвал создание указателя. Когда имя массива упоминается в выражении. Правило, которое сохранилось в современном языке Си. Состоит в том. Что значения типа массива преобразуются. Когда они появляются в выражениях. В указатели на первый из объектов. Составляющих массив.

Это изобретение позволило большинству существующих B-кодов продолжать работать. Несмотря на фундаментальный сдвиг в семантике языка. Несколько программ. Которые назначали новые значения имени массива. Чтобы настроить его происхождение—возможно в B и BCPL. Бессмысленно в C—были легко отремонтированы. Что еще более важно. Новый язык сохранил связное и работоспособное (хотя и необычное) объяснение семантики массивов. Открывая путь к более всеобъемлющей структуре типов.

Второе новшество. Которое наиболее четко отличает С от его предшественников. — это более полная структура типов и особенно ее выражение в синтаксисе объявлений. NB предложил основные типы int и charвместе с массивами из них и указателями на них. Но никаких дополнительных способов композиции. Требовалось обобщение: учитывая объект любого типа. Должна быть возможность описать новый объект. Который собирает несколько в массив. Выдает его из функции или является указателем на него.

Для каждого объекта такого составленного типа уже существовал способ упоминания базового объекта: индексировать массив. Вызывать функцию. Использовать оператор косвенного обращения к указателю. Аналогические рассуждения привели к синтаксису объявления для имен. Зеркально отражающему синтаксис выражения. В котором обычно появляются имена. Таким образом,

int i, *pi, **ppi; 

объявите целое число. Указатель на целое число. Указатель на указатель на целое число. Синтаксис этих объявлений отражает наблюдение. Что i, *piи **ppi все дают тип int при использовании в выражении. Аналогично,

int f(), *f(), (*f)(); 

объявите функцию. Возвращающую целое число, функцию. Возвращающую указатель на целое число. Указатель на функцию. Возвращающую целое число;

int *api[10]. (*pai)[10]; 

объявите массив указателей на целые числа и указатель на массив целых чисел. Во всех этих случаях объявление переменной напоминает ее использование в выражении. Тип которого называется в начале объявления.

Схема типовой композиции, принятая C, имеет значительный долг перед Algol 68, хотя, возможно. Она не появилась в форме. Которую бы одобрили сторонники Algol. Центральное понятие. Которое я захватил из Algol. Было структурой типа. Основанной на атомарных типах (включая структуры). Состоящих из массивов. Указателей (ссылок) и функций (процедур). Концепция союзов и бросков Алголя 68 также оказала влияние. Появившееся позже.

После создания системы типов. Соответствующего синтаксиса и компилятора для нового языка я почувствовал. Что он заслуживает нового названия; NB казалось недостаточно отличительным. Я решил следовать однобуквенному стилю и назвал его C. Оставив открытым вопрос. Представляет ли имя прогрессию через алфавит или через буквы в BCPL.

Неонатальный C

Быстрые изменения продолжались и после того. Как язык был назван, например. Введение операторов && и||. В BCPL и B оценка выражений зависит от контекста: внутри if и других условных операторов. Сравнивающих значение выражения с нулем. Эти языки помещают специальную интерпретацию операторов and (&) и or (|). В обычных контекстах они работают побитово. Но в операторе B

если (e1 и e2) ... 

компилятор должен вычислить e1, и если он ненулевой , вычислить e2, и если он тоже ненулевой. Разработать оператор. Зависящий от if. Требование рекурсивно нисходит на операторы & и | в пределах e1 и e2. Семантика короткого замыкания булевых операторов в таком контексте По предложению Алана Снайдера я ввел операторы && и||, чтобы сделать механизм более явным.

Их запоздалое введение объясняет неполноту правил приоритета C. В Б один пишет

если (a==b & c) ... 

чтобы проверить. Является ли a равным b и c ненулевым; в таком условном выражении лучше. Чтобы & имел более низкий приоритет, чем ==. При преобразовании из B в C нужно заменить & на && в таком операторе; чтобы сделать преобразование менее болезненным. Мы решили сохранить приоритет оператора & таким же относительно = =и просто немного разделить приоритет && от &. Сегодня кажется. Что было бы предпочтительнее переместить относительные прецеденты & и = =и тем самым упростить общую идиому C: чтобы проверить маскированное значение против другого значения . Нужно написать

if ((a&mask) == b) ... 

где внутренние скобки обязательны. Но легко забываются.

Многие другие изменения произошли около 1972-3 годов. Но наиболее важным было введение препроцессора. Отчасти по настоянию Алана Снайдера [Snyder 74]. А также в знак признания полезности механизмов включения файлов. Доступных в BCPL и PL/I. Его первоначальная версия была чрезвычайно простой и предоставляла только включенные файлы и простые замены строк: #include и #define беспараметрических макросов. Вскоре после этого он был расширен. В основном Майком Леском. А затем Джоном Райзером. Чтобы включить макросы с аргументами и условной компиляцией. Препроцессор изначально считался необязательным дополнением к самому языку. Действительно. В течение нескольких лет он даже не вызывался. Если исходная программа не содержала специального сигнала в начале. Такое отношение сохранялось и объясняет как неполную интеграцию синтаксиса препроцессора с остальным языком. Так и неточность его описания в ранних справочниках.

Портативность

К началу 1973 года основы современного языка Си были исчерпаны. Язык и компилятор были достаточно сильны. Чтобы позволить нам переписать ядро Unix для PDP-11 на языке Си летом того же года. (В 1972 году Томпсон предпринял краткую попытку создать систему. Закодированную в ранней версии языка Си—до появления структур—но оставил эту попытку.) Кроме того. В этот период компилятор был перенацелен на другие соседние машины. В частности Honeywell 635 и IBM 360/370; поскольку язык не мог жить изолированно. Были разработаны прототипы современных библиотек. В частности. Леск написал В 1978 году мы с Брайаном Керниганом опубликовали Язык программирования C [Керниган 78]. Хотя в ней не описывались некоторые дополнения. Которые вскоре стали общепринятыми. Эта книга служила справочником по языку. Пока более десяти лет спустя не был принят официальный стандарт. Несмотря на тесную совместную работу над этой книгой. Между нами существовало четкое разделение труда: Керниган написал почти весь пояснительный материал. А я отвечал за приложение. Содержащее справочное руководство и главу об интерфейсе с системой Unix.

В 1973-1980 годах язык немного вырос: структура типов приобрела типы unsigned, long. Union и enumeration. А структуры стали почти первоклассными объектами (не хватало только нотации для литералов). Не менее важные разработки появились в его окружении и сопутствующей технологии. Написание ядра Unix на языке Си дало нам достаточно уверенности в полезности и эффективности языка. Чтобы мы начали перекодировать утилиты и инструменты системы. А затем перенести наиболее интересные из них на другие платформы. Как описано в [Johnson 78a]. Мы обнаружили. Что самые сложные проблемы в распространении инструментов Unix заключаются не во взаимодействии языка Си с новым оборудованием. А в адаптации к существующему программному обеспечению других операционных систем. Таким образом . Стив Джонсон начал работать над pcc, компилятором C. Предназначенным для легкой перенастройки на новые машины [Johnson 78b]. В то время как он. Томпсон и я начали переносить саму систему Unix на компьютер Interdata 8/32.

Изменения языка в этот период. Особенно около 1977 года. Были в основном сосредоточены на соображениях переносимости и безопасности типов. Пытаясь справиться с проблемами. Которые мы предвидели и наблюдали при перемещении значительного объема кода на новую платформу Interdata. В то время еще проявлялись сильные признаки его безтипного происхождения. Указатели, например. Едва отличались от интегральных индексов памяти в ранних языковых руководствах или дошедшем до нас коде; сходство арифметических свойств указателей символов и беззнаковых целых чисел затрудняло чтобы не поддаться искушению опознать их. Беззнаковые типы были добавлены. Чтобы сделать беззнаковую арифметику доступной. Не путая ее с манипуляцией указателями. Аналогичным образом. Ранний язык допускал присвоения между целыми числами и указателями. Но эта практика стала не приветствоваться; нотация для преобразования типов (называемая Обманутый примером PL/I. Ранний C не привязывал указатели структуры твердо к структурам. На которые они указывали. И позволял программистам писать почти без учета типа указателя; такое выражение было принято некритически как ссылка на область памяти. Обозначенную указателем. В то время как имя члена указывало только смещение и тип.

Хотя первое издание K&R описывало большинство правил. Которые привели структуру типов C к ее нынешней форме. Многие программы. Написанные в более старом. Более расслабленном стиле. Сохранились. Как и компиляторы. Которые терпели это. Чтобы побудить людей уделять больше внимания правилам официального языка. Выявлять легальные. Но подозрительные конструкции и помогать обнаруживать несоответствия интерфейса. Которые невозможно обнаружить с помощью простых механизмов отдельной компиляции. Стив Джонсон адаптировал свой компилятор pcc для создания lint [Johnson 79b]. Который сканировал набор файлов и отмечал сомнительные конструкции.

Рост использования

Успех нашего эксперимента по переносимости на Interdata 8/32 вскоре привел к другому эксперименту Тома Лондона и Джона Райзера на DEC VAX 11/780. Эта машина стала гораздо более популярной. Чем Interdata. И Unix и язык C начали быстро распространяться как внутри AT&T. Так и за ее пределами. Хотя к середине 1970-х годов Unix использовался различными проектами в рамках системы Bell. А также небольшой группой ориентированных на исследования промышленных. Академических и правительственных организаций за пределами нашей компании. Его реальный рост начался только после того. Как была достигнута переносимость. Особенно следует отметить версии System III и System V из отдела новых компьютерных систем AT&T. Основанные на работе групп разработки и исследований компании. И серию выпусков BSD Калифорнийского университета в Беркли. Которые были получены от исследовательских организаций в Bell Laboratories.

В течение 1980-х годов использование языка Си широко распространилось. И компиляторы стали доступны практически для каждой архитектуры машин и операционных систем; в частности. Он стал популярным инструментом программирования для персональных компьютеров. Как для производителей коммерческого программного обеспечения для этих машин. Так и для конечных пользователей. Интересующихся программированием. В начале десятилетия почти каждый компилятор был основан на pcc Джонсона; к 1985 году появилось много независимо произведенных компиляторных продуктов.

Стандартизация

К 1982 году стало ясно. Что Си нуждается в формальной стандартизации. Лучшее приближение к стандарту. Первое издание K&R. Больше не описывало язык в реальном использовании; в частности. В нем не упоминались ни void, ни enum типы. В то время как он предвещал новый подход к структурам. Только после того. Как он был опубликован. Язык поддерживал их назначение. Передачу их в функции и из функций. А также связывание имен членов со структурой или объединением. Содержащим их. Хотя компиляторы. Распространяемые AT&T. Включили эти изменения. И большинство поставщиков компиляторов. Не основанных на pcc, быстро подхватили их. Не осталось полного. Авторитетного описания языка.

Первое издание K&R также было недостаточно точным по многим деталям языка. И стало все более непрактичным рассматривать pcc будучи Наконец. Начавшееся использование С в проектах. Подпадающих под коммерческие и государственные контракты, означало. Что имприматура официального стандарта была важной. Таким образом (по настоянию Доктора Макилроя) летом 1983 года ANSI учредила комитет X3J11 под руководством CBEMA с целью разработки стандарта C. X3J11 выпустил свой отчет [ANSI 89] в конце 1989 года. И впоследствии этот стандарт был принят ИСО как ISO/IEC 9899-1990.

С самого начала комитет X3J11 придерживался осторожного. Консервативного взгляда на расширение языка. К моему большому удовлетворению. Они серьезно отнеслись к своей цели:

X3J11 внес только одно действительно важное изменение в сам язык: он включил типы формальных аргументов в сигнатуру типа функции. Используя синтаксис. Заимствованный из C++ [Stroustrup 86]. В старом стиле внешние функции объявлялись так:

двойной грех(); 

что говорит только о том. Что sin-это функция. Возвращающая двойное (то есть с плавающей запятой двойной точности) значение. В новом стиле это лучше визуализируется

двойной грех(double sin); 

чтобы сделать тип аргумента явным и таким образом стимулировать лучшую проверку типа и соответствующее преобразование. Даже это дополнение. Хотя оно и дало заметно лучший язык. Вызвало трудности. Комитет справедливо считает. Что простое запрещение Неизбежный компромисс был настолько хорош. Насколько это было возможно. Хотя определение языка осложняется тем. Что допускает обе формы. И авторы портативного программного обеспечения должны бороться с компиляторами. Еще не доведенными до стандарта.

X3J11 также ввел множество небольших дополнений и корректировок, например. Квалификаторы типа const и volatile, а также немного отличающиеся правила продвижения типа. Тем не менее процесс стандартизации не изменил характера языка. В частности. Стандарт C не пытался формально определить семантику языка. И поэтому могут возникнуть споры по тонкостям; тем не менее. Он успешно учитывал изменения в использовании с момента первоначального описания и достаточно точен. Чтобы основывать реализации на нем.

Таким образом. Основной язык Си почти не пострадал от процесса стандартизации. И Стандарт появился скорее как лучшая. Тщательная кодификация. Чем новое изобретение. Более важные изменения произошли в окружении языка: препроцессоре и библиотеке. Препроцессор выполняет подстановку макросов. Используя соглашения. Отличные от остальных языков. Его взаимодействие с компилятором никогда не было хорошо описано. И X3J11 попытался исправить ситуацию. Результат заметно лучше. Чем объяснение в первом издании K&R; кроме того. Будучи более всеобъемлющим. Он обеспечивает операции. Такие как конкатенация токенов. Ранее доступные только по случайностям реализации.

X3J11 правильно полагал. Что полное и тщательное описание стандартной библиотеки Си так же важно. Как и работа над самим языком. Сам язык Си не обеспечивает ввода-вывода или какого-либо другого взаимодействия с внешним миром и. Таким образом. Зависит от набора стандартных процедур. На момент публикации K&R Си рассматривался в основном как системный язык программирования Unix; хотя мы приводили примеры библиотечных подпрограмм. Предназначенных для легкого переноса в другие операционные системы. Лежащая в основе поддержка Unix понималась неявно. Таким образом. Комитет X3J11 потратил большую часть своего времени на разработку и документирование набора библиотечных процедур. Которые должны быть доступны во всех соответствующих реализациях.

По правилам процесса стандартизации текущая деятельность комитета X3J11 ограничивается выпуском интерпретаций существующего стандарта. Однако неофициальная группа, первоначально созванная Рексом Яешке как NCEG (Numerical C Extensions Group), была официально принята в качестве подгруппы X3J11.1, и они продолжают рассматривать расширения до C. Как следует из названия. Многие из этих возможных расширений предназначены для того. Чтобы сделать язык более подходящим для численного использования: например. Многомерные массивы. Границы которых динамически определяются. Включение средств для работы с арифметикой IEEE и повышения эффективности языка на машинах с векторными или другими продвинутыми архитектурными особенностями. Не все возможные расширения являются конкретно числовыми; они включают в себя обозначения для структурных литералов.

Преемники

С и даже В имеют несколько прямых потомков. Хотя они не соперничают с Паскалем в создании потомства. Одна боковая ветвь развивалась рано. Когда в 1972 году Стив Джонсон посетил Университет Ватерлоо в творческом отпуске. Он привез с собой Б. Там он стал популярен на машинах Honeywell. А позже породил Eh и Zed (канадские ответы на вопрос Когда Джонсон вернулся в Bell Labs в 1973 году. Он был ошеломлен. Обнаружив, что язык. Семена которого он привез в Канаду. Эволюционировал на родине; даже его собственная программа yacc была переписана на Си Аланом Снайдером.

Более поздние потомки собственно C включают Параллельный C [Gehani 89]. Objective C [Cox 86]. C* [Thinking 90] и особенно C++ [Stroustrup 86]. Этот язык также широко используется в качестве промежуточного представления (по существу. Как переносимый язык ассемблера) для широкого спектра компиляторов. Как для прямых потомков. Таких как C++. Так и для независимых языков. Таких как Modula 3 [Nelson 91] и Eiffel [Meyer 88].

Критика

Две идеи наиболее характерны для языка Си среди языков его класса: связь между массивами и указателями и способ. Которым синтаксис объявления имитирует синтаксис выражения. Они также относятся к числу наиболее часто критикуемых его особенностей и часто служат камнем преткновения для новичка. В обоих случаях исторические случайности или ошибки усугубили их трудности. Самым важным из них была толерантность компиляторов языка Си к ошибкам в типе. Как должно быть ясно из приведенной выше истории. Си развился из языков без типов. Оно не возникло внезапно для своих самых ранних собратьев. пользователи и разработчики как совершенно новый язык со своими собственными правилами; вместо этого нам постоянно приходилось адаптировать существующие программы по мере развития языка и учитывать существующий массив кода. (Позже комитет ANSI X3J11 по стандартизации C столкнется с той же проблемой.)

Компиляторы в 1977 году и даже намного позже не жаловались на такие способы использования. Как назначение между целыми числами и указателями или использование объектов неправильного типа для ссылки на элементы структуры. Хотя определение языка. Представленное в первом издании K&R. Было разумно (хотя и не полностью) последовательным в его трактовке правил типа. Эта книга признала. Что существующие компиляторы не применяют их. Более того, некоторые правила. Призванные облегчить ранние переходы. Способствовали дальнейшей путанице. Например, пустые квадратные скобки в объявлении функции


являются живым ископаемым. Остатком способа NB объявить указатель; a, только в этом частном случае. Интерпретируется в C как указатель. Нотация сохранилась отчасти ради совместимости. Отчасти под тем предлогом. Что она позволит программистам сообщать своим читателям о намерении передать f указатель. Сгенерированный из массива. А не ссылку на одно целое число. К сожалению. Она служит не только для того. Чтобы сбить с толку слушателя. Но и для того. Чтобы насторожить читателя.

В K&R C предоставление аргументов надлежащего типа для вызова функции было обязанностью программиста. И существующие компиляторы не проверяли соответствие типов. Неспособность оригинального языка включить типы аргументов в сигнатуру типа функции была существенной слабостью. Действительно той. Которая требовала самого смелого и болезненного нововведения комитета X3J11 для исправления. Ранний дизайн объясняется (если не оправдан) моим избеганием технологических проблем. Особенно перекрестной проверки между отдельно скомпилированными исходными файлами. И моя неполная ассимиляция последствий перехода от нетипизированного к типизированному языку. Упомянутая выше программа lint попыталась облегчить эту проблему: среди других своих функций lint проверяет согласованность и когерентность всей программы путем сканирования набора исходных файлов. Сравнивая типы аргументов функций. Используемых в вызовах, с теми. Которые содержатся в их определениях.

Случайность синтаксиса способствовала воспринимаемой сложности языка. Оператор опосредования. Написанный * в C. Синтаксически является унарным префиксным оператором. Так же как и в BCPL и B. Это хорошо работает в простых выражениях. Но в более сложных случаях для управления синтаксическим анализом требуются круглые скобки. Например, чтобы отличить опосредование через значение. Возвращаемое функцией. От вызова функции. Обозначенной указателем. Пишется *fp() и (*pf)() соответственно. Стиль, используемый в выражениях. Передается в объявления. Поэтому имена могут быть объявлены

int *fp(); int (*pf)(); 

В более витиеватых. Но все же реалистичных случаях все становится еще хуже:

int *(*pfp)(); 

это указатель на функцию. Возвращающую указатель на целое число. Возникают два эффекта. Самое главное, что C имеет относительно богатый набор способов описания типов (по сравнению, скажем, с Pascal). Объявления в таких выразительных языках, как C—Algol 68, например,—описывают объекты. Одинаково трудные для понимания. Просто потому. Что сами объекты сложны. Второй эффект связан с деталями синтаксиса. Объявления на языке Си должны читаться в стиле Сетхи [Sethi 81] заметил. Что многие вложенные объявления и выражения стали бы проще. Если бы оператор косвенности был взят в качестве постфиксного оператора вместо префикса. Но к тому времени было уже слишком поздно что-либо менять.

Несмотря на трудности, я считаю. Что подход С к декларациям остается правдоподобным. И меня это устраивает; это полезный объединяющий принцип.

Другая характерная особенность C. Его обработка массивов. Более подозрительна на практических основаниях. Хотя она также имеет реальные достоинства. Хотя связь между указателями и массивами необычна. Ее можно изучить. Кроме того, язык демонстрирует значительную способность описывать важные понятия. Например, векторы. Длина которых изменяется во время выполнения. С помощью всего лишь нескольких основных правил и соглашений. В частности. Символьные строки обрабатываются теми же механизмами. Что и любой другой массив. Плюс соглашение о том. Что нулевой символ завершает строку. Интересно сравнить подход Си с подходом двух почти современных языков. Алгола 68 и Паскаля [Jensen 74]. Массивы в Algol 68 либо имеют фиксированные границы. Либо являются Позже это было частично исправлено. Хотя полученный язык еще не является универсальным.

C рассматривает строки как массивы символов. Обычно заканчивающихся маркером. Помимо одного специального правила об инициализации строковыми литералами. Семантика строк полностью подчиняется более общим правилам. Регулирующим все массивы. И в результате язык проще описать и перевести, чем язык. Включающий строку как уникальный тип данных. Некоторые затраты возникают из-за его подхода: некоторые строковые операции стоят дороже. Чем в других конструкциях. Потому что код приложения или библиотечная подпрограмма иногда должны искать конец строки. Потому что мало встроенных операций доступно. И потому что бремя управления хранением строк ложится более тяжело на пользователя. Тем не менее. Подход C к строкам работает хорошо.

С другой стороны. Обработка C массивов в целом (а не только строк) имеет печальные последствия как для оптимизации. Так и для будущих расширений. Преобладание указателей в программах на языке Си. Независимо от того. Объявлены ли они явно или получены из массивов, означает. Что оптимизаторы должны быть осторожны и должны использовать тщательные методы потока данных для достижения хороших результатов. Сложные компиляторы могут понять. Что может изменить большинство указателей. Но некоторые важные способы использования остаются трудными для анализа. Например, функции с аргументами указателя. Производными от массивы трудно скомпилировать в эффективный код на векторных машинах. Потому что редко можно определить. Что один указатель аргумента не перекрывает данные. Также упоминаемые другим аргументом. Или доступные извне. Более фундаментально. Определение C так конкретно описывает семантику массивов. Которые изменяются или расширяются. Рассматривая массивы как более примитивные объекты и разрешая операции над ними как целые. Становятся трудно вписывающимися в существующий язык. Даже расширения. Позволяющие объявлять и использовать многомерные массивы. Размер определяется динамически не совсем прямолинейно [MacDonald 89] [Ritchie 90]. Хотя они значительно облегчили бы написание числовых библиотек на языке C. Таким образом. Язык C охватывает наиболее важные области использования строк и массивов. Возникающие на практике с помощью единообразного и простого механизма. Но оставляет проблемы для высокоэффективных реализаций и расширений.

Конечно, в языке и его описании существует много меньших недостатков. Помимо тех. О которых говорилось выше. Есть также общие критические замечания. Которые выходят за рамки подробных пунктов. Главным из них является то. Что язык и его общепринятое окружение мало помогают при написании очень больших систем. Структура именования обеспечивает только два основных уровня: Промежуточный уровень видимости (в пределах одного файла данных и процедур) слабо связан с определением языка. Таким образом. Прямая поддержка модуляризации практически отсутствует. И проектировщики проектов вынуждены создавать свои собственные соглашения.

Аналогично, язык Си сам по себе обеспечивает две длительности хранения: Вне стека динамически выделяемое хранилище обеспечивается только библиотечной рутиной. И бремя управления им ложится на программиста: C враждебен автоматической сборке мусора.

Откуда Успех?

С стал успешным до такой степени. Что далеко превзошел любые ранние ожидания. Какие качества способствовали его широкому применению?

Несомненно, успех самого Unix был самым важным фактором; он сделал язык доступным для сотен тысяч людей. И наоборот, конечно. Использование Unix языка C и его последующая переносимость на широкий спектр машин были важны для успеха системы. Но вторжение языка в другие среды предполагает более фундаментальные достоинства.

Несмотря на некоторые аспекты. Загадочные для новичка. А иногда даже для адепта. Си остается простым и небольшим языком. Переводимым с помощью простых и маленьких компиляторов. Его типы и операции хорошо обоснованы в реальных машинах. И для людей. Привыкших к тому. Как работают компьютеры. Изучение идиом для создания экономичных по времени и пространству программ не представляет труда. В то же время язык достаточно абстрагирован от деталей машины. Чтобы обеспечить переносимость программы.

Не менее важно. Что C и его центральная библиотечная поддержка всегда оставались в контакте с реальной средой. Он был разработан не изолированно. Чтобы доказать свою точку зрения или служить примером. А как инструмент для написания программ. Которые делают полезные вещи; он всегда предназначался для взаимодействия с более крупной операционной системой и рассматривался как инструмент для создания более крупных инструментов. Скупой, прагматичный подход повлиял на то. Что вошло в C: он охватывает насущные потребности многих программистов. Но не пытается поставлять слишком много.

Наконец, несмотря на изменения. Которые он претерпел со времени своего первого опубликованного описания. Которое. По общему признанию. Было неофициальным и неполным. Фактический язык C. Как его видят миллионы пользователей. Использующих множество различных компиляторов. Остается удивительно стабильным и унифицированным по сравнению с аналогично распространенными языками. Например Pascal и Fortran. Существуют различные диалекты С—наиболее заметно. Терые описаны более старым K&R и более новым стандартом С—но в целом. С остается более свободным от собственных расширений. Чем другие языки. Возможно, наиболее значимыми расширениями являются квалификации указателей Хотя изначально язык Си не был разработан с целью переносимости в качестве основной цели. Он преуспел в выражении программ. Даже включая операционные системы. На машинах. Начиная от самых маленьких персональных компьютеров и заканчивая самыми мощными суперкомпьютерами.

C изворотлив. Ущербен и имеет огромный успех. Хотя случайности истории. Несомненно, помогли. Она, очевидно. Удовлетворяла потребность в языке реализации системы. Достаточно эффективном. Чтобы вытеснить язык ассемблера. Но достаточно абстрактном и беглом. Чтобы описать алгоритмы и взаимодействия в самых разнообразных средах.

Подтверждения

Стоит кратко суммировать роли непосредственных участников современного языка Си. Кен Томпсон создал язык B в 1969-70 годах; он был получен непосредственно из BCPL Мартина Ричардса. Деннис Ричи превратил B в C в 1971-73 годах. Сохранив большую часть синтаксиса B. Добавив типы и многие другие изменения. А также написав первый компилятор. Ричи, Алан Снайдер. Стивен К. Джонсон. Майкл Леск и Томпсон внесли свой вклад в языковые идеи в 1972-1977 годах. И портативный компилятор Джонсона по-прежнему широко используется. В этот период коллекция библиотечных рутин росла в значительной степени благодаря этим людям и многим другим в Bell Laboratories. В 1978 году Брайан Керниган и Ричи написали книгу. Которая на несколько лет стала определением языка. Начиная с 1983 года комитет ANSI X3J11 стандартизировал этот язык. Особенно заметными в поддержании его усилий были его офицеры Джим Броуди. Том Плам и Пи Джей Плаугер. А также последовательные редакторы проекта Ларри Рослер и Дейв Проссер.

Я благодарю Брайана Кернигана. Дуга Макилроя. Дейва Проссера. Питера Нельсона. Роба Пайка. Кена Томпсона и рецензентов ХОПЛА за советы при подготовке этой статьи.

Рекомендации

[ANSI 89]
Американский национальный институт стандартов, Американский национальный стандарт информационных систем—Язык программирования C, X3.159-1989.
[Андерсон 80]
B. Anderson. `Type syntax in the language C: an object lesson in syntactic innovation’. SIGPLAN Notices 15 (3). March, 1980, pp.
[Звонок 72]
J. R. Bell, `Threaded Code,’ C. ACM 16 (6), pp.
[Канадей 69]
Р. Х. Канадей и Д. М. Ричи. `Bell Laboratories BCPL
[Корбато 62]
F. J. Corbato. M. Merwin-Dagget. R. C. Daley. `Экспериментальная система разделения времениProc. SJCC, 1962, pp.
[Кокс 86]
B. J. Кокс и А. J. Новобильский, Объектно-ориентированное программирование: Эволюционный подход, Аддисон-Уэсли: Чтение, масс., 1986. Второе издание, 1991.
[Gehani 89]
Nh Gehani and W. D. Roome, Concurrent C, Silicon Press: Summit, NJ, 1989.
[Jensen 74]
K. Jensen and N. Wirth, Pascal Руководство пользователя и отчет, Springer-Verlag: Нью-Йорк, Гейдельберг, Берлин. Второе издание, 1974.
[Джонсон 73]
S. C. Johnson and B. W. Kernighan. `The Programming Language BТехнологии. Отчет №8, AT&T Bell Laboratories (январь 1973).
[Джонсон 78а]
С. С. Джонсон и Д. М. Ричи. `Переносимость программ C и системы UNIXTech. J. 57 (6) (часть 2). Июль-август 1978 года.
[Johnson 78b]
С. С. Джонсон. `Портативный компилятор: теория и практика
[Johnson 79a]
С. С. Джонсон. `Еще один компилятор-компиляторРуководстве программиста Unix, Седьмое издание, Том 2А. М. Д. Макилрой и Б. В. Керниган, ред. AT&T Bell Laboratories: Murray Hill, NJ, 1979.
[Johnson 79b]
S. C. Johnson, `Lint. A Program Checker’, in Unix Programmer’s Manual, Seventh Edition, Vol. 2B. M. D. McIlroy and B. W. Kernighan, eds. AT&T Bell Laboratories: Murray Hill, NJ, 1979.
[Kernighan 78]
B. W. Kernighan and D. M. Ritchie, The C Programming Language, Prentice-Hall: Englewood Cliffs, NJ, 1978. Второе издание, 1988.
[Керниган 81]
Керниган, Tech. Rep. #100, AT&T Bell Laboratories, 1981.
[Леск 73]
Леск,
[Макдональд 89]
Т. Макдональд. `Массивы переменной длиныTrans 1 (3). Dec. 1989, pp.
[МакКлюр 65]
R. M. McClure. `TMG—A Syntax Directed Compiler’. Proc. 20th ACM National Conf. (1965). Pp. 262-274.
[Макилрой 60]
M. D. McIlroy. `Macro Instruction Extensions of Compiler Languages’. C. ACM 3 (4), pp.
[Макилрой 79]
M. D. McIlroy and B. W. Kernighan, eds, Unix Programmer’s Manual, Седьмое издание, Vol. I, AT&T Bell Laboratories: Murray Hill, NJ, 1979.
[Мейер 88]
B. Meyer, Object-oriented Software Construction, Prentice-Hall: Englewood Cliffs, NJ, 1988.
[Нельсон 91]
G. Nelson, Systems Programming with Modula-3, Prentice-Hall: Englewood Cliffs, NJ, 1991.
[Органик 75]
E. I. Organick, The Multics System: An Examination of its Structure, MIT Press: Cambridge, Mass., 1975.
[Ричардс 67]
М. Ричардс, `Справочное руководство BCPL
[Ричардс 79]
М. Ричардс и К. Уитби-Стревенс, BCPL: The Language and its Compiler, Cambridge Univ. Пресса: Кембридж, 1979.
[Ричи 78]
Д. М. Ричи, `UNIX: ретроспективаTech. J. 57 (6) (часть 2). Июль-август 1978 года.
[Ричи 84]
Д. М. Ричи, `Эволюция системы разделения времени UNIXTech. J. 63 (8) (часть 2). Октябрь 1984 г.
[Ричи 90]
Д. М. Ричи, `Массивы переменного размера в языке СиТранс. 2 (2). Сентябрь 1990 г., стр.
[Сэти 81]
R. Sethi, `Uniform syntax for type expressions and declarators’, Softw. Prac. and Exp. 11 (6). Июнь 1981 г., стр.
[Снайдер 74]
A. Снайдер, Портативный компилятор для языка C, MIT: Cambridge, Mass., 1974.
[Стой 72]
J. E. Stoy and C. Strachey. `OS6—Экспериментальная операционная система для небольшого компьютера. Часть I: Общие принципы и структура,’ Comp J. 15, (Aug. 1972). Pp. 117-124.
[Страуструп 86]
B. Строструп, Язык программирования C++, Addison-Wesley: Reading. Mass., 1986. Второе издание, 1991.
[Такер 79]
С. П. Такер. Э. М. Маккрайт. Б. У. Лампсон. Р. Ф. Спраулл. Д. Р. Боггс, компьютерных структурах: Принципы и примеры, Д. Сиворек. К. Г. Белл, А. Ньюэлл. Макгроу-Хилл: Нью-Йорк, 1982.
[Думая 90]
C* Руководство по программированию, Thinking Machines Corp.: Cambridge Mass., 1990.
[Томпсон 69]
К. Томпсон, `Бон—интерактивный язык
[Wijngaarden 75]
A. van Wijngaarden, B. J. Mailloux, J. E. Peck, C. H. Koster, M. Sintzoff, C. Lindsey, L. G. Meertens, R. G. Fisker, `Revised report on the algorithmic language Algol 68,’ Acta Informatica 5, pp. 1-236.

Copyright © 2003 Lucent Technologies Inc. Все права защищены.