Содержание

Выражения

Выражение — последовательность операторов и операндов, которые описывают как:

или оба.

Последовательность исполнения выражений обычно определяется комбинацией

  1. круглых скобок (), которые указывают компилятору необходимый порядок группировки операторов,
  2. приоритетом операторов, который описывает их относительное старшинство при отсутствии скобок,
  3. общепринятый алгебраический порядок вычислений,
  4. ассоциативность операторов.

В большинстве других случаев порядок исполнения определяется компилятором и лучше на это не полагаться. Исключения из этих правил описаны в соответствующих разделах. Большинство пользователей найдут порядок вычислений чётко определённым и интуитивно понятным. Однако в сомнительных случаях используйте скобки.

В следующей таблице операторы сгруппированы по их приоритетам.

Тип выражения Операторы
первичное идентификатор
константа
стринг
(выражение)
постфиксное a[b]
f()
a.b a->b
a++ a--
унарное sizeof u sizeof( a )
++a --a
&a *a
+a -a ~a !a
приведение к типу (тип) a
мультипликативные a * b
a / b
a % b
аддитивные a + b
a - b
сдвига a << b
a >> b
отношения a < b
a > b
a <= b
a >= b
равенства a == b
a != b
побитовое И a & b
побитовое исключающее ИЛИ a ^ b
побитовое ИЛИ a | b
логическое И a && b
логическое ИЛИ a || b
условное *) a ? b : c
присвоения*) a = b
a += b a -= b
a *= b a /= b a %= b
a &= b a ^= b a |= b
a <<= b a >>= b
запятая a, b

*) выражения вычисляются справа налево

Операторы в таблице располагаются в порядке убывания приоритета. Все операторы включающие более одного операнда выполняются слева направо, за исключением условного оператора и оператора присваивания, которые выполняются справа налево. Операторы одного приоритета, за исключением оговорённых в соответствующих разделах, могут выполняться в любом порядке по выбору компилятора (при условии соблюдения алгебраических правил). В частности, компилятор для увеличения эффективности кода может перегруппировать подвыражения, которые оба и ассоциативны, и коммутативны, так, что смысл операндов (т.е. их типы и результаты) после перегруппировки не изменится.

Порядок любых побочных эффектов (например, присвоение или действия при вызове функции) также остаётся на усмотрение компилятора.

Когда операнды неверны, возникает исключительное состояние. Например, исключительное состояние вызывает деление на ноль. При возникновении исключительного состояния поведение не определено. Если такое состояние возможно, то программа должна быть готова его обработать.

В последующих разделах для описания каждого уровня в таблице приоритетов используется формальный синтаксис. Это необходимо для более полного раскрытия отношений между различными уровнями.

L-значения

Для понимания некоторых компонентов выражений важно понимать термин l-значение.

L-значение — выражение обозначающее объект. Его простейшая форма — идентификатор, который является объектом (например, целое).

Типом выражения не может быть void или функция. Термин l-значение образован из слов left (левое) значение, что отсылает к тому факту, что l-значение обычно находится в левой части выражения присваивания.

Если ptr — указатель на тип отличный от void или функции, то оба, и ptr, и *ptrl-значения.

Поддающимся изменению l-значением является l-значение, чьим типом не является массив или незавершенный тип, и чья декларация не содержит ключевого слова const, а, если оно — структура или объединение, то ни один его член не содержит ключевого слова const.

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

первичное-выражение: идентификатор

или

константа

или

стринговый-литерал

или

( выражение )

Первичное выражение — простейший представитель выражений. Оно может быть одним из:

идентификатор
Идентификатор обозначающий функцию называется именующим выражением функции. Идентификатор обозначающий объект является l-значением.

константа
Константа — первичное выражение, чей тип зависит от формы. См. Константы.

стринговый-литерал
Стринговый литерал — первичное выражение, чьим типом является «массив символов». Стринговый литерал также является l-значением (но неподдающимся изменению).

