Производственно-внедренческий кооператив

"И Н Т Е Р Ф Е Й С"

Диалоговая Единая Мобильная

Операционная Система

Демос/P 2.1


Язык программирования Си.

Москва

1988


Описан универсальный язык программирования Си. Приве-
дены структура и синтаксис языка, правила написания прог-
рамм, даны начальные сведения о взаимодействии программ на
Си с операционной системой Демос.








    * 1. ВВЕДЕНИЕ



Язык Си - это универсальный язык программирования, для
которого характерны экономичность выражения, современный
набор операторов и типов данных. Язык Си не является ни
языком "очень высокого уровня", ни "большим" языком, и не
предназначается для некоторой специальной области примене-
ния, но отсутствие ограничений и общность языка делают его
для многих задач более удобным и эффективным, чем языки,
предположительно более мощные. Операционная система, компи-
лятор с языка Си и по существу все прикладные программы сис-
темы "ДЕМОС" написаны на Си. Язык Си не связан с какими-
либо определенными аппаратными средствами или системами, и
на нем легко писать программы, которые можно пропускать без
изменений на любой ЭВМ, имеющей Си-компилятор.

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

Язык Си - это язык относительно "низкого уровня". Это
означает, что Си имеет дело с объектами того же вида, что и
большинство ЭВМ, а именно, с символами, числами и адресами.
Они могут объединяться и пересылаться посредством обычных
арифметических и логических операций, осуществляемых реаль-
ными ЭВМ.

В языке Си отсутствуют операции, имеющие дело непос-
редственно с составными объектами, такими как строки симво-
лов, множества, списки или с массивами, рассматриваемыми как
целое. Здесь, например, нет никакого аналога операциям PL/1,
оперирующим с массивами и строками. Язык не предоставляет
никаких других возможностей распределения памяти, кроме ста-
тического определения и механизма стеков, обеспечиваемого
локальными переменных функций. Сам по себе язык Си не обес-
печивает никаких возможностей ввода-вывода. Все эти меха-
низмы высокого уровня должны обеспечиваться явно вызываемыми
функциями.

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

Удержание языка в скромных размерах дает реальные преи-
мущества. Так как Си относительно мал, он не требует много
места для своего описания и может быть быстро выучен.


-1-


Компилятор с Си может быть простым и компактным. Это обес-
печивает высокую степень мобильности языка. Поскольку типы
данных и структуры управления, имеющиеся в Си, непосредст-
венно поддерживаются большинством существующих ЭВМ, библио-
тека, необходимая во время прогона изолированных программ,
оказывается очень маленькой. На СМ-4, например , она содер-
жит только программы для 32-битового умножения и деления и
для упрятывания и восстановления регистров при входе в функ-
цию. Конечно, каждая реализация обеспечивает исчерпывающую,
совместимую библиотеку функций для выполнения операций
ввода-вывода, обработки строк и распределения памяти, но так
как обращение к ним осуществляется только явно, можно, если
необходимо, избежать их вызова; эти функции могут быть ком-
пактно написаны на самом Си.

Опять же из-за того, что язык Си отражает возможности
современных компьютеров, программы на Си оказываются доста-
точно эффективными, так что не возникает побуждения писать
вместо этого программы на языке ассемблера. Хотя Си соот-
ветствует возможностям многих ЭВМ, он не зависит от какой-
либо конкретной архитектуры машины и в силу этого без особых
усилий позволяет писать "переносимые" программы, т.е. прог-
раммы, которые можно пропускать без изменений на различных
аппаратных средствах.

Язык Си не является языком со строгими типами данных в
смысле Паскаля или Алгола-68. Он сравнительно снисходителен
к преобразованию данных, хотя и не будет буйно преобразовы-
вать типы данных подобно языку PL/1. Компилятор не предус-
матривает никакой проверки индексов массивов, типов аргумен-
тов и т.д. во время выполнения программы.

В тех ситуациях, когда желательна строгая проверка
типов, используется специальная программа lint. Программа
lint не генерирует машинного кода, а делает очень строгую
проверку всех тех сторон программы, которые можно проконтро-
лировать во время компиляции и загрузки. Она определяет
несоответствие типов, несовместимость аргументов, неисполь-
зованные или очевидным образом неинициализированные перемен-
ные, потенциальные трудности переносимости и т.д.

