Ряд операций может в зависимости от своих операндов вы- зывать преобразование
значение операнда из одного типа в другой. В этом разделе объясняются результаты,
которые сле- дует ожидать от таких преобразований. В п. 14.6 Подводятся итоги преобразований,
требуемые большинством обычных опера- ций; эти сведения дополняются необходимым
образом при обсуж- дении каждой операции.
14.1. Символы и целые
Символ или короткое целое можно использовать всюду, где можно использовать целое.
Во всех случаях значение преобра- зуется к целому. Преобразование более короткого
целого к бо- лее длинному всегда сопровождается знаковым расширением; це- лые являются
величинами со знаком. Осуществляется или нет знаковое расширение для символов, зависит
от используемой машины, но гарантируется, что член стандартного набора сим- волов
неотрицателен. из всех машин, рассматриваемых в этом руководстве, только PDP-11
осуществляет знаковое расширение. область значений символьных переменных на PDP-11
меняется от -128 до 127; символы из набора ASC11 имеют положительные значения. Символьная
константа, заданная с помощью восьме- ричной условной последовательности, подвергается
знаковому расширению и может оказаться отрицательной; например, '\377' имеет значение
-1.
Когда более длинное целое преобразуется в более короткое или в CHAR, оно обрезается
слева; лишние биты просто отбра- сываются.
14.2. Типы FLOAT и DOUBLE
Вся плавающая арифметика в "C" выполняется с двойной точностью каждый раз, когда
объект типа FLOAT появляется в выражении, он удлиняется до DOUBLE посредством добавления
нулей в его дробную часть. когда объект типа DOUBLE должен быть преобразован к типу
FLOAT, например, при присваивании, перед усечением DOUBLE округляется до длины FLOAT.
14.3. Плавающие и целочисленные величины
Преобразование плавающих значений к целочисленному типу имеет тенденцию быть
до некоторой степени машинно-зависимым; в частности направление усечения отрицательных
чисел меняет- ся от машине к машине. Результат не определен, если значение не помещается
в предоставляемое пространство.
Преобразование целочисленных значений в плавающие выпол- няется без осложнений.
Может произойти некоторая потеря точ- ности, если для результата не содержится достаточного
коли- чества битов.
14.4. Указатели и целые
Целое или длинное целое может быть прибавлено к указате- лю или вычтено из него;
в этом случае первая величина преоб- разуется так, как указывается в разделе описания
операции сложения.
Два указателя на объекты одинакового типа могут быть вычтены; в этом случае результат
преобразуется к целому, как указывается в разделе описания операции вычитания.
14.5. Целое без знака
Всякий раз, когда целое без знака объединяется с простым целым, простое целое
преобразуется в целое без знака и ре- зультат оказывается целым без знака. Значением
является наи- меньшее целое без знака, соответствующее целому со знаком (по модулю
2**размер слова). В двоичном дополнительном пред- ставлении это преобразование является
чисто умозрительным и не изменяет фактическую комбинацию битов.
Когда целое без знака преобразуется к типу LONG, значе- ние результата совпадает
со значением целого без знака. Та- ким образом, это преобразование сводится к добавлению
нулей слева.
14.6. Арифметические преобразования
Подавляющее большинство операций вызывает преобразование и определяет типы результата
аналогичным образом. Приводимая ниже схема в дальнейшем будет называться "обычными
арифмети- ческими преобразованиями". Сначала любые операнды типа CHAR или SHORT
преобразуются в
INT, а любые операнды типа FLOAT преобразуются в DOUBLE. Затем, если какой-либо
операнд имеет тип DOUBLE, то другой
преобразуется к типу DOUBLE, и это будет типом результата. В противном случае,
если какой-либо операнд имеет тип LONG,
то другой операнд преобразуется к типу LONG, и это и будет
типом результата. В противном случае, если какой-либо операнд имеет тип
UNSIGNED, то другой операнд преобразуется к типу UNSIGNED,
и это будет типом результата. В противном случае оба операнда будут иметь тип
INT, и это
будет типом результата.
15. Выражения
Старшинство операций в выражениях совпадает с порядком следования основных подразделов
настоящего раздела, начиная с самого высокого уровня старшинства. Так, например,
выраже- ниями, указываемыми в качестве операндов операции + (п.15.4), Являются выражения,
определенные в п.п.15.1-15.3. Внутри каждого подраздела операции имеет одинаковое
старшин- ство. В каждом подразделе для описываемых там операций ука- зывается их
ассоциативность слева или справа. Старшинство и ассоциативность всех операций в
выражениях резюмируются в грамматической сводке в п.18.
В противном случае порядок вычислений выражений не опре- делен. В частности,
компилятор считает себя в праве вычис- лять подвыражения в том порядке, который
он находит наиболее эффективным, даже если эти подвыражения приводят к побочным
эффектам. Порядок, в котором происходят побочные эффекты, не специфицируется. Выражения,
включающие коммутативные и ассо- циативные операции ( *,+,&,!,^ ), могут быть переупорядочены
произвольным образом даже при наличии круглых скобок; чтобы вынудить определенный
порядок вычислений, в этом случае не- обходимо использовать явные промежуточные
переменные.
При вычислении выражений обработка переполнения и про- верка при делении являются
машинно-зависимыми. Все существу- ющие реализации языка "C" игнорируют переполнение
целых; об- работка ситуаций при делении на 0 и при всех особых случаях с плавающими
числами меняется от машины к машине и обычно выполняется с помощью библиотечной
функции.
15.1. Первичные выражения
Первичные выражения, включающие ., ->, индексацию и об- ращения к функциям, группируются
слева направо.
Первичное выражение:
идентификатор
константа
строка
(выражение)
первичное-выражение [выражение]
первичное-выражение (список-выражений нео
первичное-L-значение . Идентификатор
первичное-выражение -> идентификатор
список-выражений:
выражение
список-выражений, выражение
Идентификатор является первичным выражением при условии, что он описан подходящим
образом, как это обсуждается ниже. тип идентификатора определяется его описанием.
Если, однако, ти- пом идентификатора является "массив ...", то значением выра- жения,
состоящего из этого идентификатора , является указа- тель на первый объект в этом
массиве, а типом выражения бу- дет "указатель на ...". Более того, идентификатор
массива не является выражением L-значения. подобным образом идентифика- тор, который
описан как "функция, возвращающая ...", за иск- лючением того случая, когда он используется
в позиции имени функции при обращении, преобразуется в "указатель на функ- цию,
которая возвращает ...".
Константа является первичным выражением. В зависимости от ее формы типом константы
может быть INT, LONG или DOUBLE.
Строка является первичным выражением. Исходным ее типом является "массив символов";
но следуя тем же самым правилам, которые приведены выше для идентификаторов, он
модифицирует- ся в "указатель на символы", и результатом является указа- тель на
первый символ строки. (имеется исключение в некото- рых инициализаторах; см. П.
16.6.)
Выражение в круглых скобках является первичным выражени- ем, тип и значение которого
идентичны типу и значению этого выражения без скобок. Наличие круглых скобок не
влияет на то, является ли выражение L-значением или нет.
Первичное выражение, за которым следует выражение в квадратных скобках, является
первичным выражением. Интуитив- но ясно, что это выражение с индексом. Обычно первичное
вы- ражение имеет тип "указатель на ...", индексное выражение имеет тип INT, а типом
результата является "...". Выражение E1[E2] по определению идентично выражению *
((E1) + (E2)). Все, что необходимо для понимания этой записи, содержится в этом
разделе; вопросы, связанные с понятием идентификаторов и операций * и + рассматриваются
в п.п. 15.1, 15.2 И 15.4 соответственно; выводы суммируются ниже в п. 22.3.
Обращение к функции является первичным выражением, за которым следует заключенный
в круглые скобки возможно пустой список выражений, разделенных запятыми, которые
и представ- ляют собой фактические аргументы функции. Первичное выраже- ние должно
быть типа "функция, возвращающая ...", а резуль- тат обращения к функции имеет тип
"...". Как указывается ни- же, ранее не встречавщийся идентификатор, за которым
непос- редственно следует левая круглая скобка, считается описанным по контексту,
как представляющий функцию, возвращающую це- лое; следовательно чаще всего встречающийся
случай функции, возвращающей целое значение, не нуждается в описании.
Перед обращением любые фактические аргументы типа FLOAT преобразуются к типу
DOUBLE, любые аргументы типа CHAR или SHORT преобразуются к типу INT, и, как обычно,
имена масси- вов преобразуются в указатели. Никакие другие преобразования не выполняются
автоматически; в частности, не сравнивает ти- пы фактических аргументов с типами
формальных аргументов. Если преобразование необходимо, используйте явный перевод
типа (CAST); см. П.п. 15.2, 16.7.
При подготовке к вызову функции делается копия каждого фактического параметра;
таким образом, все передачи аргумен- тов в языке "C" осуществляются строго по значению.
функция может изменять значения своих формальных параметров, но эти изменения не
влияют на значения фактических параметров. С другой строны имеется возможность передавать
указатель при таком условии, что функция может изменять значение объекта, на который
этот указатель указывает. Порядок вычисления ар- гументов в языке не определен;
обратите внимание на то, что различные компиляторы вычисляют по разному.
Допускаются рекурсивные обращения к любой функции.
Первичное выражение, за которым следует точка и иденти- фикатор, является выражением.
Первое выражение должно быть L-значением, именующим структуру или объединение, а
иденти- фикатор должен быть именем члена структуры или объединения. Результатом
является L-значение, ссылающееся на поименован- ный член структуры или объединения.
Первичное выражение, за которым следует стрелка (состав- ленная из знаков - и
>) и идентификатор, является выражени- ем. первое выражение должно быть указателем
на структуру или объединение, а идентификатор должен именовать член этой структуры
или объединения. Результатом является L-значение, ссылающееся на поименованный член
структуры или объединения, на который указывает указательное выражение.
Следовательно, выражение E1->MOS является тем же самым, что и выражение (*E1).MOS.
Структуры и объединения рассмат- риваются в п. 16.5. Приведенные здесь правила использования
структур и объединений не навязываются строго, для того что- бы иметь возможность
обойти механизм типов. См. П. 22.1.
15.2. Унарные операции
Выражение с унарными операциями группируется справо на- лево.
Унарное-выражение:
* выражение
& L-значение
- выражение
! Выражение
\^ выражение
++ L-значение
-- L-значение
L-значение ++
L-значение --
(имя-типа) выражение
SIZEOF выражение
SIZEOF имя-типа
Унарная операция * означает косвенную адресацию: выраже- ние должно быть указателем,
а результатом является L-значе- ние, ссылающееся на тот объект, на который указывает
выраже- ние. Если типом выражения является "указатель на...", то ти- пом результата
будет "...".
Результатом унарной операции & является указатель на объект, к которому ссылается
L-значение. Если L-значение имеет тип "...", то типом результата будет "указатель
на ...".
Результатом унарной операции - (минус) является ее опе- ранд, взятый с противоположным
знаком. Для величины типа UNSIGNED результат получается вычитанием ее значения из
2**N (два в степени N), где N-число битов в INT. Унарной операции + (плюс) не существует.
Результатом операции логического отрицания ! Является 1, если значение ее операнда
равно 0, и 0, если значение ее операнда отлично от нуля. Результат имеет тип INT.
Эта опе- рация применима к любому арифметическому типу или указате- лям.
Операция \^ дает обратный код, или дополнение до едини- цы, своего операнда.
Выполняются обычные арифметические пре- образования. Операнд должен быть целочисленного
типа.
Объект, на который ссылается операнд L-значения префикс- ной операции ++, увеличивается.
значением является новое значение операнда, но это не L-значение. Выражение ++х
экви- валентно х+=1. Информацию о преобразованиях смотри в разборе операции сложения
(п. 15.4) и операции присваивания (п. 15.14).
Префиксная операция -- аналогична префиксной операции ++, но приводит к уменьшению
своего операнда L-значения.
При применении постфиксной операции ++ к L-значению ре- зультатом является значение
объекта, на который ссылается L-значение. После того, как результат принят к сведению,
объект увеличивается точно таким же образом, как и в случае префиксной операции
++. Результат имеет тот же тип, что и выражение L-значения.
При применении постфиксной операции -- к L-значению ре- зультатом является значение
объекта, на который ссылается L-значение. После того, как результат принят к сведению,
объект уменьшается точно таким же образом, как и в случае префиксной операции --.
Результат имеет тот же тип, что и выражение L-значения.
Заключенное в круглые скобки имя типа данных,стоящее пе- ред выражением , вызывает
преобразование значения этого вы- ражения к указанному типу. Эта конструкция называется
пере- вод (CAST). Имена типов описываются в п. 16.7.
Операция SIZEOF выдает размер своего операнда в байтах. (Понятие байт в языке
не определено, разве только как значе- ние операции SIZEOF. Однако во всех существующих
реализациях байтом является пространство, необходимое для хранения объекта типа
CHAR). При применении к массиву результатом яв- ляется полное число байтов в массиве.
Размер определяется из описаний объектов в выражении. Это выражение семантически
является целой константой и может быть использовано в любом месте, где требуется
константа. Основное применение эта опе- рация находит при связях с процедурами,
подобным распредели- телям памяти, и в системах ввода- вывода.