выражение в скобках
Тип и значение выражения в скобках такие же, как у выражения без скобок. Оно может быть l-значением, именующим выражением функции или пустым выражением.

Если имеются декларации:

int     count;
int *   ctrptr;
int     f( int );
int     g( int );

то все следующие выражения допустимы:

count
3
3.2
'a'
"Hello there"
(count + 3)
(*(ctrptr+1))
(f( ++i ) * g( j++ ))

Постфиксные операторы

постфиксное-выражение: первичное-выражение

или

выражение-индексации-массива

или

выражение-вызова-функции

или

именующее-выражение-члена

или

постфиксный-инкремент

или

постфиксный-декремент

Индексация массива

выражение-индексации-массива: постфиксное-выражение[выражение]

Обобщённой формой индексации массива является

массив[индекс]

где массив должен иметь тип «массив типа» или «указатель на тип», а индекс должен быть целочисленным. Результат имеет тип тип.

Массив[индекс] эквивалентно (*(массив+индекс)) или элементу с номером индекс в массиве массив, в котором первый элемент имеет номер ноль. Заметим, что индекс масштабируется, чтобы соответствовать размеру элементов массива.

Альтернативной формой индексации массива является

индекс[массив]

однако эта форма используется редко.

Вызов функции

выражение-вызова-функции: постфиксное-выражение()

или

постфиксное-выражение ( список-аргументных-выражений )

список-аргументных-выражений: одно или более аргументное-выражение, разделённые запятыми

Постфиксное-выражение, за которым следуют круглые скобки, содержащие нуль или более выражений разделённых запятыми, является выражением-вызова-функции. Постфиксное-выражение обозначает вызываемую функцию и должно вычисляться в указатель на функцию. Простейшая форма такого выражения — идентификатор, который является именем функции. Например, Fn() вызывает функцию Fn.

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

Если постфиксное-выражение является простым идентификатором и для этого идентификатора нет прототипа функции в области видимости, то в самый внутренний блок, содержащий вызов функции, неявно помещается декларация

extern int идентификатор();

Это объявляет функцию в внешней связью без информации о параметрах и возвращающую целое.

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

Если прототип функции находится в области видимости и вызова функции, и её определения (и их заголовки совпадают), то компилятор обеспечит необходимые число и тип параметров.

Если прототип не находится в области видимости при вызове функции, то производится расширение аргументов по умолчанию. (Целые типы, такие как char и short int конвертируются в int, а float — в double.) Если после этого встретится определение функции, и типы параметров не совпадают с типами по умолчанию, то поведение не определено. (Обычно параметры функции получат неверные значения.)

Если прототип декларирован до вызова функции, то каждый аргумент конвертируется, как при присвоении, в тип соответствующий параметру. Если после этого встретится определение функции, и типы параметров не совпадают с типами в прототипе, то поведение не определено.

Если в прототипе используется эллипсис (,...), то над теми аргументами в вызове функции, которые соответствуют эллипсису, производится только расширение по умолчанию. (Полное описание эллипсиса см. в разделе Функции.)

Вызовы функций могут быть рекурсивными. Функции могут вызывать сами себя напрямую, либо через другие функции.

Ниже приводятся некоторые примеры вызова функции:

putchar( 'x' );
chr = getchar();
valid = isdigit( chr );
printf( "chr = %c, valid = %2x\n", chr, valid );
fnptr = &MyFunction;
(*fnptr)( parm1, parm2 );
fnptr( parm1, parm2 );

Члены структур и объединений

выражение-обозначения-члена: постфиксное-выражение . идентификатор

или

постфиксное-выражение -> идентификатор

Первый операнд оператора . должен быть объектом структурного типа или объединением. Второй — именем члена этого типа. Результатом является значение члена, и, если первый операнд — l-значение, то результат тоже — l-значение.

Первый операнд оператора -> должен быть указателем на объект структурного типа или объединения. Второй — именем члена этого типа. Результат — значение члена структуры или объединения, на которую (или которое) первое выражение указывает. Результат является l-значением.