Из за того, что в языке отсутствуют средства
ввода/вывода и т.п., при программировании на нем существен-
ную роль играет библиотека стандартных программ, осуществля-
ющих взаимодействие с системой. Во всех системах, совмести-
мых с ОС UNIX, к которым относится и ДЕМОС, существует сов-
местимый набор программ для ввода/вывода, управления
памятью, преобразования данных и выполняющих другие функции,
использование которых обеспечивает возможность переноса
программ на другие ЭВМ.

В данном документе описывается язык Си, расширения,
обеспечиваемые специальным препроцессором (фактически они


-2-


вошли уже в понятие "язык Си"), стандартная библиотека
ввода/вывода, и даются начальные сведения о взаимодействии
программ на Си с ОС ДЕМОС. Полное описание библиотечных
программ имеется в руководстве программиста ОС ДЕМОС (части
3 и 4), и в оперативной документации man(2) и man(3). Хоро-
шим учебником по языку Си является книга [1], краткое фор-
мальное описание языка приведено в [2].

В тексте встречаются примечания, относящиеся к реализа-
ции языка Си в ОС ДЕМОС. Такие примечания выделяются верти-
кальной чертой справа (как выделен данный абзац).

    * 2. СИНТАКСИЧЕСКАЯ НОТАЦИЯ



В используемой в этом руководстве синтаксической нота-
ции синтаксические категории записываются русскими буквами и
символом "_", а все остальные символы рассматриваются как
литерные (то есть изображающие сами себя). Альтернативные
категории перечисляются на отдельных строчках. Необязатель-
ный символ, терминальный или нетерминальный, указывается
индексом "необ", так что

{ выражение }
необ

указывает на необязательное выражение, заключенное в фигур-
ных скобках. Синтаксис описывается в Приложении 1.

Если описание не помещается на одной строке, оно про-
должается на следующей с некоторым сдвигом вправо, например:

описание_структуры:
спецификатор_типа
список_описателей_структуры

Здесь следует читать:

описание_структуры:
спецификатор_типа список_описателей_структуры

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

IBM/370 (OS-360) 7 символов, 1 регистр
VAX 11 (UNIX) 7 символов, 2 регистра


    2.1. Ключевые слова



Следующие идентификаторы зарезервированы для использо-
вания в качестве ключевых слов и не могут использоваться
иным образом:


-3-


int extern else
char register for
float typedef do
double static while
struct goto switch
union return case
long sizeof default
short break entry
unsigned continue
auto if

Ключевое слово entry в настоящее время не используется
каким-либо компилятором; оно зарезервировано для использова-
ния в будущем. В некоторых реализациях резервируются также
слова fortran и asm.

    2.2. Константы



Имеется несколько видов констант, которые перечислены
ниже.

    2.2.1. Целые константы



Целая константа, состоящая из последовательности цифр,
считается восьмеричной, если она начинается с 0 (цифра
нуль), и десятичной в противном случае. Цифры 8 и 9 имеют
восьмеричные значения 10 и 11 соответственно. Последова-
тельность цифр, которой предшествуют символы (нуль, х-
маленькое) или (нуль X-большое), рассматривается как
шестнадцатиричное целое. Шестнадцатиричные цифры включают
буквы от a (маленькое) или A (большое) до f (маленькое) или
F (большое) со значениями от 10 до 15. Десятичная константа,
величина которой превышает наибольшее машинное целое со зна-
ком, считается длинной; восьмеричная или шестнадцатиричная
константа, которая превышает наибольшее машинное целое без
знака, также считается длинной.

    2.2.2. Длинные (long) константы



Десятичная, восьмеричная или шестнадцатиричная конс-
танта, за которой непосредственно следует l (эль-маленькое)
или L (эль-большое), является длинной константой. На некото-
рых машинах целые и длинные значения могут рассматриваться
как идентичные.

    2.2.3. Символьные константы



Символьная константа - это символ, заключенный в оди-
ночные кавычки, как, например, 'х'. Значением символьной
константы является численное значение этого символа в машин-
ном представлении набора символов.

-4-


Некоторые неграфические символы, одиночная кавычка ' и
обратная косая черта \ могут быть представлены двумя симво-
лами в соответствии со следующей таблицей условных последо-
вательностей:

Название Код Обозначение

новая строка 012 \n
горизонтальная табуляция 011
символ возврата на одну 010
позицию
возврат каретки 015 \r
переход на новую страницу 014 \f
обратная косая черта 0133 \\
одиночная кавычка 047 \'
произвольный символ 0ddd \ddd


