Операции и выражения

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

В Си определены 5 арифметических операций: сложение (знак операции "+"), вычитание ("-"), умножение ("*"), деление ("/") и взятие остатка от деления ("%"). Приоритеты и работа операций обычные: умножение, деление и взятие остатка от деления равноправны между собой и старше, чем сложение и вычитание. Ассоциативность (порядок выполнения) арифметических операций принята слева направо.

Операция % определена только над целыми операндами, а результат операции деления зависит от типа операндов. Деление целых в Си дает всегда целое число, если же хотя бы один из операндов вещественный, результат также будет вещественным:

3/2 //результат=1

3./2 //результат=1.5

В Си существует богатая коллекция разновидностей оператора присваивания. Обычное присваивание выполняется оператором "=":

x=y+z;

Возможно объединение присваивания с другой операцией, используемое как сокращенная форма записи для присваивания, изменяющего значение переменной:

x+=3; //эквивалентно x=x+3; p*=s; //эквивалентно p=p*s;Присваивание также может быть составным, при этом цепочка вычислений выполняется справа налево: i=j=0;с=1; a=b=c+1; //a=b=2;Присваивание начального значения переменной (а также элементов массива) может быть выполнено непосредственно при описании: int k=5;Для распространенных операций инкремента (увеличения на 1) и декремента (уменьшения на 1) есть специальные обозначения ++ и -- соответственно:i++; //эквивалентно i+=1; или i=i+1;Операнд инкремента и декремента может иметь целый или вещественный тип или быть указателем.Операции инкремента и декремента могут записываться как перед своим операндом (префиксная форма записи), так и после него (постфиксная запись). Для операции в префиксной форме операнд сначала изменяется, а затем его новое значение участвует в дальнейшем вычислении выражения. Для операции в постфиксной форме операнд изменяется после того, как его старое значение участвует в вычислении выражения:int i=3;printf ("\n%d",i++); //напечатает значение i=3printf ("\n%d",++i); //напечатает значение i=4 Присваивание с приведением типа (тип)(выражение) использует заключенное в круглые скобки название типа, к которому нужно привести результат:

float f1=4,f2=3;

int a = (int)(f1/f2); //a=1

f2=(float)a+1.5; //f2=2.5

При этом разрешены преобразования типов, приводящие к потере точности, ответственность за это целиком лежит на программисте.

Логические операции в языке Си делятся на 2 класса. Во‑первых, это логические функции, служащие для объединения условий, во‑вторых, поразрядные логические операции, выполняемые над отдельными битами своих операндов. Операнды логических операций могут иметь целый, вещественный тип, либо быть указателями. Типы первого и второго операндов могут различаться. Сначала всегда вычисляется первый операнд; если его значения достаточно для определения результата операции, то второй операнд не вычисляется.

Логические операции не выполняют каких‑либо преобразований по умолчанию. Вместо этого они вычисляют свои операнды и сравнивают их с нулем. Результатом логической операции является либо 0, понимаемый как ложь, либо ненулевое значение (обычно 1), трактуемый как истина. Существенно то, что в языке Си нет специального логического типа данных и тип результата логической операции — целочисленный.

Логическая функция "И" (соответствует and в Паскале) обозначается как &&, "ИЛИ" (or) как ||, унарная функция отрицания (not в Паскале) записывается как! перед своим операндом:

if (х<у && у<z) min=x;

if (!(x>=a && x<=z))

printf ("\nx не принадлежит [a,b]");

Приоритеты логических функций традиционны: операция! старше чем &&, которая, в свою очередь, старше ||. При необходимости приоритеты могут быть изменены с помощью круглых скобок.

Как отмечено ранее, поразрядные логические операции выполняются над отдельными битами (разрядами) своих операндов. Имеется три бинарных и одна унарная поразрядная операция. Они описаны в табл. 3.1.

Таблица 3.1. Поразрядные логические операции

Операнд x         Описание
Операнд y        
x|у         Побитовое ИЛИ
x&у         Побитовое И
x^y         Побитовое исключающее ИЛИ
~x         Побитовое отрицание

Примеры:

char x=1,y=3; char z=x&y; //z=1

char x=0x00; x=x^0x01; //либо x^=1;

//организует "флажок", переключающийся

//между состояниями 0 и 1

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

x<<=1; //соответствует x*=2;

Традиционные для любого языка операции отношения (сравнения) сравнивают первый операнд со вторым и вырабатывают целочисленное значение 1 (истина) или 0 (ложь). Операции отношения описаны в табл. 3.2.

Таблица 3.2. Операции отношения

Операция Проверяемое отношение
< > <= >= == != Первый операнд меньше, чем второй Первый операнд больше, чем второй Первый операнд меньше или равен второму Первый операнд больше или равен второму Первый операнд равен второму Первый операнд не равен второму

Обратите внимание, что в отличие от присваивания, сравнение требует указания двойного знака "равно".