Постфиксные инкремент и декремент

выражение-пост-инкремента: постфиксное-выражение ++

выражение-пост-декремента: постфиксное-выражение --

Операндом пост-икремента и пост-декремента должно быть поддающееся изменению скалярное (не структура, объединение или массив) l-значение.

В результате операции операнд увеличивается или уменьшается на 1, соответствующую типу операнда. Например, если операнд декларирован как «указатель на тип», то увеличение или уменьшение будет производиться на значение sizeof( тип ).

Результатом обоих, и пост-инкремента, и пост-декремента (если они — подвыражения другого выражения) является начальное, неизменённое значение операнда. Другими словами, начальное значение операнда используется в выражении, а затем увеличивается или уменьшается. Будет ли операнд увеличен непосредственно после использования или после вычисления выражения, не определено. Рассмотрим команды

int i = 2;
int j;

j = (i++) + (i++);

В зависимости от компилятора j может получить значения 4 или 5. Если инкремент происходит после вычисления всего выражения, то j получает значение 2 + 2. Если же инкремент i происходит непосредственно после получения его значения, то j получает значение 2 + 3.

Чтобы избежать неопределённости предыдущее выражение нужно записать так:

j = i + i;
i += 2;

Унарные операторы

унарное-выражение: постфиксное-выражение

или

выражение-пред-инкремента

или

выражение-пред-декремента

или

унарный-оператор выражение-приведённое-к-типу

или

выражение-sizeof

унарный-оператор: один из & * + - ~ !

Префиксные инкремент и декремент

выражение-пред-инкремента: ++ унарное-выражение

выражение-пред-декремента: -- унарное-выражение

Операндом операторов пред-инкремента и пред-декремента должно быть скалярным поддающимся изменению l-значение (не структура, объединение или массив).

В результате операции операнд увеличивается или уменьшается на 1, соответствующую типу операнда. Например, если операнд декларирован как «указатель на тип», то увеличение или уменьшение будет производиться на значение sizeof( тип ).

Выражение ++obj эквивалентно (obj += 1), а --obj эквивалентно (obj -= 1).

Операторы взятия адреса и косвенности

унарное-выражение: & выражение-приведённое-к-типу

или

* выражение-приведённое-к-типу

Унарный символ & обозначает операцию взятия адреса. Его операнд должен обозначать функцию или массив, или быть l-значением, обозначающим объект, который не является битовым полем и не объявлен со спецификатором класса памяти register. Если типом операнда является тип, то тип результата — «указатель на тип», а сам результат — адрес операнда.

Если тип операнда — «массив из тип», то тип результата — «указатель на тип», а сам результат — адрес первого элемента массива.

Символ * в его унарной форме обозначает операцию косвенности или указатель. Его операндом должен быть указательного типа, но не указатель на void. Если операнд является «указателем на тип», то тип результата — «тип», а сам результат — объект, на который указывает операнд.

Проверки правильности значения указателя не производится. Если используется неверное значение указателя, то поведение * не определено.

Примеры:

int   counter;
int * ctrptr;
void  (*fnptr)( int, int * );

ctrptr  = &counter;
*ctrptr = 3;

fnptr = FnRetVoid;
fnptr( *ctrptr, &counter );

Унарные арифметические операторы

унарное-выражение: + выражение-приведённое-к-типу

или

- выражение-приведённое-к-типу

или

~ выражение-приведённое-к-типу

или

! выражение-приведённое-к-типу

Символ + в его унарной форме просто возвращает значение операнда. Тип операнда должен быть арифметическим (символ, целое или с плавающей точкой). Над операндом производится целочисленное расширение и результат имеет расширенный тип.

Символ - в его унарной форме является оператором отрицания. Тип операнда должен быть арифметическим (символ, целое или с плавающей точкой). Результат — операнд с другим знаком. Над операндом производится целочисленное расширение и результат имеет расширенный тип. Выражение -obj эквивалентно (0-obj).