Условная последовательность \ddd состоит из обратной
косой черты, за которой следуют 1, 2 или 3 восьмеричных
цифры, которые рассматриваются как задающие значение желае-
мого символа. Специальным случаем этой конструкции является
последовательность \0 (за нулем не следует цифра), которая
определяет нулевой символ. Если следующий за обратной косой
чертой символ не совпадает с одним из указанных, то обратная
косая черта игнорируется.

    2.2.4. Вещественные константы



Вещественная константа состоит из целой части, десятич-
ной точки, дробной части, буквы e (маленькая) или E (боль-
шая) и целой экспоненты с необязательным знаком. Как целая,
так и дробная часть являются последовательностью цифр. Либо
целая, либо дробная часть (но не обе) может отсутствовать;
либо десятичная точка, либо e и экспонента (но не то и дру-
гое одновременно) может отсутствовать. Вещественные конс-
танты в большинстве реализаций считаются константами двойной
точности.

    2.3. Строки



Строка - это последовательность символов, заключенная в
двойные кавычки, как, например, "...". Строка имеет тип мас-
сив символов и класс памяти static (см. ниже). Строка иници-
ализирована указанными в ней символами. Все строки, даже
идентично записанные, считаются различными. Компилятор
помещает в конец каждой строки нулевой байт \0, с тем чтобы
просматривающая строку программа могла определить ее конец.
Перед стоящим внутри строки символом двойной кавычки " дол-
жен быть поставлен символ обратной косой черты \; кроме
того, могут использоваться те же условные последователь-
ности, что и в символьных константах. Обратная косая черта
\, за которой непосредственно следует символ новой строки,


-5-


игнорируется.

Имеются макропроцессорные средства, позволяющие объеди-
нять совпадающие строки при трансляции с целью экономии
памяти (см. команду xstr).

    2.4. Характеристики аппаратных средств



Следующая ниже таблица суммирует некоторые свойства
аппаратного оборудования, которые меняются от машины к
машине. Хотя они и влияют на переносимость программ, на
практике они представляют меньшую проблему, чем это может
казаться заранее.
Таблица 1.
-----------------------------------------
| CM-ЭВМ IBM 370 (OS) VAX-11 |
| КОИ-8 ebcdic ASCII |
| char 8 бит 8 бит 8 бит |
| int 16 32 32 |
| short 16 16 16 |
| long 32 32 32 |
| float 32 32 32 |
| double 64 64 64 |
| range -38/+38 -76/+76 -76/+76 |
|_______________________________________|

    * 3. ОБ'ЕКТЫ ЯЗЫКА СИ



    3.1. Интерпретация идентификаторов



С каждым идентификатором в Си связано два атрибута: его
класс памяти и его тип. Класс памяти определяет место и
время хранения памяти, связанной с идентификатором; тип
определяет смысл величин, находящихся в памяти, определенной
под идентификатором.

Имеются четыре класса памяти: автоматическая, статичес-
кая, внешняя и регистровая. Автоматические переменные явля-
ются локальными для каждого вызова блока и исчезают при
выходе из этого блока. Статические переменные являются
локальными, но сохраняют свои значения даже после того, как
управление передается за пределы блока. Внешние переменные
существуют и сохраняют свои значения в течение выполнения
всей программы и могут использоваться для связи между функ-
циями, в том числе и между независимо скомпилированными
функциями. Регистровые переменные хранятся (если это воз-
можно) в быстрых регистрах машины; подобно автоматическим
переменным они являются локальными для каждого блока и исче-
зают при выходе из этого блока.

В языке Си предусмотрено несколько основных типов
объектов:

-6-


Символьный.
- Объекты, описанные как символы (char), достаточно
велики, чтобы хранить любой член из соответствующего
данной реализации внутреннего набора символов, и если
действительный символ из этого набора символов хра-
нится в символьной переменной, то ее значение эквива-
лентно целому коду этого символа. В символьных пере-
менных можно хранить и другие величины, но реализация
будет машинно-зависимой. (На СМ ЭВМ значение символь-
ных переменных изменяется от -0177 до 0177.)

Целый.
- Можно использовать до трех размеров целых, описывае-
мых как short int, int и long int. Длинные целые
занимают не меньше памяти, чем короткие, но в конк-
ретной реализации может оказаться, что либо короткие
целые, либо длинные целые, либо те и другие будут
эквивалентны простым целым. "Простые" целые имеют
естественный размер, предусматриваемый архитектурой
используемой машины; другие размеры вводятся для
удовлетворения специальных потребностей.

