Заключать в скобки первое выражение в условном выражении не обязательно, так как приоритет ?: очень низкий (более низкий приоритет имеет только присваивание), однако мы рекомендуем всегда это делать, поскольку благодаря обрамляющим скобкам условие в выражении лучше воспринимается.
Условное выражение часто позволяет сократить программу. В качестве примера приведем цикл, обеспечивающий печать n элементов массива по 10 на каждой строке с одним пробелом между колонками; каждая строка цикла, включая последнюю, заканчивается символом новой строки:
for (i = 0; i ‹ n; i++)
printf("%6d %c", a[i], (i%10 == 9 || i == n-1) ? '\n' : ' ');
Символ новой строки посылается после каждого десятого и после n-го элемента. За всеми другими элементами следует пробел. Эта программа выглядит довольно замысловато, зато она более компактна, чем эквивалентная программа с использованием if-else. Вот еще один хороший пример:
printf("Вы имеете %d элемент%s: \n", n, (n%10 == 1 && n%100 != 11)?
" ": ((n%100 ‹ 10 || n%100 › 20) && n%10 ›= 2 && n%10 ‹= 4) ?
"a": "ов");
Упражнение 2.10. Напишите функцию lower, которая переводит большие буквы в малые, используя условное выражение (а не конструкцию if-else).
2.12 Приоритет и очередность вычислений
В таблице 2.1 показаны приоритеты и очередность вычислений всех операторов, включая и те, которые мы еще не рассматривали. Операторы, перечисленные на одной строке, имеют одинаковый приоритет: строки упорядочены по убыванию приоритетов; так, например, *, / и % имеют одинаковый приоритет, который выше, чем приоритет бинарных + и -. "Оператор" () относится к вызову функции. Операторы -› и . (точка) обеспечивают доступ к элементам структур; о них пойдет речь в главе 6, там же будет рассмотрен и оператор sizeof (размер объекта). Операторы * (косвенное обращение по указателю) и & (получение адреса объекта) обсуждаются в главе 5. Оператор "запятая" будет рассмотрен в главе 3.
Таблица 2.1. Приоритеты и очередность вычислений операторов
Операторы | Выполняются |
---|---|
() [] -› . | слева направо |
! ~ ++ -- + - * & (type) sizeof | справа налево |
* / % | слева направо |
+ - | слева направо |
‹‹ ›› | слева направо |
‹ ‹= › ›= | слева направо |
== != | слева направо |
& | слева направо |
^ | слева направо |
| | слева направо |
&& | слева направо |
|| | слева направо |
?: | справа налево |
= += -= *= /= %= &= ^= |= ‹‹= ››= | справа налево |
, | слева направо |
Примечание. Унарные операторы +, -, * и & имеют более высокий приоритет, чем те же бинарные операторы.
Заметим, что приоритеты побитовых операторов &, ^ и | ниже, чем приоритет == и !=, из-за чего в побитовых проверках, таких как
if ((x & MASK) == 0)…
чтобы получить правильный результат, приходится использовать скобки. Си подобно многим языкам не фиксирует очередность вычисления операндов оператора (за исключением &&, ||, ?: и ,). Например, в инструкции вида
x = f() + g();
f может быть вычислена раньше g или наоборот. Из этого следует, что если одна из функций изменяет значение переменной, от которой зависит другая функция, то помещаемый в x результат может зависеть от очередности вычислений. Чтобы обеспечить нужную последовательность вычислений, промежуточные результаты можно запоминать во временных переменных.
Очередность вычисления аргументов функции также не определена, поэтому на разных компиляторах
printf("%d %d\n", ++n, power(2, n)); /* НЕВЕРНО*/
может давать несовпадающие результаты. Результат вызова функции зависит от того, когда компилятор сгенерирует команды увеличения n - до или после обращения к power. Чтобы обезопасить себя от возможного побочного эффекта, достаточно написать
++n;
printf("%d %d\n", n, power(2, n));
Обращения к функциям, вложенные присвоения, инкрементные и декрементные операторы дают "побочный эффект", проявляющийся в том, что при вычислении выражения значения некоторых переменных изменяются. В любом выражении с побочным эффектом может быть скрыта трудно просматриваемая зависимость результата выражения от очередности изменения значений переменных, входящих в выражение. В такой, например, типично неприятной ситуации
a[i] = i++; /* I.B.: doubtful example */