Символ ~ является оператором побитового дополнения или побитовым НЕ. Типом операнда должен быть целочисленный тип, над ним производится целочисленное расширение. Результат имеет расширенный тип. Каждый бит результата является дополнением соответствующего бита операнда, т.е. ноль соответствует единице, а единица — нулю.

Символ ! является оператором логического НЕ. Его операнд должен быть скалярного типа (не структура, объединение или массив). Результат имеет тип int. Если операнд имел значение ноль, то результат будет равен 1. Если операнд был отличным от нуля, то результат — 0.

Оператор sizeof

выражение-sizeof: sizeof унарное-выражение

или

sizeof( имя-типа )

Оператор sizeof возвращает размер операнда в байтах. Операнд должен быть выражением или типом в круглых скобках. В любом случае тип не может быть функцией, битовым полем или незавершённым типом (таким как void или массивом, у которого не декларирована длина).

Нужно отметить, что операнд-выражение sizeof не вычисляется. Проверяется только тип результата, и берётся размер этого типа.

Если операнд имеет символьный тип, то результат равен 1.

Если тип является структурой или объединением, то результат — полное число байт в структуре или объединении, включая все внутренние и конечные промежутки, полученные в результате выравнивания. Размер структуры может быть больше, чем сумма размеров её членов.

Если тип является массивом, то результат — полное число байт в массиве, если при этом операнд не является параметром в определении функции включающей текущий блок. В этом случае результат — размер указателя.

Тип результата оператора sizeof зависит от реализации, но это беззнаковый целый тип, и он представлен типом size_t в заголовочном файле <stddef.h>.

В компиляторах Watcom C/16 и C/32 макро size_t является unsigned int.

Пример:

struct s {
    struct s * next;
    int        obj1;
    int        obj2;
};

static struct s * SAllocAndFill( const struct s * def_s )
/*******************************************************/
{
    struct s * sptr;

    sptr = malloc( sizeof( struct s ) );
    if( sptr != NULL ) {
        memcpy( sptr, def_s, sizeof( struct s ) );
    }
    return( sptr );
}

Функция SAllocAndFill получает указатель на struct s. Она получает память для такой структуры и копирует туда содержимое структуры, на которую указывает def_s. Возвращает указатель на полученную структуру.

Библиотечная функция malloc получает в качестве параметра число байт, которые нужно разместить, и sizeof( struct s ) даёт такое значение. Библиотечная функция memcpy в качестве третьего параметра также принимает число байт, которые нужно скопировать, и снова sizeof( struct s ) предоставляет такое значение.

Оператор приведения типа

выражение-приведённое-к-типу: унарное-выражение

или

( имя-типа ) выражение-приведённое-к-типу

Когда выражению предшествует имя типа заключённое в круглые скобки, то значение выражения конвертируется в указанный тип. Это называется приведением типа. И имя типа, и операнд должны быть скалярными (не структурой, объединением или массивом), но может быть void. Если именем типа является void, то операнд должен быть завершённого типа (массивом известного размера или полностью определённой структурой или объединением).

Приведение типа не даёт l-значение.

Указатели могут быть свободно конвертированы из «указателя на void» в указатель на любой тип без явного использования оператора приведения типа. Они также могут быть конвертированы из указателя на любой тип в «указатель на void».

Указатель может быть конвертирован в указатель на другой тип. Однако указатель может быть неправильным, если результирующий указатель не будет должным образом выровнен. Конвертирование указателя в указатель с менее строгим выравниванием и обратно в результате даст тот же указатель. Однако конвертирование в указатель с более строгим выравниванием и обратно, может дать другой указатель. На многих компьютерах, где выравнивание не требуется (но может увеличить производительность), указатели можно конвертировать свободно.

В Watcom C/16 и C/32 выравнивание целых, указателей и чисел с плавающей точкой не требуется, поэтому компилятор не производит никакого выравнивания. Но выравнивание этих типов может сделать программу более производительной.

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

Указатель на функцию можно конвертировать в указатель на функцию другого типа и обратно. Результирующий указатель будет тот же, что и начальный.

