Почему не меняется префикс до приращения записи в итерационной части цикла for?

Почему это

int x = 2; for (int y =2; y>0;y--){ System.out.println(x + " "+ y + " "); x++; } 

печатает то же самое, что и это?

  int x = 2; for (int y =2; y>0;--y){ System.out.println(x + " "+ y + " "); x++; } 

Насколько я понимаю, пост-инкремент сначала используется «как есть», затем увеличивается. Сначала добавляется предварительный инкремент, а затем используется. Почему это не относится к телу цикла for?

Цикл эквивалентен:

 int x = 2; { int y = 2; while (y > 0) { System.out.println(x + " "+ y + " "); x++; y--; // or --y; } } 

Как вы можете видеть из чтения этого кода, не имеет значения, используете ли вы оператор post или pre decment в третьем разделе цикла for.

В более общем плане, любой для цикла формы:

 for (ForInit ; Expression ; ForUpdate) forLoopBody(); 

точно эквивалентен циклу while:

 { ForInit; while (Expression) { forLoopBody(); ForUpdate; } } 

Цикл for более компактен и, таким образом, легче анализировать такую ​​общую идиому.

Чтобы визуализировать эти вещи, разверните цикл for для цикла while:

 for (int i = 0; i < 5; ++i) { do_stuff(i); } 

Расширяется до:

 int i = 0; while (i < 5) { do_stuff(i); ++i; } 

Независимо от того, выполняете ли вы post-increment или pre-increment на счетчике цикла, не имеет значения, потому что результат выражения инкремента (либо значение до, либо после инкремента) не используется в одном и том же выражении.

Нет никакой разницы в показателях производительности, если это вас беспокоит. Его можно использовать только неправильно (и, следовательно, чувствительно к ошибкам), когда вы используете его во время приращения.

Рассматривать:

 for (int i = 0; i < 3;) System.out.print(++i + ".."); //prints 1..2..3 for (int i = 0; i < 3;) System.out.print(i++ + ".."); //prints 0..1..2 

или

 for (int i = 0; i++ < 3;) System.out.print(i + ".."); //prints 1..2..3 for (int i = 0; ++i < 3;) System.out.print(i + ".."); //prints 1..2 

Интересная деталь, однако, заключается в том, что нормальная идиома заключается в использовании i++ в выражении приращения оператора for и что компилятор Java будет скомпилировать его, как если бы использовался ++i .

++ i и i ++ имеют значение при использовании в сочетании с оператором присваивания, таким как int num = i ++ и int num = ++ i или другие выражения. В вышеприведенном цикле FOR существует только дополнительное условие, поскольку оно не используется в сочетании с каким-либо другим выражением, оно не имеет никакого значения. В этом случае это будет означать только i = i + 1.

Этот цикл такой же, как while цикле while:

 int i = 0; while(i < 5) { // LOOP i++; // Or ++i } 

Так что да, это должно быть одинаково.

Потому что это утверждение просто по себе. Порядок приращения там не имеет значения.

Эти два случая эквивалентны, потому что значение i сравнивается после выполнения инструкции increment. Однако, если вы это сделали

 if (i++ < 3) 

против

 if (++i < 3) 

вам придется беспокоиться о порядке вещей.

И если вы это сделали

 i = ++i + i++; 

то вы просто орехи.

Потому что ничто в ваших примерах не использует значение, возвращаемое из пре-или пост-приращений. Попробуйте обернуть System.out.println() вокруг ++x и x++ чтобы увидеть разницу.

В главе «Спецификация языка Java» для циклов :

BasicForStatement:

  for ( ForInit ; Expression ; ForUpdate ) Statement 

… если присутствует часть ForUpdate, выражения вычисляются последовательно слева направо; их значения, если они есть, отбрасываются. … Если часть ForUpdate отсутствует, никаких действий не предпринимается.

(выделение – мое).

Результат тот же, потому что элемент «increment» в «for (initial; compare; increment)» не использует результат инструкции, он просто полагается на побочный эффект инструкции, которая в этом случае увеличивая «i», что в обоих случаях одинаково.

Проверка выполняется до вычисления аргумента increment. Операция «increment» выполняется в конце цикла, хотя она объявлена ​​в начале.

Попробуйте этот пример:

 int i = 6; System.out.println(i++); System.out.println(i); i = 10; System.out.println(++i); System.out.println(i); 

Вы должны уметь выработать то, что он делает.

Поскольку значение y вычисляется for оператора for а значение x вычисляется в собственной строке, но в System.out.println они ссылаются только на.

Если вы уменьшаетесь внутри System.out.println , вы получите другой результат.

 System.out.println(y--); System.out.println(--y); 

Здесь есть много хороших ответов, но в случае, если это поможет:

Подумайте о y– и -y как выражениях с побочными эффектами или о выражении, сопровождаемом выражением. y– так (подумайте об этих примерах как псевдо-сборке):

 decrement y return y 

и -y делает это:

 store y into t decrement y load t return t 

В примере вашего цикла вы выбрасываете возвращаемое значение в любом случае и полагаетесь только на побочный эффект (проверка цикла происходит ПОСЛЕ выполнения инструкции декремента, она не получает / не проверяет значение, возвращаемое декрементом).

Если цикл for использовал результат выражения i++ или ++i для чего-то, тогда это было бы правдой, но это не так, это просто потому, что его побочный эффект.

Вот почему вы также можете использовать метод void , а не только числовое выражение.

Приращение выполняется как независимый оператор. Так

y–;

а также

–y;

эквивалентны друг другу, и оба эквивалентны

y = y – 1;

Потому что это:

 int x = 2; for (int y =2; y>0; y--){ System.out.println(x + " "+ y + " "); x++; } 

Эффективно переводится компилятором на это:

 int x = 2; int y = 2 while (y > 0){ System.out.println(x + " "+ y + " "); x++; y--; } 

Как вы видите, использование y-- или --y не приводит к какой-либо разнице. Было бы важно, если бы вы написали свой цикл следующим образом:

 int x = 2; for (int y = 3; --y > 0;){ System.out.println(x + " "+ y + " "); x++; } 

Это приведет к тому же результату, что и ваши два варианта цикла, но переход от --y к y-- приведет к нарушению вашей программы.

Это вопрос вкуса. Они делают то же самое.

Если вы посмотрите на код java-classов, вы увидите там для-петель с пост-инкрементом.

в вашем случае это то же самое, никакой разницы вообще.

Ты прав. Разницу можно увидеть в этом случае:

 for(int i = 0; i < 5; ) { System.out.println("i is : " + ++i); } 

Да, он делает это последовательно. Intialisation, затем условие оценки, и если true, то выполняется тело, а затем увеличивается.

Различия в префиксе и постфикс будут заметны только тогда, когда вы выполняете операцию присваивания с помощью Increment / Decrement.

Нет никаких различий, потому что каждая часть аргументов «разделяет аргументы».

Интересно то, что компилятор может решить заменить простые пост-инкременции на предварительные инкрементации, и это не изменит вещь на код.

Они НЕ ведут себя одинаково. Конструкция с i ++ немного медленнее, чем с ++ i, потому что первая включает в себя возврат как старых, так и новых значений i . С другой стороны, последний возвращает только старое значение i .

Тогда, вероятно, компилятор делает небольшую магию и меняет любой изолированный i ++ в ++ i по соображениям производительности, но с точки зрения необработанного алгоритма они не являются строго одинаковыми.

В Stackoverflow есть много похожих сообщений:

  • Разница между i ++ и ++ i в цикле?
  • Есть ли разница в производительности между ++ i и i ++ в C #?
  • Есть ли разница в производительности между i ++ и ++ i в C?

Однако, похоже, ваш вопрос более общий, поскольку он не является специфическим для любого языка или компилятора. Большинство из вышеперечисленных вопросов касаются конкретного языка / компилятора.

Вот краткое изложение:

  • если мы говорим о C / C ++ / Java (возможно, C # тоже) и современный компилятор:
    • если i – целое число ( const int , int и т. д.):
      • то компилятор будет в основном заменить i++ на ++i , потому что они семантически идентичны и поэтому не меняют вывод. это можно проверить, проверив сгенерированный код / ​​байт-код (для Java я использую просмотрщик байт-кода jclasslib ).
    • еще:
  • еще:
    • все ставки отключены, поскольку компилятор не может гарантировать, что они семантически идентичны, поэтому он не пытается оптимизировать.

Поэтому, если у вас есть class на C ++, который переопределяет операторы постфикса и префикса (например, std::iterator ), эта оптимизация выполняется редко, если вообще когда-либо.

В итоге:

  • Значит, что ты говоришь, и говоришь, что ты имеешь в виду. Для инкрементной части циклов for вы почти всегда хотите версию префикса (то есть, ++i ).
  • Компилятор switchcheroo между ++i и i++ не всегда может быть выполнен, но он попытается сделать это для вас, если это возможно.

в цикле, первую инициализацию, затем проверку условий, затем выполнение, после этого приращения / уменьшения. поэтому pre / post increment / decment не влияет на программный код.

О i ++ (post-incrementation) vs. ++ i (pre-incrementation) @me: «В обоих случаях выражение оценивается, и результат используется для проверки на это условие. В пред-инкрементном случае приращение выражение увеличивает значение переменной и возвращает результирующее значение. Для пост-инкремента выражение increment также увеличивает эту переменную, но возвращает предыдущее значение. В результате предварительный прирост сравнивается с приращенным значением, тогда как пост-инкремент сравнивается с исходное значение, в обоих случаях переменная была увеличена при проверке состояния “. – tdammers

Существует довольно путаница между оператором post и pre increment, это легко понять из этой выдержки из «Алгоритмов, 4-е издание Роберта Седжуика и Кевина Уэйна»,

Операторы приращения / уменьшения: i ++ совпадает с i = i + 1 и имеет значение i в выражении. Аналогично, i– совпадает с i = i-1. Код ++ i и -i одинаковы, за исключением того, что значение выражения принимается после приращения / уменьшения, а не раньше.

например

 x = 0; post increment: x++; step 1: assign the old value (0) value of the x back to x.So, here is x = 0. step 2: after assigning the old value of the x, increase the value of x by 1. So, x = 1 now; when try to print somthing like: System.out.print(x++); the result is x : 0. Because only step one is executed which is assigning old value of the x back and then print it. But when, we do operation like this: i++; System.out.print(i); the result is x: 1. which is because of executing Step one at first statement and then step two at the second statement before printing the value. pre increment: ++x; step 1: increase the value of x by 1. So, x = 1 now; step 2: assign the increased value back to x. when try to print something like: System.out.print(++1) the result is x : 1. Because the value of the x is raised by 1 and then printed. So, both steps are performed before print x value. Similarly, executing ++i; system.out.print(i); Both steps are executed at statement one. At second statement, just the value of "i" is printed. 
Давайте будем гением компьютера.