Беззнаковый.
- Целые без знака, описываемые как unsigned, подчиня-
ются законам арифметики по модулю 2**n, где n - число
битов в их представлении. (На CM-ЭВМ длинные вели-
чины без знака не предусмотрены).

Вещественный.
- Вещественные одинарной точности (float) и веществен-
ные двойной точности (double) в некоторых реализациях
могут быть синонимами. (На СМ ЭВМ float занимает 32
бита памяти, а double - 64).

В языке нет логического типа данных, а в качестве логических
значений используются целые "0" - "ложь" и "1" - "истина"
(при проверках любое целое, не равное 0, трактуется как
"истина").

Поскольку объекты упомянутых выше типов могут быть
разумно интерпретированы как числа, эти типы будут назы-
ваться арифметическими. Типы char и int всех размеров сов-
местно будут называться целочисленными. Типы float и double
совместно будут называться вещественными типами.

Кроме основных арифметических типов существует концеп-
туально бесконечный класс производных типов, которые образу-
ются из основных типов следующим образом:

- массивы объектов большинства типов;

- функции, которые возвращают объекты заданного типа;

-7-


- указатели на объекты данного типа;

- структуры, содержащие последовательность объектов
различных типов;

- объединения, способные содержать один из нескольких
объектов различных типов.

Вообще говоря, эти методы построения объектов могут
применяться рекурсивно.

    3.2. Объекты и l_значения



Объект является доступным обработке участком памяти;
l_значение (левое значение) - это выражение, ссылающееся на
объект. Очевидным примером выражения l_значения является
идентификатор. Существуют операции, результатом которых
являются l_значения; если, например, e - выражение типа ука-
затель, то *e является выражением l_значения, ссылающимся на
тот объект, на который указывает е. Название "l_значение"
происходит от выражения присваивания e1=e2, в котором левая
часть должна быть выражением l_значения. При последующем
обсуждении каждой операции будет указываться, ожидает ли она
операндов l_значения и выдает ли она l_значение.

    3.3. Преобразования



Ряд операций может в зависимости от своих операндов
вызывать преобразование значения операнда из одного типа в
другой. В этом разделе объясняются результаты, которые сле-
дует ожидать от таких преобразований. В конце подводятся
итоги преобразований, требуемые большинством обычных опера-
ций; эти сведения дополняются необходимым образом при обсуж-
дении каждой операции.

    3.3.1. Символы и целые



Символ или короткое целое можно использовать всюду, где
можно использовать целое. Во всех случаях значение преобра-
зуется к целому. Преобразование более короткого целого к
более длинному всегда сопровождается знаковым расширением;
целые являются величинами со знаком. Осуществляется или нет
знаковое расширение для символов, зависит от используемой
машины, на СМ-ЭВМ такое преобразование осуществляется так,
что русские буквы при прямом преобразовании получат отрица-
тельные коды. Область значений символьных переменных на
CM-ЭВМ меняется от -128 до 127; символы из набора ASCII
имеют положительные значения. Символьная константа, задан-
ная с помощью восьмеричной условной последовательности, под-
вергается знаковому расширению и может оказаться отрицатель-
ной; например, '\377' имеет значение -1.

-8-


Когда более длинное целое преобразуется в более корот-
кое или в char, оно обрезается слева; лишние биты просто
отбрасываются.

    3.3.2. Типы float и double



Вся вещественная арифметика в Си выполняется с двойной
точностью. Каждый раз, когда объект типа float появляется в
выражении, он удлиняется до double посредством добавления
нулей в его дробную часть. Когда объект типа double должен
быть преобразован к типу float, например, при присваивании,
перед усечением double округляется до длины float.

Единственное исключение может быть сделано в компилято-
рах для ЭВМ, на которых нет аппаратных операций над числами
типа double (например, СМ-4). Уточнить это можно по описа-
нию компилятора (команда cc).

    3.3.3. Вещественные и целочисленные величины



Преобразование вещественных значений к целочисленному
типу в некоторой степени машинно-зависимо; в частности, нап-
равление усечения отрицательных чисел меняется от машине к
машине. Результат не определен, если значение не помещается
в предоставляемое пространство.

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

    3.3.4. Указатели и целые



Целое или длинное целое может быть прибавлено к указа-
телю или вычтено из него; в этом случае первая величина пре-
образуется так, как указывается в описании операции сложе-
ния.