Если указатель конвертирован в указатель на функцию другого типа, и произведён вызов функции с использованием этого указателя, то поведение не определено.

Указатель может быть конвертирован в целый тип. Целый тип, требуемый для хранения значения указателя, зависит от реализации. Если целое недостаточно большое, то поведение не определено.

Целое может быть конвертировано в указатель. Результат зависит от реализации.

В Watcom C/16 при конвертировании между указателями и целыми указатели __near трактуются как unsigned int, а указатели __far и __huge — как unsigned long int с сегментным значением указателя в старших (более значащих) двух байтах. Поэтому тут применимы все обычные правила преобразований. Помните, что огромные (huge) указатели не нормализуются.

В Watcom C/32 при конвертировании между указателями и целыми указатели __near трактуются как unsigned int, указатели __far16 и _Seg16 также — как unsigned int с сегментным значением в старших (более значащих) двух байтах. Поэтому тут применимы все обычные правила преобразований. Помните, что указатели __far не могут быть конвертированы в целое без потери информации о сегменте.

Мультипликативные операторы

мультипликативное-выражение: выражение-приведённое-к-типу

или

мультипликативное-выражение * выражение-приведённое-к-типу

или

мультипликативное-выражение / выражение-приведённое-к-типу

или

мультипликативное-выражение % выражение-приведённое-к-типу

Символ * в его бинарной форме даёт произведение своих операндов. Операнды должны быть арифметического типа, над ними производятся обычные арифметические преобразования.

Символ / даёт частное от деления первого операнда на второй. Операнды должны быть арифметического типа, над ними производятся обычные арифметические преобразования. Помните, что, когда происходит деление на нуль, то поведение не определено.

Когда оба операнда / — положительные целые типа и деление неточное, то результятом будет целое меньшее, чем алгебраическое (точное) частное. (Результат округляется с недостатком).

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

Компиляторы Watcom C/16 и C/32 всегда округляют результат целого деления в сторону нуля. Это действие называется отбрасыванием.

Символ % даёт остаток от деления первого операнда на второй. Операнды % должны быть целого типа.

Когда оба операнда % — положительные, результат — положительное значение меньшее второго операнда. Когда один или оба операнда — отрицательные, то каким будет результат, положительным или отрицательным, зависит от реализации.

В компиляторах Watcom C/16 и C/32 остаток от деления имеет тот же знак, что и первый операнд.

Для целых a и b, если b не ноль, (a/b)*b + a%b будет эквивалентно a.

Аддитивные операторы

аддитивное-выражение: мультипликативное-выражение

или

аддитивное-выражение + мультипликативное-выражение

или

аддитивное-выражение - мультипликативное-выражение

Символ + в его бинароной форме означает сумму операндов.

Если оба операнда — арифметического типа, то над ними производятся обычные арифметические преобразования.

Если один из операндов — указатель, то другой операнд должен быть целого типа. Операнд-указатель не может быть указателем на void. До прибавления к указателю целый тип умножается на размер объекта, на который указывает указатель. Тип результата совпадает типом операнда-указателя. Если указатель — указатель элемент массива, то результирующий указатель будет указывать элемент того же массива, если он достаточно большой. Если результирующий указатель не указывает на элемент массива, то при его использовании с унарным оператором * (косвенность) или оператором -> (стрелка) поведение будет не определено.

Символ - в его бинарной форме означает разность как результат вычитания второго операнда из первого. Если оба операнда — арифметического типа, то над ними производятся обычные арифметические преобразования.

Если первый операнд — указатель, то второй должен быть либо указателем на тот же тип, либо целым.

Так же как и при сложении указателя и целого, целое умножается на размер объекта, на который указывает указатель. Операнд-указатель не может быть указателем на void. Результат имеет тот же тип, что и операнд-указатель.

Если оба операнда — указатели на одинаковый тип, то разность делится на размер типа и представляет собой разницу индексов элементов массива (подразумевается, что тип — «массив типа»). Тип результата зависит от реализации и представлен типом ptrdiff_t (знаковый целый тип) определённым в заголовочном файле <stddef.h>.

