почему постфиксный оператор не выполняется раньше всего?

int a = 1;
a = ++a * a++;
int b = 1;
b = b++ * ++b;

Почему a = 4, b = 3? Приоритет у постфиксного оператора же больше, значит он выполнится до префиксного и умножения. Разве не так?


Ответы (2 шт):

Автор решения: VladD

Приоритет никак не связан с порядком операций. Приоритеты лишь позволяют иногда (если они подобраны хорошо, то часто) писать без скобок.

В C# правило следующее: выражения вычисляются строго (с точностью до as-if-правила) слева направо. Точнее, строится дерево выражения, и обход его производится в обратном порядке: левое поддерево, затем правое поддерево, затем сам узел.

Выражение a = ++a * a++ даёт такое дерево:

        assign
        /    \
      a(1)    *
            /   \
  preincrement  postincrement
          /       \
        a(2)      a(3)

Вычисление:

  1. Вычисляем корневой узел assign, для этого:
  2. Вычисляем адрес a (1).
  3. Начинаем вычислять правый аргумент присваивания, для этого:
  4. Начинаем вычислять преинкремент, для этого:
  5. Вычисляем адрес a (2)
  6. Выполняем преинкремент (a становится равным 2, значение узла 2).
  7. Начинаем вычислять постинкремент, для этого:
  8. Берём снова адрес a (3)
  9. Выполняем постинкремент (a становится равным 3, значение узла 2).
  10. Умножение даёт 4.
  11. Выполняем присваивание, a становится равным 4.

Выражение b = b++ * ++b даёт такое дерево:

        assign
        /    \
      b(1)    *
            /   \
 postincrement  preincrement
          /       \
        b(2)      b(3)

Вычисление:

  1. Вычисляем корневой узел assign, для этого:
  2. Вычисляем адрес b (1).
  3. Начинаем вычислять правый аргумент присваивания, для этого:
  4. Начинаем вычислять постинкремент, для этого:
  5. Вычисляем адрес b (2)
  6. Выполняем постинкремент (b становится равным 2, значение узла 1).
  7. Начинаем вычислять преинкремент, для этого:
  8. Берём снова адрес b (3)
  9. Выполняем преинкремент (b становится равным 3, значение узла 3).
  10. Умножение даёт 3.
  11. Выполняем присваивание, b становится равным 3.

Дополнительное чтение по теме: Eric Lippert. Precedence vs associativity vs order.

Ещё один забавный пример тут.


В отличие от C#, в некоторых других языках (например, C++) порядок вычисления может быть не определён, а выражения наподобие тех, которые вы привели, могут приводить и вовсе к неопределённому поведению.

→ Ссылка
Автор решения: CrazyElf

Да вроде всё логично.

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.

→ Ссылка