Приоритеты
Приоритеты операций задают последовательность вычислений в сложном выражении. Например, какое значение получит ival?
int ival = 6 + 3 * 4 / 2 + 2;
Если вычислять операции слева направо, получится 20. Среди других возможных результатов будут 9, 14 и 36. Правильный ответ: 14.
В С++ умножение и деление имеют более высокий приоритет, чем сложение, поэтому они будут вычислены раньше. Их собственные приоритеты равны, поэтому умножение и деление будут вычисляться слева направо. Таким образом, порядок вычисления данного выражения таков:
1. 3 * 4 => 12
2. 12 / 2 => 6
3. 6 + 6 => 12
4. 12 + 2 => 14
Следующая конструкция ведет себя не так, как можно было бы ожидать. Приоритет операции присваивания меньше, чем операции сравнения:
while ( ch = nextChar() != '\n' )
Программист хотел присвоить переменной ch значение, а затем проверить, равно ли оно символу новой строки. Однако на самом деле выражение сначала сравнивает значение, полученное от nextChar(), с '\n', и результат – true или false – присваивает переменной ch.
Приоритеты операций можно изменить с помощью скобок. Выражения в скобках вычисляются в первую очередь. Например:
4 * 5 + 7 * 2 ==> 34
4 * ( 5 + 7 * 2 ) ==> 76
4 * ( (5 + 7) * 2 ) ==> 96
Вот как с помощью скобок исправить поведение предыдущего примера:
while ( (ch = nextChar()) != '\n' )
Операторы обладают и приоритетом, и ассоциативностью. Оператор присваивания правоассоциативен, поэтому вычисляется справа налево:
ival = jval = kva1 = lval
Сначала kval получает значение lval, затем jval – значение результата этого присваивания, и в конце концов ival получает значение jval .
Арифметические операции, наоборот, левоассоциативны. Следовательно, в выражении
ival + jval + kva1 + 1va1
сначала складываются ival и jval, потом к результату прибавляется kval, а затем и lval.
В таблице 4.4 приведен полный список операторов С++ в порядке уменьшения их приоритета. Операторы внутри одной секции таблицы имеют равные приоритеты. Все операторы некоторой секции имеют более высокий приоритет, чем операторы из секций, следующих за ней. Так, операции умножения и деления имеют одинаковый приоритет, и он выше приоритета любой из операций сравнения.
Упражнение 4.18
Каков порядок вычисления следующих выражений? При ответе используйте таблицу 4.4.
(a) ! ptr == ptr->next
(b) ~ uc ^ 0377 & ui << 4
(c) ch = buf[ bp++ ] != '\n'
Упражнение 4.19
Все три выражения из предыдущего упражнения вычисляются не в той последовательности, какую, по-видимому, хотел задать программист. Расставьте скобки так, чтобы реализовать его первоначальный замысел.
Упражнение 4.20
Следующие выражения вызывают ошибку компиляции из-за неправильно понятого приоритета операций. Объясните, как их исправить, используя таблицу 4.4.
(a) int i = doSomething(), 0;
(b) cout << ival % 2 ? "odd" : "even";
Таблица 4.4. Приоритеты операций
Оператор |
Значение |
Использование |
|
:: |
Глобальная область видимости |
::name |
|
:: |
Область видимости класса |
class::name |
|
:: |
Область видимости пространства имен |
namespace::name |
|
. |
Доступ к члену |
object.member |
|
-> |
Доступ к члену по указателю |
pointer->member |
|
[] |
Взятие индекса |
variable[expr] |
|
() |
Вызов функции |
name(expr_list) |
|
() |
Построение значения |
type(expr_list) |
|
++ |
постфиксный инкремент |
lvalue++ |
|
-- |
постфиксный декремент |
lvalue-- |
|
typeid |
идентификатор типа |
typeid(type) |
|
typeid |
идентификатор типа выражения |
typeid(expr) |
|
const_cast |
преобразование типа |
const_cast<type>(expr) |
|
dynamic_cast |
преобразование типа |
dynamic_cast<type>(expr) |
|
reinterpret_cast |
приведение типа |
reinterpret_cast<type> (expr) |
|
static_cast |
приведение типа |
static_cast<type>(expr) |
|
sizeof |
размер объекта |
sizeof expr |
|
sizeof |
размер типа |
sizeof( type) |
|
++ |
префиксный инкремент |
++lvalue |
|
-- |
префиксный декремент |
--lvalue |
|
~ |
побитовое НЕ |
~expr |
|
! |
логическое НЕ |
!expr |
|
- |
унарный минус |
-expr |
|
+ |
унарный плюс |
+expr |
|
* |
разыменование |
*expr |
|
& |
адрес |
&expr |
|
() |
приведение типа |
(type)expr |
|
new |
выделение памяти |
new type |
|
new |
выделение памяти и инициализация |
new type(exprlist) |
|
new |
выделение памяти |
new (exprlist) type(exprlist) |
|
new |
выделение памяти под массив |
все формы |
|
delete |
освобождение памяти |
все формы |
|
delete |
освобождение памяти из-под массива |
все формы |
|
->* |
доступ к члену классу по указателю |
pointer-> *pointer_to_member |
|
.* |
доступ к члену класса по указателю |
object.*pointer_to_member |
|
* |
умножение |
expr * expr |
|
/ |
деление |
expr / expr |
|
% |
деление по модулю |
expr % expr |
|
+ |
сложение |
expr + expr |
|
- |
вычитание |
expr - expr |
|
<< |
сдвиг влево |
expr << expr |
|
>> |
сдвиг вправо |
expr >> expr |
|
< |
меньше |
expr < expr |
|
<= |
меньше или равно |
expr <= expr |
|
> |
больше |
expr > expr |
|
>= |
больше или равно |
expr >= expr |
|
== |
равно |
expr == expr |
|
!= |
не равно |
expr != expr |
|
& |
побитовое И |
expr & expr |
|
^ |
побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ |
expr ^ expr |
|
| |
побитовое ИЛИ |
expr | expr |
|
&& |
логическое И |
expr && expr |
|
|| |
логическое ИЛИ |
expr || expr |
|
?: |
условный оператор |
expr ? expr * expr |
|
= |
присваивание |
l-значение = expr |
|
=, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= |
составное присваивание |
l-значение += expr и т.д. |
|
throw |
возбуждение исключения |
throw expr |
|
, |
запятая |
expr, expr |