В Watcom C/16 и C/32 ptrdiff_t является int, если не используется огромная модель памяти. В этом случае ptrdiff_t long int.

Операторы сдвига

выражение-сдвига: аддитивное-выражение

или

выражение-сдвига << аддитивное-выражение

или

выражение-сдвига >> аддитивное-выражение

Символ << означает оператор сдвига влево. Оба операнда должны быть целого типа, и над ними производится целочисленное расширение. Типом результата является тип расширенного левого операнда.

Результатом op << amt является op сдвинутый влево на amt бит. Биты справа заполняются нулями. Старшие биты выдвинутые из op отбрасываются. Результат — полученный набор битов. Другими словами, это означает умножение op на 2 в степени amt.

Символ >> означает оператор сдвига вправо. Оба операнда должны быть целого типа, и над ними производится целочисленное расширение. Типом результата является тип расширенного левого операнда.

Результатом op >> amt является op сдвинутый вправо на amt бит. Если op — беззнаковый или знаковый с неотрицательным значением, то op делится на 2 в степени amt. Младшие (правые) биты выдвинутые из op отбрасываются, биты слева заполняются нулями. Результат — полученный набор битов.

Если op — знакового типа и имеет отрицательное значение, то поведение op >> amt зависит от реализации. Обычно старшие биты освобождённые правым сдвигом заполняются знаковым битом, который был до сдвига (арифметический сдвиг), или нулями (логический сдвиг).

В Watcom C/16 и C/32 правый сдвиг отрицательного значения знакового типа происходит с заполнением освободившихся битов знаковым. То есть, свободные биты заполняются единицами.

Для обоих операторов сдвига, если число битов для сдвига превышает число бит в типе, то результат неопределён.

Операторы отношения

выражение-отношения: выражение-сдвига

или

выражение-отношения < выражение-сдвига

или

выражение-отношения > выражение-сдвига

или

выражение-отношения <= выражение-сдвига

или

выражение-отношения >= выражение-сдвига

Каждый символ < (меньше), > (больше) <= (меньше или равно) >= (больше или равно) дают 1, если отношение истинно, и 0, если ложно. Результат имеет тип int.

Если оба операнда — арифметического типа, то над ними производятся обычные арифметические преобразования.

Если один из операндов — указатель, то другой должен быть указателем на совместимый тип. Результат зависит от того, куда именно указывают указатели.

Если указатели указывают на элементы одного и того же массива, то указатель, который указывает на элемент с большим индексом, будет больше.

Если указатели указывают на различные члены внутри одной структуры, то указатель, который указывает на член, декларированный в структуре позже, будет больше.

Если оба указателя указывают на один объект-объединение, то они будут равны.

Все другие сравнения приводят к неопределённому поведению. Как сказано выше, отношения между указателями определяются местом в памяти машины, куда они указывают. Обычно сравниваются численные значения операндов-указателей.

Операторы равенства

выражение-равенства: выражение-отношения

или

выражение-равенства == выражение-отношения

или

выражение-равенства != выражение-отношения

Символы == (равно) и != (не равно) дают 1, если отношение истинно и 0, если ложно. Результат имеет тип int.

Если оба операнда — арифметического типа, то над ними производятся обычные арифметические преобразования.

Если оба операнда — указатели на один и тот же тип, и их сравнение даёт равенство, то они указывают на один и тот же объект.

Если оба операнда — указатели, и один из них — указатель на void, то другой указатель конвертируется в void.

Если один из операндов — указатель, то другой может быть константой null-указатель (нуль).

Все другие комбинации запрещены.

Оператор побитового И

выражение-И: выражение-равенства

или

выражение-И & выражение-равенства

Символ & в его бинарной форме обозначает оператор побитового И. Каждый операнд должен быть целого типа, и над ними производятся обычные арифметические преобразования.

