Почему изменение приращения pre на post на итерационной части цикла 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++;
        }

насколько я понимаю, постинкремент сначала используется "как есть", а затем увеличивается. Предварительно инкремент сначала добавляется, а затем используется. Почему это не относится к телу цикла?

27 ответов


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

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

как вы можете видеть из чтения этого кода, не имеет значения, используете ли вы оператор post или pre decrement в третьем разделе цикла 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 на счетчике цикла, не имеет значения, потому что результат выражения 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++ имеют значение при использовании в сочетании с оператором присваивания, таким как int num = I++ и int num = ++i или другими выражениями. В приведенном выше цикле для цикла существует только условие приращения, поскольку оно не используется в сочетании с любым другим выражением, это не имеет никакого значения. В этом случае это будет означать только i = i + 1.


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

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

да, это должно быть то же самое.


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


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

if (i++ < 3) 

и

if (++i < 3)

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

и если вы сделали

i = ++i + i++;

тогда ты просто орехи.


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


С глава спецификации языка Java на for loops:

BasicForStatement:

    for ( ForInit ; Expression ; ForUpdate ) Statement

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

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


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


проверка выполняется до вычисления аргумента increment. Операция "инкремент"выполняется в конце цикла, даже если она объявлена в начале.


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

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

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

вы должны быть в состоянии выяснить, что он делает из этого.


потому что значение y рассчитывается в 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

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


если for loop использовал результат выражения 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, вы увидите там for-loops с post-increment.


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


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

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

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

префикс и постфикс разница будет заметна только при выполнении операции назначения с приращением / уменьшением.


различий нет, потому что каждая часть для "аргументов" - это отдельные операторы.

и что интересно, что компилятор может решить заменить простой пост-incrementations предварительно incrementations и это не будет ничего менять в коде.


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

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


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

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

вот краткое описание:

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

поэтому, если у вас есть класс В C++, который переопределяет операторы postfix и prefix (например,std::iterator), такая оптимизация крайне редко, сделанный.

в итоге:

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

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


о i++ (пост-приращение) и ++я (предварительно приращения) @мне: "в обоих случаях выражение оценивается, и результат используется для проверки состояния. В случае pre-increment выражение increment увеличивает переменную и возвращает результирующее значение. Для post-increment выражение increment также увеличивает переменную, но возвращает Предыдущее значение. В результате pre-increment сравнивается с инкрементным значением, тогда как post-increment сравнивает против исходного значения; в обоих случаях переменная была увеличена при проверке условия."- tdammers


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

операторы инкремента/декремента: i++ совпадает с i = i + 1 и имеет значение я в выражении. Аналогично, я -- это то же самое, что 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.