Два указателя на объекты одинакового типа могут быть
вычтены; в этом случае результат преобразуется к целому, как
указывается в описании операции вычитания.

    3.3.5. Целое без знака



Всякий раз, когда целое без знака объединяется с прос-
тым целым, простое целое преобразуется в целое без знака и
результат оказывается целым без знака. Значением является
наименьшее целое без знака, соответствующее целому со знаком
(по модулю 2**размер слова). В двоичном дополнительном
представлении это преобразование является чисто умозритель-
ным и не изменяет фактическую комбинацию битов.

Когда целое без знака преобразуется к типу long, значе-
ние результата совпадает со значением целого без знака.


-9-


Таким образом, это преобразование сводится к добавлению
нулей слева.

    3.3.6. Арифметические преобразования



Подавляющее большинство операций вызывает преобразова-
ние и определяет типы результата аналогичным образом. Приво-
димая ниже схема в дальнейшем будет называться "обычными
арифметическими преобразованиями". Сначала любые операнды
типа char или short преобразуются в int, а любые операнды
типа float преобразуются в double. Затем, если какой-либо
операнд имеет тип double, то другой преобразуется к типу
double, и это будет типом результата. В противном случае,
если какой-либо операнд имеет тип long, то другой операнд
преобразуется к типу long, и это и будет типом результата.
В противном случае, если какой-либо операнд имеет тип
unsigned, то другой операнд преобразуется к типу unsigned, и
это будет типом результата. В противном случае оба операнда
будут иметь тип int, и это будет типом результата.

    * 4. ВЫРАЖЕНИЯ



Старшинство операций в выражениях совпадает с порядком
следования основных подразделов настоящего раздела, начиная
с самого высокого уровня старшинства. Так, например, выраже-
ниями, указываемыми в качестве операндов операции + (п.0.4),
являются выражения, определенные в п.п.0.1-0.3. Внутри каж-
дого подраздела операции имеют одинаковое старшинство. В
каждом подразделе для описываемых там операций указывается
их ассоциативность слева или справа. Старшинство и ассоциа-
тивность всех операций в выражениях резюмируются в граммати-
ческой сводке в приложении.

В противном случае порядок вычислений выражений не
определен. В частности, компилятор может вычислять подвыра-
жения в том порядке, который он находит наиболее эффектив-
ным, даже если эти подвыражения приводят к побочным эффек-
там. Порядок, в котором происходят побочные эффекты, не спе-
цифицируется. Выражения, включающие коммутативные и ассоциа-
тивные операции (*,+,&,|,^), могут быть переупорядочены про-
извольным образом даже при наличии круглых скобок; в этом
случае необходимо использовать явные промежуточные перемен-
ные.

При вычислении выражений обработка переполнения и про-
верка при делении являются машинно-зависимыми. Большинство
реализаций языка Си (в том числе и в ОС ДЕМОС) игнорируют
переполнение целых; обработка ошибки при делении на 0 и при
всех особых случаях в операциях с вещественными числами
меняется от машины к машине и обычно выполняется с помощью
библиотечной функции.

-10-


    4.1. Первичные выражения



Первичные выражения, включающие ., ->, индексацию и
обращения к функциям, группируются слева направо.

первичное выражение:
идентификатор
константа
строка
(выражение)
первичное_выражение [выражение]
первичное_выражение (список_выражений)
необ
первичное_l_значение . Идентификатор
первичное_выражение -> идентификатор
список_выражений:
выражение
список_выражений, выражение

Идентификатор является первичным выражением при условии, что
он описан подходящим образом, как это обсуждается ниже. Тип
идентификатора определяется его описанием. Если, однако,
типом идентификатора является массив ..., то значением выра-
жения, состоящего из этого идентификатора, является указа-
тель на первый объект в этом массиве, а типом выражения
будет указатель на .... Более того, идентификатор массива не
является выражением l_значения. Подобным образом интерпрети-
руется идентификатор, который описан как функция, возвращаю-
щая .... За исключением того случая, когда он используется в
позиции имени функции при обращении, преобразуется в указа-
тель на функцию, которая возвращает ....

Константа является первичным выражением. В зависимости
от ее формы типом константы может быть int, long или double.

Строка является первичным выражением. Исходным ее типом
является массив символов; но следуя тем же самым правилам,
которые приведены выше для идентификаторов, он модифициру-
ется в указатель на символы, и результатом является указа-
тель на первый символ строки. (Имеется исключение в некото-
рых инициализаторах; см. ниже.)