Comments 19
Что-то я не понял, почему в случае
$c = ($a + $a) + ($a++)
получили int(3)? Опечатка?
Сложение в левой скобке дает 2, во второй тоже 2, потом 2 и 2 складываются.
Или с фразой "инкремент имеет более высокий приоритет" что-то не так.
Там же пост инкремент, сначала к 2 добавляется $а со значением 1, потом $a инкрементируется (но уже поздно)
Постинкремент в текущее выражение возвращает текущее значение переменной, а после этого увеличивает значение самой переменной. Оба примера в итоге сводятся к 2+1. Операнды в выражении инициализируются не в порядке упоминания, а в порядке выполнения операторов.
Если разложить примеры, то выйдет
// $c = $a + $a++;
$a = 1;
$c = $a; $a = $a + 1; // постинкремент в выражении
$c = $c + $a; // сложение
// $c = $a + $a + $a++;
$a = 1;
$c = $a + $a; // левое сложение
$c = $c + $a; $a = $a + 1; // постинкремент и правое сложение
$a = $a + 1;
идёт перед $c = $c + $a;
, а во втором — после? Это как-то непоследовательноПотому что
Операнды в выражении инициализируются не в порядке упоминания, а в порядке выполнения операторов.
В первом примере интерпретатор хочет произвести сложение, натыкается на $a++, выполняет и получает выражение "текущее значение А (уже равное двум) + старое значение А (ещё равное единице)" = 3.
Во втором примере он сначала выполняет первое слева сложение, получает $a + $a = 2; затем хочет выполнить следующее сложение, натыкается на $a++, выполняет его, а затем получает выражение "2 + старое значение А", результатом вычисления которого так же является тройка.
P.S. и да, в обоих примерах $a = $a+1; идёт после сложения. Смотри код внимательней: в одном примере одно сложение, в другом два.
Или, может, так понятней будет:
// $c = $a + $a + $a++;
$a = 1;
$c = $a + $a; // левое сложение
$b = $a; $a = $a + 1; // постинкремент
$c = $c + $b; // правое сложение
Операнды в выражении инициализируются не в порядке упоминания, а в порядке выполнения операторов.
Всё-таки порядок вычисления операндов — не определен (это прямо в документации PHP написано). Так что в одной версии PHP может посчитаться одним образом, а в другой — другим.
Может получиться и так, что из 2х групп ($a+$a)
и ($a++)
, пост-инкремент ($a++)
будет считаться первым и тогда сумма ($a+$a)
уже будет равна 4, а общий результат будет 5.
Я отвечал на вопрос "почему оно возвращает 3 в обоих вариантах", а не "сколько оно должно возвращать по стандарту". Порядок вычисления операндов, всё-таки, в текущей версии интерпретатора реализован так как реализован и уже не изменится.
А про то, что он может измениться в следующем релиза, как раз, написано и в стандарте, и в переведённой вами заметке Никиты, прямо перед предложением о том, что так писать не надо.
Результат:
$tmp_2 = POST_INC $a
не $tmp_2=2 $a=1, а, наоборот, $tmp_2=1 $a=2. Т.е. $tmp_2 — это результат $a++, а не состояние $a
Ведь:
$a = 1;
$tmp_2 = $a++;
var_dump($a, $tmp_2); // int(2), int(1)
$c = $a + $a + $a++ = ($a + $a) + ($a++) = (1+1) + (1)
$c = $a + $a++ = $a + ($a++) = 2+(1)
Видимо оп коды это еще не совсем справа команда, слева результат без сайд эффектов.
Т.е. опкоды — это скорее результат разложения кода на более простые операции и последовательности операций, а не линейный «машинный код»
Вот почему на код ревью я настаиваю, чтобы инкременты/декременты всегда были единственным выражением в инструкции. Если и пропускаю, то по невнимательности.
Любопытно, что точное такое же поведение среди всех других языков демонстрирует только компилятор GCC для С++, а вот CLang выдает адекватные значения и предупреждение.
gcc -Wall
тоже предупреждает, хотя логично чтобы это было по умолчанию.
Вообще же по спецификации C/C++ порядок выполнения действий в пределах выражения отдаётся на усмотрение компилятору и не определен совершенно официально, любое "определенное" поведение конкретного компилятора — это частный случай.
А почему так есть объяснение?
Для того чтобы можно было оптимизировать выполнение операций если конечный результат не зависит от порядка вычисления.
К примеру, если у нас есть нечто вроде:
x = a * b + c * d;
и при этом значения c
или d
уже находятся в регистрах в связи с более ранним использованием, компилятор может сгенерировать код для вычисления правого слагаемого перед вычислением левого.
Каждый опкод имеет максимум 2 операнда и опциональный результат.
И extend, который может кардинально поменять работу опкода
Порядок вычисления в PHP