Результатом является побитовое И двух операндов. То есть бит результата устанавливается только, если оба соответствующих бита в операндах установлены.

В следующей таблице приводятся несколько примеров операций побитового И:

Операция Результат
0x0000 & 0x7A4C 0x0000
0xFFFF & 0x7A4C 0x7A4C
0x1001 & 0x0001 0x0001
0x29F4 & 0xE372 0x2170

Оператор побитового исключающего ИЛИ

выражение-исключающего-ИЛИ: выражение-И

или

выражение-исключающего-ИЛИ ^ выражение-И

Символ ^ обозначает оператор побитового исключающего ИЛИ. Каждый операнд должен быть целого типа, и над ними производятся обычные арифметические преобразования.

Результатом является побитовое исключающее ИЛИ. То есть, бит результата устанавливается, если установлен только один из соответствующих битов операндов.

Можно сказать и так: если один из операндов считать маской, то каждый бит в маске равный 1 обращает соответствующий бит второго операнда (0 становится 1, а 1 становится 0), а бит равный 0 оставляет его без изменения. Изменённый второй операнд помещается в результат.

В следующей таблице приводятся несколько примеров операций побитового исключающего ИЛИ:

Операция Результат
0x0000 ^ 0x7A4C 0x7A4C
0xFFFF ^ 0x7A4C 0x85B3
0xFFFF ^ 0x85B3 0x7A4C
0x1001 ^ 0x0001 0x1000
0x29F4 ^ 0xE372 0xCA86

Оператор побитового ИЛИ

выражение-ИЛИ: выражение-исключающего-ИЛИ

или

выражение-ИЛИ | выражение-исключающего-ИЛИ

Символ | обозначает оператор побитового включающего ИЛИ. Каждый операнд должен быть целого типа, и над ними производятся обычные арифметические преобразования.

Результатом является побитовое ИЛИ двух операндов. То есть, бит результата устанавливается, если хотя бы один из соответствующих битов операндов установлен.

В следующей таблице приводятся несколько примеров операций побитового ИЛИ:

Операция Результат
0x0000 | 0x7A4C 0x7A4C
0xFFFF | 0x7A4C 0xFFFF
0x1100 | 0x0022 0x1122
0x29F4 | 0xE372 0xEBF6

Оператор логического И

выражение-логического-И: выражение-ИЛИ

или

выражение-логического-И && выражение-ИЛИ

Символ && означает оператор логическое И. Каждый операнд должен быть скалярного типа.

Если оба операнда не равны нулю, то результат будет 1. В противном случае результат — 0. Результат имеет тип int.

Если первый операнд — нуль, то второй операнд не вычисляется. Любые побочные эффекты, которые могли произойти при вычислении второго операнда, не произойдут. Любой вызов функции во втором операнде выполняться не будет.

Оператор логического ИЛИ

выражение-логического-ИЛИ: выражение-логического-И

или

выражение-логического-ИЛИ || выражение-логического-И

Символ || обозначает оператор логического ИЛИ. Каждый операнд должен быть скалярного типа.

Если один или оба операнда не равны нулю, то результат будет 1. В противном случае, результат — 0 (оба операнда — нули). Результат имеет тип int.

Если первый операнд — не нуль, то второй операнд не вычисляется. Любые побочные эффекты, которые могли произойти при вычислении второго операнда, не произойдут. Любой вызов функции во втором операнде выполняться не будет.

Условный оператор

условное-выражение: выражение-логического-ИЛИ

или

выражение-логического-ИЛИ ? выражение : условное-выражение

Символ ? разделяет первые две части условного оператора, а символ : — вторую и третью. Первый операнд должен быть скалярного типа (не структура, объединение или массив).

Сначала вычисляется первый операнд. Если его значение не равно нулю, то вычисляется второй операнд, и результатом оператора становится его значение. В противном случае вычисляется третий операнд, и результатом становится значение третьего операнда.

Какой бы операнд (второй или третий) ни вычислялся, другой операнд вычисляться не будет. Любые побочные эффекты, которые могли произойти при вычислении второго операнда, не произойдут.

