Nickolay.info. Обучение. Лекции по Си. Глава 3

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

 

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

В Си определены 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=3

printf ("\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

0

0

1

1

Описание

Операнд y

0

1

0

1

x

0

1

1

1

Побитовое ИЛИ

x

0

0

0

1

Побитовое И

x^y

0

1

1

0

Побитовое исключающее ИЛИ

~x

1

 

0

 

Побитовое отрицание

Примеры:

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;

 

 

Рейтинг@Mail.ru
вверх гостевая; E-mail