none
x += x++ Как выполняется выражение в С# RRS feed

  • Вопрос

  • Добрый день!

    Подскажите, как выполняется выражение в C# x += x++ ?

    int x = 3;
    x += x++;

    Согласно Приоритет и порядок вычисления в С# 

    данное выражение должно быть равносильно x=4+3 т.е. в итоге x=7, но на деле получается 6 (почему-то постфиксный инкремент не обновляет значение переменной x значением x+1)

    Объясните, почему так получается?



    24 ноября 2016 г. 6:07

Ответы

  • Выражения в C# выполняются с лево на право. Если расписать Ваше выражение, то получится следующие:
    int x = 3;
    int t1 = x; // 3
    int t2 = x++; // 3
    x = t1 + t2 // 6
    24 ноября 2016 г. 8:37
  • Приоритет и порядок выполнения это разные вещи. В вашем случае у оператора += два операнда x и x++. Так как C# вычисляет выражения с лева на право, то сначала будет вычислен левый операнд x (t1 в моём коде) в результате Вы получите значение переменной x в момент вычисления (не ссылку на переменную x, а именно значение), а только потом будет вычислен правый операнд x++ (t2 в моём коде). Затем будет вычислен сам оператор +=.
    24 ноября 2016 г. 13:42

Все ответы

  • Я бы сказал что результат не определен и может быть разным в зависимости от реализации JIT, процессора, уровня оптимизации, наличия отладчика и т.п. Дело в том что компилятор использует копии переменных считая их равными самим переменным, а в подобных случаях это не так.

    Например, 6 выйдет если сначала сложили 3 + 3 в регистре (получив 6), потом увеличили х до 4 (помните что 6 еще не записано в память), а потом занесли предыдущий результат. Можете попробовать разные варианты очередности и посмотрите какие еще значения можно получить. Думаю 4 и 7 вполне возможны.

    Это в общем то всем известная фишка, поэтому важно так никогда не делать. Если вам просто интересно то переключите на ассемблер и посмотрите что именно происходит.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Предложено в качестве ответа VadimTagil 23 декабря 2016 г. 19:37
    24 ноября 2016 г. 7:25
    Модератор

  • int x = 3;
    x += x++;
    Console.WriteLine(x.ToString());
    Console.ReadKey();

    В IL превращается в:

      .entrypoint
      // Code size       29 (0x1d)
      .maxstack  4
      .locals init ([0] int32 x)
      IL_0000:  ldc.i4.3 		//Push num of type int32 onto the stack as int32. 
      IL_0001:  stloc.0 		//Pop a value from stack into local variable 0.
      IL_0002:  ldloc.0 		//Load local variable 0 onto stack. В стеке:3
      IL_0003:  ldloc.0 		//Load local variable 0 onto stack. В стеке:3|3
      IL_0004:  dup 		//Duplicate the value on the top of the stack. В стеке:3|3|3
      IL_0005:  ldc.i4.1 		//Push num of type int32 onto the stack as int32. В стеке:3|3|3|1
      IL_0006:  add 		//Add two values, returning a new value. В стеке:3|3|4
      IL_0007:  stloc.0 		//Pop a value from stack into local variable 0. В стеке:3|3
      IL_0008:  add 		//Add two values, returning a new value. В стеке:6
      IL_0009:  stloc.0 		//Pop a value from stack into local variable 0.
      IL_000a:  ldloca.s   x	//Load address of local variable with index indx.
      IL_000c:  call       instance string [mscorlib]System.Int32::ToString()
      IL_0011:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_0016:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
      IL_001b:  pop
      IL_001c:  ret
    Т.е. полученная четверка просто отбрасывается? Мда. Я догадывался, что сложные выражения использовать плохо, но не знал что настолько...

    • Предложено в качестве ответа VadimTagil 23 декабря 2016 г. 19:37
    24 ноября 2016 г. 7:39
  • Спецификация C# строго определяет последовательность вычисления выражений, так что результат всегда будет один и тот же.
    24 ноября 2016 г. 8:28
  • Выражения в C# выполняются с лево на право. Если расписать Ваше выражение, то получится следующие:
    int x = 3;
    int t1 = x; // 3
    int t2 = x++; // 3
    x = t1 + t2 // 6
    24 ноября 2016 г. 8:37
  • Выражения в C# выполняются с лево на право. Если расписать Ваше выражение, то получится следующие:
    int x = 3;
    int t1 = x; // 3
    int t2 = x++; // 3
    x = t1 + t2 // 6

    Но есть же ещё приоритеты, например умножение выполнится в первую очередь перед сложением независимо справа оно стоит или слева.

    В рассматриваемом случае

    x += x++

    сначала выполняется ++, потом уже +=


    24 ноября 2016 г. 10:53
  • https://msdn.microsoft.com/en-us/library/36x43w8w.aspx

    "The second form is a postfix increment operation. The result of the operation is the value of the operand before it has been incremented."

    выполняться то он выполняется, но толку 0. постфиксный инкремент возвращает значение ДО инкремента, оно и идет в следующую операцию.

    в листинге IL видно, как четверка честно вычисляется до операции суммирования и помещается в переменную 0, но потом перезаписывается 6-кой

    24 ноября 2016 г. 11:23
  • Всё равно не пойму, раз инкремент выполняется первым, то в переменную x после его выполнения должно быть записано значение 4, т.е. x=4+3=7. Почему в итоге записывается всё равно 3? Это какой-то баг?
    24 ноября 2016 г. 11:53
  • Приоритет и порядок выполнения это разные вещи. В вашем случае у оператора += два операнда x и x++. Так как C# вычисляет выражения с лева на право, то сначала будет вычислен левый операнд x (t1 в моём коде) в результате Вы получите значение переменной x в момент вычисления (не ссылку на переменную x, а именно значение), а только потом будет вычислен правый операнд x++ (t2 в моём коде). Затем будет вычислен сам оператор +=.
    24 ноября 2016 г. 13:42