Странный PHP
А вот ПХП, в отличие от ДжаваСкрипта действительно странный. Давайте посмотрим на такой вот код и его результат:
$ php -a
Interactive shell
php > $a = 1; echo $a + $a++;
3
php > $a = 1; echo $a + $a + $a++;
3
Как видите, в том и другом случае у нас один результат — «3». Даже первая «тройка», казалось бы, противоречит здравому смыслу, а вторая — тем более. Что же происходит? Давайте разбираться.
Как работает первый пример?
Операция сложения левоассоциативна — разбор агрументов начинается слева направо. Двигаясь таким образом, парсер видит выражение в котором две операции — сложение и постинкремент, у постинкремента приоритет выше, поэтому вычисляется сначала он — возвращая в качестве значения «1» и увеличивая переменную на единицу, потом вычисляется операция сложения, складывая полученную единицу с двойкой (так как постинкремент увеличил значение переменной на единицу). Получается «три».
Похожим образом обрабатывается умножение вместе со сложением: 2 + 2 * 2 = 6, а не 8, потому что умножение имеет более высокий приоритет.
Во втором случае всё происходит похожим образом, но чуть иначе — парсер, обрабатывая левоассоциативное сложение, берёт первые два аргумента, складывает их, получает «двойку», двигается дальше, видит двойку, сложение и постинкремент переменной. Постинкремент более приоритетный, он его вычисляет раньше, возвращая в сложение «единицу», значение переменной увеличивается, но его уже никто не использует — складываются числа «2» (от предыдущего сложения) и «1» (вернул постинкремент). Получается «три».
В байт-кодах всё перечисленное выглядит следующим образом:
Тут с восьмой строки начинается второй пример (правда присваивание единицы во втором примере я опустил — как видите второй операции ASSIGN нет).
Похоже на int i = 5; i = ++i + ++i;
Комментарий для Артём Зорин:
Разве?
Это ведь Си? Я скомпилировал, получил ожидаемый ответ — 13. Что тут неожиданного? 6 + 7 = 13.
Комментарий для Евгения Степанищева:
Всё равно не могу понять, почему во втором примере постинкремент вернул 1.
«значение переменной увеличивается, но его уже никто не использует — складываются числа „2“ (от предыдущего сложения) и „1“ (вернул постинкремент)»
Т. е. после сложения, когда начинает выполняться постинкремент, что для него лежит в $a? Там ведь так и осталась единица? Почему он её не увеличивает на 1 как в первом примере?
Комментарий для Василий Топоров:
Поддерживаю, мне тоже это не понятно
Комментарий для Василий Топоров:
Потому что постинкремент всегда возвращает текущее значение (т. е. $a, а там 1), он так устроен — возвращает значение, потом увеличивает переменную на единицу.
Комментарий для Василий Топоров:
«Потому что постинкремент всегда возвращает текущее значение (т. е. $a, а там 1), он так устроен — возвращает значение, потом увеличивает переменную на единицу.»
Фактически получается, что во втором примере постинкремент не выполняется.
Ещё вопрос, если позволите: почему этот пример выводит 5? Разве не должны скобки установить приоритет выполнения?
php > $a = 1; echo $a + ($a + $a++);
5
Комментарий для Василий Топоров:
тут так — ($a + $a++) — первым считается $a++ — он выдает 1 и меняет $a на 2 и результат складывает = 3.
все верно
а так, да ПХП местами странный.
Что-то не могу понять логики объяснения, почему в первом примере мы НЕ суммируем возвращаемую постинкрементом 1, а во втором примере суммируем ?
По-моему мы пытаемся оправдать косяк php
в С поведение в первом и втором примере одинаково:
#include <stdio.h>
int main()
{
int a = 1;
printf(«%d\n», a + a++);
a = 1;
printf(«%d\n»,a + a + a++);
return 0;
}
результат:
2
3
Комментарий для Dmitry:
Ну так перечитайте ещё раз, я не знаю как ещё понятнее объяснить.
Следуя логике второго примера применительно к первому:
Парсер, обрабатывая левоассоциативное сложение, берёт первый аргумент, 1, двигается дальше, видит сложение и постинкремент переменной. Постинкремент более приоритетный, он его вычисляет раньше, возвращая в сложение «единицу», значение переменной увеличивается, но его уже никто не использует — складываются числа «1» (первый аргумент) и «1» (вернул постинкремент). Получается «два».
Где эта тонкая грань, когда постинкремент в одном случае возвращает в сложение 2, а в другом 1?
Действительно хочется понять.
Комментарий для Dmitry:
В том и другом случае постинкремент работает одинаково, вычисляется просто в разом порядке.