Если второй и третий операнд — арифметического типа, то над ними производятся обычные арифметические преобразования, и результат имеет тот же тип, что и конвертированные операнды.

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

Если оба операнда — указатели, и один из них — «указатель на void», то результат имеет тип «указатель на void».

Если один операнд — указатель, а другой — константа null-указатель, то результат имеет тип этого указателя.

Если оба операнда — выражения типа void, то результат — void.

Другие комбинации типов результата не допускаются.

Помните, что в противоположность большинству других операторов, условный оператор выполняется справа налево. Например, выражение

a = b ? c : d ? e : f;

транслируется как, если бы были расставлены скобки:

a = b ? c : (d ? e : f);

Это создаёт путаницу, и, по возможности, лучше этого избегать.

Операторы присваивания

выражение-присваивания: условное-выражение

или

выражение-простого-присваивания

или

выражение-составного-присваивания

Оператор присваивания сохраняет значение в объекте, который обозначен левым операндом. Левый операнд должен быть поддающимся изменению l-значением.

Тип и значение результата такие же, как у левого операнда после присваивания.

Какой операнд, левый или правый, вычисляется первым, не определено.

Помните, что в противоположность большинству других операторов, оператор присваивания выполняется справа налево. Например, выражение

a += b = c;

транслируется как, если бы были расставлены скобки:

a += (b = c);

Простое присваивание

оператор-простого-присваивания: унарное-выражение = выражение-присваивания

Символ = обозначает простое присваивание. Значение правого операнда конвертируется в тип левого и, затем, замещает значение левого операнде.

Два операнда должны подчиняться одному из следующих правил:

Составное присваивание

выражение-составного-присваивания: унарное-выражение оператор-присваивания выражение-присваивания

оператор-присваивания: один из

+= -=
*= /= %=
&= ^= |=
<<= >>=

Оператор составного присваивания в виде a оп= b эквивалентен простому выражению присваивания a = a оп (b), но левый операнд вычисляется только раз.

Составной оператор присваивания должен иметь операнды, удовлетворяющие соответствующему бинарному оператору.

Оператор запятая

выражение: выражение-присваивания

или

выражение , выражение-присваивания

Обладая наименьшим приоритетом, оператор запятая сначала вычисляет левый операнд как выражение с типом void (оно вычисляется, но результат, какой бы ни был, отбрасывается), а затем вычисляет правый операнд. Результат имеет тип и значение второго операнда.

В контексте, где запятая используется как разделитель (список аргументов функции и список инициализаторов), выражение запятая должно помещаться в круглых скобках.

Например,

Fn( (pi=3.14159,two_pi=2*pi) );

функция Fn имеет один параметр, который имеет значение 2*pi.

for( i = 0, j = 0, k = 0;; i++, j++, k++ )
    команда;

Команда for принимает три выражения. В этом примере первое выражение инициализирует три объекта, а третье выражение увеличивает тоже три объекта.

Константные выражения

Константное выражение может использоваться в нескольких качествах:

В большинстве случаев константное выражение состоит из серии константных значений и операций, которые вычисляются в константное значение. Некоторые операции могут появляться только в операнде оператора sizeof. Это:

В константном выражении, которое является инициализатором, могут быть указаны константы с плавающей точкой и операторы приведения типа. Объекты со статической продолжительностью хранения и именующие выражения функций (имена) можно использовать для получения адреса, явно используя унарный оператор & (взятия адреса). Для имён функций можно также указывать только идентификатор.

Следующие примеры демонстрируют константные выражения, которые можно использовать везде:

3
256*3 + 27
OPSYS == OS_DOS /* Это -- имена макро */

Следующий набор примеров — константные выражения, которые допустимы только в инициализаторах:

&SomeObject
SomeFunction
3.5 * 7.2 / 6.5

В константном выражении, которое является частью директивы препроцессора #if или #elif допускаются только целочисленные константы и операторы (макро после замены следуют тем же правилам).