Операция последовательного выполнения по очереди вычисляет два своих операнда, сначала первый, затем второй. Оба операнда являются выражениями. Знак операции – символ "," (запятая):

i=0,j=0;

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

Характерная для Си условная операция?: работает не с двумя, а с тремя операндами (является тернарной). Она имеет следующий формат: операнд1? операнд2: операнд3.

Операнд1 вычисляется и сравнивается с нулем, при этом он может иметь целый, плавающий тип, либо быть указателем. Если операнд1 не равен 0, вычисляется операнд2 и результатом операции является его значение. В противном случае вычисляется операнд3 и результатом является его значение. В любом случае вычисляется только один из операндов 2 или 3, но не оба. Примеры:

#define max(a,b) (a>b? a: b)

y=(x>0?1:(x==0?0:-1)); //y = знаку числа x

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

Табл. 3.3. Приоритет и ассоциативность операций

Знаки операций Наименование Ассоциативность
() []. -> Первичные Слева направо
+ - ~! * & ++ -- sizeof(тип) приведение типа Унарные Справа налево
* / % Мультипликативные Слева направо
+ - Аддитивные Слева направо
>> << Сдвиг Слева направо
< > <= >= Отношение Слева направо
==!= Отношение Слева направо
& Поразрядное И Слева направо
^ Поразрядное исключающее ИЛИ Слева направо
| Поразрядное включающее ИЛИ Слева направо
&& Логическое И Слева направо
|| Логическое ИЛИ Слева направо
?: Условная Справа налево
= *= /= %= += -= <<= >>= &= |= ^= Простое и составное присваивание Справа налево
, Последовательное вычисление Слева направо

Следует отметить, что приоритет некоторых операций явно неудачен, в частности, сдвига и поразрядных логических операций. Они имеют приоритет ниже, чем арифметические действия. Поэтому выражение а=b&0xF0+1 вычисляется как а= b&(0xF0+1), а а+b>>1 как (а+b)>>1.

Преобразование типов в Си производится либо неявно, например, при преобразовании по умолчанию операнды преобразуются к более длинным типам, либо в процессе присваивания, либо явно, путем выполнения операции приведения типа:переменная=(новый тип)выражение;

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

int a=-100; float p=4.5; a=(int)p;

unsigned b = (unsigned)a;

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

В теле некоторых операторов могут содержаться другие операторы. Оператор, находящийся в теле другого оператора, в свою очередь может содержать операторы.

Составной оператор ограничивается фигурными скобками { }. Все другие операторы заканчиваются точкой с запятой (;). Точка с запятой в языке Си является признаком конца оператора, а не разделителем операторов, как в ряде других языков.

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

Далее приведен полный список операторов Си.

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

Составной оператор или блок { }. Действие составного оператора заключается в последовательном выполнении содержащихся в нем операторов, за исключением тех случаев, когда какой‑либо оператор явно передает управление в другое место программы. Типичное применение блока подобно другим языкам – ограничение тела функции, тела цикла, описания структурного типа данных или ветви условного оператора.

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

Оператор‑выражение строится по правилам, описанным в п. 3.

Условный оператор if записывается в общем виде следующим образом:

if (УсловноеВыражение1) оператор1;

else if (УсловноеВыражение2) оператор2;

else if (УсловноеВыражениеN) операторN;

else оператор0;

Как и в других языках, выполняется только один из операторов 1,2,…,N, в зависимости от того, какое из соответствующих условных выражений первым оценено как истинное. Если все условия ложны, выполняется оператор0. Любая из ветвей, кроме первой, необязательна. Круглые скобки и точки с запятой обязательны везде, где указаны. Еще раз акцентируем внимание на том, что в Си отсутствуют булевские выражения и в качестве условий применяются обычные выражения языка Си. Значение выражения считается истинным, если оно не равно нулю, и ложным, если равно нулю. Из этого следует, что условные выражения не обязательно должны содержать операции отношения. Условия могут быть записаны в обычном виде

if (а < 0) …

а могут выглядеть, например, так:

if (а) … или if (а + b) ….

Условия могут включать логические функции:

if (x>0 && y>0) ch=1;

Оператор пошагового цикла for имеет следующий общий вид:

for (НВ; УВ; ВП) Оператор;

Здесь НВ — начальное выражение, служащее для инициализации параметров цикла, УВ — условное выражение, прямо или косвенно определяющее число повторений цикла (цикл выполняется, пока УВ не станет ложным), ВП — выражение приращения, используемое для модификации параметра или параметров цикла. Любое из трех выражений может быть опущено, а также любые 2 выражения или все сразу.

Цикл работает следующим образом: сначала вычисляется НВ, если оно имеется. Затем вычисляется УВ и производится его оценка следующим образом:

· если УВ истинно (не равно нулю), то выполняется тело оператора. Затем вычисляется ВП, если оно есть, и процесс повторяется;

