Непосредственный выигрыш от переносимости заключается в том, что для Unix-программ является нормой пережить свою исходную аппаратную платформу, поэтому не требуется каждые несколько лет заново изобретать инструменты и приложения. Сегодня приложения, первоначально написанные для Version 7 Unix (1979), регулярно используются не только на Unix-системах, "генетически" происходящих от V7, но и на таких вариантах системы, как Linux, в которой API-интерфейс операционной системы был написан на основании Unix-спецификации и не содержит кода, заимствованного у Bell Labs.
Косвенная выгода менее очевидна, но, возможно, более важна. Дисциплина переносимости склонна упрощать архитектуры, интерфейсы и реализации. Это увеличивает вероятность успешного проекта и сокращает затраты на обслуживание в течение жизненного цикла программ.
В данной главе рассматривается диапазон и история Unix-стандартов. Ниже обсуждается, какие стандарты до сих пор актуальны, и описываются области большего или меньшего расхождения в Unix API. Кроме того, здесь рассматриваются инструменты и практические приемы, которые используются Unix-разработчиками для сохранения переносимости кода, а также формулируются некоторые важные принципы хорошей практики.
17.1. Эволюция С
Главным фактом практики Unix-программирования всегда была стабильность языка С и небольшого количества служебных интерфейсов, которые всегда ему сопутствовали (особенно стандартная I/O-библиотека и подобные ей). Тот факт, что язык, созданный в 1973 году, в течение тридцати лет интенсивного использования потребовал небольших изменений, является действительно примечательным, в этом отношении язык С не имеет аналогов в компьютерной науке.
В главе 4 приводились аргументы в пользу того, что С достиг успеха, благодаря тому, что он выполняет роль тонкого связующего уровня над аппаратным обеспечением компьютера, приближающимся к "стандартной архитектуре" [4]. Однако для того чтобы понять другие факторы, необходимо кратко рассмотреть историю данного языка.
17.1.1. Ранняя история С
Язык С родился в 1971 году как язык системного программирования для PDP-11-варианта Unix и основывался на раннем интерпретаторе языка В, разработанного Кеном Томпсоном. Язык В, в свою очередь, был смоделирован с базового языка общего программирования (Basic Common Programming Language — BCPL), разработанного в Кембриджском университете в 1966–1967 годах[137].
Первоначальный C-компилятор Денниса Ритчи (который часто называли "DMR" по инициалам его создателя) обслуживал сообщество, быстро разрастающееся вокруг операционной системы Unix версий 5, 6 и 7. От шестой версии С языка произошла версия С компании Whitesmiths, повторная реализация, которая стала первым коммерческим C-компилятором и ядром IDRIS, первого клона Unix. Однако самые современные реализации С смоделированы на основе "переносимого C-компилятора" (Portable С Compiler — PCC) Стивена Джонсона (Steven С. Johnson), который дебютировал в седьмой версии и полностью заменил компилятор DMR как в ветви System V, так и в BSD-версиях четвертого поколения.
В 1976 году в шестой версии С были представлены объявления typedef
, union
и unsigned int
. Также подверглись изменениям утвержденный синтаксис для инициализации переменных и некоторые составные операторы.
Оригинальным описанием языка С была книга Брайана Кернигана и Денниса Ритчи
Белая книга описывала усовершенствованную шестую версию языка С с одним значительным исключением, которое касалось обработки общедоступных переменных. Первоначально Ритчи намеревался смоделировать правила С с COMMON- объявлений языка FORTRAN, основываясь на теоретическом соображении, что любая машина, способная поддерживать FORTRAN, будет готова к использованию С. В модели с общим блоком общедоступная переменная может быть объявлена несколько раз; идентичные объявления объединяются компоновщиком. Однако два ранних варианта С (для мэйнфреймов Honeywell и IBM 360) работали на машинах с очень ограниченной общей памятью или примитивным компоновщиком, или имели оба недостатка. Таким образом, компилятор Version 6 С был перенесен в боле строгую модель определения (которая требовала максимум одно определение любой заданной общедоступной переменной и ключевого слова extern
, задающего на нее ссылки), описанную в [42].