почему постфиксный оператор не выполняется раньше всего?
int a = 1;
a = ++a * a++;
int b = 1;
b = b++ * ++b;
Почему a = 4, b = 3? Приоритет у постфиксного оператора же больше, значит он выполнится до префиксного и умножения. Разве не так?
Ответы (2 шт):
Приоритет никак не связан с порядком операций. Приоритеты лишь позволяют иногда (если они подобраны хорошо, то часто) писать без скобок.
В C# правило следующее: выражения вычисляются строго (с точностью до as-if-правила) слева направо. Точнее, строится дерево выражения, и обход его производится в обратном порядке: левое поддерево, затем правое поддерево, затем сам узел.
Выражение a = ++a * a++ даёт такое дерево:
assign
/ \
a(1) *
/ \
preincrement postincrement
/ \
a(2) a(3)
Вычисление:
- Вычисляем корневой узел assign, для этого:
- Вычисляем адрес
a(1). - Начинаем вычислять правый аргумент присваивания, для этого:
- Начинаем вычислять преинкремент, для этого:
- Вычисляем адрес
a(2) - Выполняем преинкремент (
aстановится равным 2, значение узла 2). - Начинаем вычислять постинкремент, для этого:
- Берём снова адрес
a(3) - Выполняем постинкремент (
aстановится равным 3, значение узла 2). - Умножение даёт 4.
- Выполняем присваивание,
aстановится равным 4.
Выражение b = b++ * ++b даёт такое дерево:
assign
/ \
b(1) *
/ \
postincrement preincrement
/ \
b(2) b(3)
Вычисление:
- Вычисляем корневой узел assign, для этого:
- Вычисляем адрес
b(1). - Начинаем вычислять правый аргумент присваивания, для этого:
- Начинаем вычислять постинкремент, для этого:
- Вычисляем адрес
b(2) - Выполняем постинкремент (
bстановится равным 2, значение узла 1). - Начинаем вычислять преинкремент, для этого:
- Берём снова адрес
b(3) - Выполняем преинкремент (
bстановится равным 3, значение узла 3). - Умножение даёт 3.
- Выполняем присваивание,
bстановится равным 3.
Дополнительное чтение по теме: Eric Lippert. Precedence vs associativity vs order.
Ещё один забавный пример тут.
В отличие от C#, в некоторых других языках (например, C++) порядок вычисления может быть не определён, а выражения наподобие тех, которые вы привели, могут приводить и вовсе к неопределённому поведению.
Да вроде всё логично.
int a = 1;
a = ++a * a++;
Оператор ++a увеличивает a и возвращает увеличенное значение.
А оператор a++ наоборот - сначала возвращает значение, а потом увеличивает a.
Что мы имеем, т.е. что возвращает инкремент и чему после этого равно a:
a = ++a * a++;
^^^ ^^^
2(a=2) 2(a=3)
После a++ в a будет 3, но это значение тут же затрётся после присваивания результата a = 2 * 2, поэтому в a будет 4.
int b = 1;
b = b++ * ++b;
^^^ ^^^
1(b=2) 3(b=3)
Тут в b и так уже 3 после двух инкрементов, и после присваивания результата вычисления b = 1 * 3 - тоже 3.