В данном случае это ошибка компилятора C++. точнее то, что называется «неопределённое поведение»
x = x + x++;
такой код тоже даст в результате 7, и это неправильно. Хотя неправильнее писать такой код.
IL_0003: ldloc.0 //вычисляем x. стек [x]
IL_0004: ldloc.0 // начинаем вычислять x++. Загружаем в стек x. стек [x,x]
IL_0005: dup //сохраняем в стеке x и загружаем его копию. стек [x,x,x]
IL_0006: ldc.i4.1 //загружаем 1. стек [x,x,x,1]
IL_0007: add //выполняем сложение. стек [x,x,(x+1)]
IL_0008: stloc.0 //устанавливаем новое значение x. Обратите внимание что в стеке находится 2 СТАРЫХ значения x. стек [oldx,oldx]
IL_0009: add // выполняем операцию сложения. В стеке [oldx+oldx]
IL_000a: stloc.0 //выполняем оператор присвоения, стек пуст. В переменной x находится сумма двух его первоначальных значений
P.S. Рекомендую обратить внимание на то, что не всегда выполняются все аргументы оператора. Например логические операторы выполняются до момента когда будет известно значение всего выражения. Также в ходе выполнения операции может возникнуть исключение, тогда часть действий выполнится, а часть нет.
Немного ликбеза:
Бинарные выражения большинством компиляторов/интерпретаторов выполняются так.
1) выбираем оператор с наименьшим приоритетом
2) вычисляем его слева-направо для всех операторов кроме присвоения.
вычисляем первый аргумент, вычисляем второй аргумент, выполняем первую операцию, вычисляем третий аргумент, выполняем вторую операцию и т.д.
Присвоение для понимания лучше преобразовать в выражение со скобками. Так a+=b раскрывается как a = (a+b).
a+=b=c+=d раскрывается так a = (a+(b=c+=d)), a = (a+(b=(c+=d))), a = (a+(b=(c=(c+d))))
Унарные операторы вычисляются когда вычисляется непосредственно аргумент в который они входят. Их можно раскрыть с помощью функции (х (в компиляторе используется инструкция дублирования значения на вершине стека)
Например:
x++;
T RightIncrement (ref T x) {
var tmpx = x;
x = x+1;
return tmpx;
}
++x; раскрывается как (x=x+1)
Тогда наше выражение раскрывается как
x = (x + RightIncrement(ref x))
Сегодня таки истекла жизнь бесплатного рефлектора. Спасибо за статью — в очередной раз порадовали JetBrains. Прискорбно, конечно, что IL не показывает — придётся пока чем-нибудь другим пользоваться, или дату на компе откатывать…
x = x + x++;
такой код тоже даст в результате 7, и это неправильно. Хотя неправильнее писать такой код.
IL_0003: ldloc.0 //вычисляем x. стек [x]
IL_0004: ldloc.0 // начинаем вычислять x++. Загружаем в стек x. стек [x,x]
IL_0005: dup //сохраняем в стеке x и загружаем его копию. стек [x,x,x]
IL_0006: ldc.i4.1 //загружаем 1. стек [x,x,x,1]
IL_0007: add //выполняем сложение. стек [x,x,(x+1)]
IL_0008: stloc.0 //устанавливаем новое значение x. Обратите внимание что в стеке находится 2 СТАРЫХ значения x. стек [oldx,oldx]
IL_0009: add // выполняем операцию сложения. В стеке [oldx+oldx]
IL_000a: stloc.0 //выполняем оператор присвоения, стек пуст. В переменной x находится сумма двух его первоначальных значений
P.S. Рекомендую обратить внимание на то, что не всегда выполняются все аргументы оператора. Например логические операторы выполняются до момента когда будет известно значение всего выражения. Также в ходе выполнения операции может возникнуть исключение, тогда часть действий выполнится, а часть нет.
Бинарные выражения большинством компиляторов/интерпретаторов выполняются так.
1) выбираем оператор с наименьшим приоритетом
2) вычисляем его слева-направо для всех операторов кроме присвоения.
вычисляем первый аргумент, вычисляем второй аргумент, выполняем первую операцию, вычисляем третий аргумент, выполняем вторую операцию и т.д.
Присвоение для понимания лучше преобразовать в выражение со скобками. Так a+=b раскрывается как a = (a+b).
a+=b=c+=d раскрывается так a = (a+(b=c+=d)), a = (a+(b=(c+=d))), a = (a+(b=(c=(c+d))))
Унарные операторы вычисляются когда вычисляется непосредственно аргумент в который они входят. Их можно раскрыть с помощью функции (х (в компиляторе используется инструкция дублирования значения на вершине стека)
Например:
x++;
T RightIncrement (ref T x) {
var tmpx = x;
x = x+1;
return tmpx;
}
++x; раскрывается как (x=x+1)
Тогда наше выражение раскрывается как
x = (x + RightIncrement(ref x))