· если УВ опущено, то его значение принимается за истину и процесс выполнения продолжается, как описано выше. В этом случае цикл for бесконечен и может завершиться только при выполнении в его теле операторов break,goto,return;

· если УВ ложно, то выполнение цикла заканчивается и управление передается следующему за ним оператору программы. Цикл for может завершиться также при выполнении операторов break,goto,return в его теле.

Пример: показанный ниже цикл выполняется 10 раз.

for (x=1; x<11; x++) printf ("*");

Оператор цикла с предусловием while:

while (выражение) оператор;

Тело цикла while выполняется до тех пор, пока значение выражения не станет ложным (равным нулю). Сначала вычисляется выражение, если оно изначально ложно, то тело цикла не выполняется и управление передается на следующий за ним оператор программы. Если выражение истинно, то выполняется тело цикла. Перед каждым следующим выполнением цикла выражение вычисляется заново. Процесс повторяется до тех пор, пока выражение не станет ложным. Оператор while может завершиться досрочно при выполнении break,goto илиreturn внутри своего тела.

Приведенный далее цикл while аналогичен показанному выше for:

x=1;while (x<11) { printf ("*"); x++;}

Оператор цикла с постусловием do записывается в виде

do операторwhile (выражение);

Тело цикла do выполняется один или несколько раз до тех пор, пока значение выражения не станет ложным (равным нулю). Сначала выполняется тело цикла — оператор, затем вычисляется условие — выражение. Если выражение ложно, цикл завершается и управление передается следующему за телом цикла оператору программы. Если выражение истинно (не равно нулю), то тело цикла выполняется вновь, и выражение вычисляется повторно. Выполнение цикла повторяется до тех пор, пока выражение не станет ложным. Цикл do может завершиться досрочно при выполнении в своем теле операторов break,goto,return.Показанный далее цикл по действию совпадает с примерами на for и while:

x=1;

do {

printf ("*"); x++;

} while (x<11);

Оператор‑переключатель switch записывается в виде

switch (выражение) {

объявление

case КВ1: оператор1;

case КВN: операторN;

default: оператор0;

}

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

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

Все КВ должны иметь порядковый тип. В ряде компиляторов КВ принудительно преобразуются к типу int. Тип каждой КВ также приводится к типу выражения переключения.

После вычисления выражения управление передается тому из операторов 1,…,N тела переключателя, значение константы варианта которого совпадает с вычисленным значением.

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

Оператор, следующий за ключевым словом default, выполняется, если ни одна из констант варианта не равна значению выражения переключения. Если ветвь default опущена, ни один оператор в теле переключателя не выполняется.

Синтаксически конструкции case и default являются метками, однако, на них нельзя передать управление по оператору goto. Метки case и default существенны только при начальной проверке, когда выбирается оператор для выполнения в теле переключателя. Все операторы тела переключателя, следующие за выбранным, выполняются последовательно, "не замечая" меток case и default, если только какой‑либо оператор не передаст управление за пределы тела переключателя. Для выхода из тела переключателя после выполнения одной из ветвей, как правило, используется оператор разрыва break.

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

char c=getch();switch (c) { case ' ': printf ("\nПробел"); break; case 72: case 80: printf ("\nСтрелка вверх или вниз"); break; default: printf ("\nДругая клавиша");}

Оператор продолжения continue передает управление на следующую итерацию в циклах do,for,while. Он может появиться только в теле этих операторов. Остающиеся в теле цикла операторы при этом не выполняются. В циклах do и while следующая итерация начинается с вычисления условного выражения. В цикле for следующая итерация начинается с вычисления выражения приращения, а затем происходит вычисление условного выражения.

Оператор разрыва break прерывает выполнение операторов do,for,while или switch. Он может содержаться только в теле этих операторов. Управление передается оператору программы, следующему за прерванным.

Оператор перехода goto имеет вид

goto метка;

и передает управление непосредственно на оператор, помеченный меткой:

метка: оператор;

Метка представляет собой обычный идентификатор. Область действия метки ограничивается функцией, в которой она определена. Каждая метка должна быть уникальна в пределах функции, где она указана. Нельзя передать управление по оператору goto в другую функцию. Метка оператора имеет смысл только для goto. Можно войти в блок, тело цикла, условный оператор, оператор‑переключатель по метке. Нельзя с помощью goto передать управление на конструкции case и default в теле переключателя.

Оператор возврата return выражение; заканчивает выполнение функции, в которой он содержится, и возвращает управление в вызывающую функцию.

Управление передается в точку вызывающей функции, непосредственно следующую за оператором вызова функции. Значение выражения, если оно задано, вычисляется, приводится к типу, объявленному для функции, содержащей оператор, и возвращается в вызывающую функцию. Если выражение опущено, то возвращаемое функцией значение не определено (является пустым).

Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: