Dlaczego przechodzi to w nieskończoną pętlę?

493

Mam następujący kod:

public class Tests {
    public static void main(String[] args) throws Exception {
        int x = 0;
        while(x<3) {
            x = x++;
            System.out.println(x);
        }
    }
}

Wiemy, że powinien był napisać sprawiedliwie x++lub x=x+1, ale na x = x++nim powinien najpierw przypisać xsobie, a później zwiększyć. Dlaczego xkontynuuje się 0jako wartość?

--aktualizacja

Oto kod bajtowy:

public class Tests extends java.lang.Object{
public Tests();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge   22
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  iload_1
   16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   19:  goto    2
   22:  return

}

Przeczytam o instrukcjach, aby spróbować zrozumieć ...

Tom Brito
źródło
8
Podejrzewam, że to, co się dzieje: 1. załaduj x do rejestru (= 0); 2. przyrost x (x = 1); 3. zapisz wartość rejestru w x (x = 0). W C / C ++ byłoby to niezdefiniowane zachowanie, ponieważ nie ma formalnego punktu sekwencji definiującego kolejność 2 i 3. Mam nadzieję, że ktoś może zacytować coś równoważnego ze specyfikacji Java.
Rup
19
Próbowaliśmy tego w C ++, aby zobaczyć, co się stanie, i wypisuje 1,2,3 i kończy działanie. Nie spodziewałem się tego. Zakładam, że jest to zależne od kompilatora, ponieważ jest to niezdefiniowane zachowanie. Użyliśmy gnu g ++.
smuć
13
@saj x++jest przyrostowy; x=to przypisanie wyniku ; wynik o x++to oryginalna x(i nie jest efektem ubocznym przyrostu, ale to nie zmienia wynik), więc to może być interpretowane jakovar tmp = x; x++; x = tmp;
Marc Gravell
5
Teraz, gdy mam popularne pytanie, żałuję, że (powtarzane) odpowiedzi nie przestają przychodzić, nawet po wybraniu prawidłowej odpowiedzi. Mój ekran „Ostatnia aktywność” jest pełen tych samych odpowiedzi i nadchodzi więcej ...
Tom Brito
3
@Rob Vermeulen możesz przeczytać pełne pytanie przed komentarzem;;) To był kod napisany przez mojego studenta, a ja byłem ciekawy, dlaczego to zachowanie.
Tom Brito

Odpowiedzi:

354

Uwaga : pierwotnie opublikowałem kod C # w tej odpowiedzi w celach ilustracyjnych, ponieważ C # umożliwia przekazywanie intparametrów przez odniesienie do refsłowa kluczowego. Postanowiłem zaktualizować go o rzeczywisty legalny kod Java, używając pierwszej MutableIntklasy, którą znalazłem w Google, aby w przybliżeniu to, co refrobi w C #. Naprawdę nie wiem, czy to pomaga, czy boli odpowiedź. Powiem, że osobiście nie zrobiłem tyle rozwoju Java; więc o ile wiem, można zilustrować ten idiomatyczny sposób.


Być może, jeśli napiszemy metodę równoważną temu, co x++to uczyni, będzie to jaśniejsze.

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

Dobrze? Zwiększ przekazaną wartość i zwróć pierwotną wartość: taka jest definicja operatora poinkrementacji.

Zobaczmy teraz, jak wygląda to zachowanie w przykładowym kodzie:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)robi co? Przyrosty x, tak. A potem zwraca to, co x było przed przyrostem . Ta zwracana wartość jest następnie przypisywana do x.

Tak więc kolejność przypisanych wartości xto 0, następnie 1, a następnie 0.

To może być jeszcze jaśniejsze, jeśli przepiszemy powyższe:

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

Twoje przypuszczenie, że po zastąpieniu xpo lewej stronie powyższego zadania słowem y„widać, że najpierw zwiększa x, a później przypisuje je y”, wydaje mi się mylące. Nie xprzypisuje się tego y; jest to wartość poprzednio przypisanax . Naprawdę, wstrzykiwanie ynie różni się niczym od powyższego scenariusza; po prostu mamy:

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

Jest więc jasne: x = x++skutecznie nie zmienia wartości x. Zawsze powoduje, że x ma wartości x 0 , następnie x 0 + 1, a następnie x 0 ponownie.


Aktualizacja : Nawiasem mówiąc, aby nie wątpić, że xkiedykolwiek zostanie przypisane do 1 „pomiędzy” operacją przyrostową a przypisaniem w powyższym przykładzie, zrzuciłem szybkie demo, aby zilustrować, że ta wartość pośrednia rzeczywiście „istnieje”, chociaż będzie nigdy nie będzie „widoczny” w wątku wykonującym.

Demo wywołuje x = x++;pętlę, podczas gdy oddzielny wątek ciągle drukuje wartość xna konsoli.

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

Poniżej znajduje się fragment wyników powyższego programu. Zwróć uwagę na nieregularne występowanie zarówno 1, jak i 0.

Rozpoczynam wątek w tle ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1
Dan Tao
źródło
1
Nie musisz tworzyć klasy, która będzie przekazywana przez referencję w java (choć na pewno to zadziała). Możesz skorzystać z Integerklasy, która jest częścią standardowej biblioteki, a nawet ma tę zaletę, że jest automatycznie zapakowana do i z int niemal przezroczystego.
rmeador
3
@rmeador Integer jest niezmienny, więc nadal nie możesz zmienić jego wartości. AtomicInteger jest jednak zmienny.
ILMTitan
5
@ Dan: Nawiasem mówiąc, xw ostatnim przykładzie należy zadeklarować volatile, w przeciwnym razie jest to niezdefiniowane zachowanie, a widzenie 1s jest specyficzne dla implementacji.
axtavt
4
@burkestar: Nie sądzę, aby ten link był w tym przypadku całkiem odpowiedni, ponieważ jest to pytanie Java i (o ile się nie mylę) zachowanie w C ++ jest niezdefiniowane.
Dan Tao
5
@Tom Brito - w C nie jest zdefiniowane ... ++ można to zrobić przed lub po przypisaniu. Praktycznie rzecz biorąc, może istnieć kompilator, który robi to samo co Java, ale nie chciałbyś się na to stawiać.
detly
170

x = x++ działa w następujący sposób:

  • Najpierw ocenia wyrażenie x++. Ocena tego wyrażenia tworzy wartość wyrażenia (która jest wartością xprzed przyrostem) i przyrosty x.
  • Później przypisuje wartość wyrażenia x, zastępując wartość przyrostową.

Tak więc sekwencja zdarzeń wygląda następująco (jest to faktycznie zdekompilowany kod bajtowy wytworzony przez javap -cmoje komentarze):

   8: iload_1 // Zapamiętaj bieżącą wartość x na stosie
   9: iinc 1, 1 // Przyrost x (nie zmienia stosu)
   12: istore_1 // Zapisz zapamiętaną wartość ze stosu do x

Dla porównania x = ++x:

   8: iinc 1, 1 // Przyrost x
   11: iload_1 // Wciśnij wartość x na stos
   12: istore_1 // Pop wartość ze stosu do x
axtavt
źródło
jeśli wykonasz test, zobaczysz, że najpierw zwiększa, a później atrybuty. Więc nie powinien przypisywać zera.
Tom Brito
2
@Tom właśnie o to chodzi - ponieważ jest to pojedyncza sekwencja, robi rzeczy w nieoczywistej (i prawdopodobnie nieokreślonej) kolejności. Próbując to sprawdzić, dodajesz punkt sekwencji i uzyskujesz inne zachowanie.
Rup
Jeśli chodzi o dane wyjściowe kodu bajtowego: zwróć uwagę, że iincinkrementuje zmienną, nie zwiększa wartości stosu ani nie pozostawia wartości na stosie (w przeciwieństwie do prawie każdej innej operacji arytmetycznej). Możesz dodać kod wygenerowany przez ++xdla porównania.
Anon
3
@Rep Może nie być zdefiniowany w C lub C ++, ale w Javie jest dobrze zdefiniowany.
ILMTitan
104

Dzieje się tak, ponieważ wartość parametru xwcale się nie zwiększa.

x = x++;

jest równa

int temp = x;
x++;
x = temp;

Wyjaśnienie:

Spójrzmy na kod bajtu dla tej operacji. Rozważ przykładową klasę:

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

Teraz uruchamiając dezasembler klas otrzymujemy:

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

Teraz Java VM jest oparta na stosie, co oznacza, że ​​dla każdej operacji dane zostaną wypchnięte na stos, a ze stosu dane wyskoczą, aby wykonać operację. Istnieje również inna struktura danych, zazwyczaj tablica do przechowywania zmiennych lokalnych. Zmienne lokalne otrzymują identyfikatory, które są tylko indeksami tablicy.

Spójrzmy na mnemoniki w main()metodzie:

  • iconst_0: Stała wartość 0 jest przekazywana na stos.
  • istore_1: Górny element stosu jest wyskakujący i zapisywany w zmiennej lokalnej o indeksie, 1
    który jest x.
  • iload_1: Wartość w lokalizacji 1, której wartość x jest 0, jest wypychana do stosu.
  • iinc 1, 1: Wartość w miejscu pamięci 1jest zwiększana o 1. Tak się xteraz staje 1.
  • istore_1: Wartość na górze stosu jest zapisywana w lokalizacji pamięci 1. Jest to 0przypisane do x zastąpienia jego przyrostowej wartości.

Dlatego wartość xnie zmienia się, co powoduje powstanie nieskończonej pętli.

kodaddict
źródło
5
W rzeczywistości jest on zwiększany (to jest znaczenie ++), ale zmienna zostaje później nadpisana.
Progman
10
int temp = x; x = x + 1; x = temp;lepiej nie używać tautologii w twoim przykładzie.
Scott Chamberlain,
52
  1. Notacja prefiksu zwiększy zmienną PRZED obliczeniem wyrażenia.
  2. Notacja Postfiksa zwiększy się PO ocenie wyrażenia.

Jednak „ =” ma niższy priorytet operatora niż „ ++”.

Więc x=x++;powinien oceniać w następujący sposób

  1. x przygotowane do zadania (ocenione)
  2. x zwiększony
  3. Poprzednia wartość xprzypisana do x.
Jaydee
źródło
To najlepsza odpowiedź. Niektóre znaczniki pomogłyby mu wyróżnić się jeszcze bardziej.
Justin Force
1
To jest źle. Tu nie chodzi o pierwszeństwo. ++ma wyższy priorytet niż =w C i C ++, ale instrukcja jest niezdefiniowana w tych językach.
Matthew Flaschen
2
Oryginalne pytanie dotyczy Javy
Jaydee,
34

Żadna z odpowiedzi nie była na miejscu, więc oto:

Kiedy piszesz int x = x++, nie przypisujesz xsiebie do nowej wartości, przypisujesz, xże jest to wartość zwracana x++wyrażenia. Która okazuje się być oryginalną wartością x, jak wskazano w odpowiedzi Colina Cochrane'a .

Dla zabawy przetestuj następujący kod:

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

Wynik będzie

0
1

Zwracana wartość wyrażenia jest wartością początkową x, która wynosi zero. Ale później, czytając wartość x, otrzymujemy zaktualizowaną wartość, to jest jedną.

Robert Munteanu
źródło
Spróbuję zrozumieć linie kodu bajtowego, zobacz moją aktualizację, więc wszystko będzie jasne .. :)
Tom Brito
Korzystanie z println () bardzo mi pomogło w zrozumieniu tego.
ErikE
29

Zostało to już dobrze wyjaśnione przez innych. Podaję tylko linki do odpowiednich sekcji specyfikacji Java.

x = x ++ jest wyrażeniem. Java zastosuje się do kolejności oceny . Najpierw oceni wyrażenie x ++, które zwiększy x i ustawi wartość wynikową na poprzednią wartość x . Następnie przypisze wynik wyrażenia do zmiennej x. Na koniec x powraca do poprzedniej wartości.

plodoc
źródło
1
+1. To zdecydowanie najlepsza odpowiedź na pytanie „dlaczego?”
Matthew Flaschen
18

To oświadczenie:

x = x++;

ocenia tak:

  1. Pchać x na stos;
  2. Przyrost x;
  3. Wyskakuj xze stosu.

Zatem wartość pozostaje niezmieniona. Porównaj to z:

x = ++x;

który ocenia jako:

  1. Przyrost x;
  2. Wciśnijx na stos;
  3. Wyskakuj xze stosu.

Co chcesz to:

while (x < 3) {
  x++;
  System.out.println(x);
}
Cletus
źródło
13
Zdecydowanie poprawna implementacja, ale pytanie brzmi „dlaczego?”.
p.campbell
1
Oryginalny kod wykorzystywał przyrostową wartość x, a następnie przypisywał ją do x. x zostanie powiązane z x przed inkrementem, dlatego nigdy nie zmieni wartości.
wkl
5
@cletus Nie jestem zwycięzcą, ale twoja wstępna odpowiedź nie zawierała wyjaśnienia. Właśnie powiedział „do ++”.
Petar Minchev
4
@cletus: Nie głosowałem, ale twoją odpowiedzią był pierwotnie x++fragment kodu.
p.campbell
10
Wyjaśnienie jest również nieprawidłowe. Jeśli kod najpierw przypisał x do x, a następnie zwiększył x, działałoby dobrze. Po prostu zmień x++;rozwiązanie x=x; x++;i robisz to, co twierdzisz, że robi oryginalny kod.
Wooble,
10

Odpowiedź jest dość prosta. Ma to związek z kolejnością oceniania rzeczy. x++zwraca wartość, xa następnie zwiększa x.

W związku z tym wartość wyrażenia x++wynosi 0. Więc przypisujesz za x=0każdym razem w pętli. Na pewnox++ zwiększa tę wartość, ale dzieje się to przed przypisaniem.

Mike Jones
źródło
1
Wow, na tej stronie jest tyle szczegółów, kiedy odpowiedź jest krótka i prosta, tj. Ta.
Charles Goodwin
8

From http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

Operatory inkrementacji / dekrementacji można zastosować przed (prefiksem) lub po (postfiksie) operandu. Wynik kodu ++; i wynik ++; oba zakończą się zwiększeniem o jeden. Jedyną różnicą jest to, że wersja przedrostka (wynik ++) ocenia na wartość przyrostową, podczas gdy wersja po przedrostku (wynik ++) ocenia na wartość pierwotną . Jeśli wykonujesz prosty przyrost / spadek, tak naprawdę nie ma znaczenia, którą wersję wybierzesz. Ale jeśli użyjesz tego operatora jako części większego wyrażenia, ten, który wybierzesz, może mieć znaczącą różnicę.

Aby to zilustrować, spróbuj wykonać następujące czynności:

    int x = 0;
    int y = 0;
    y = x++;
    System.out.println(x);
    System.out.println(y);

Które wypisze 1 i 0.

Colin Cochrane
źródło
1
Problemem nie jest wynik oceny, ale kolejność sklepów.
Rup
2
Nie zgadzam się. Jeśli x = 0, to x ++ zwróci 0. Dlatego x = x ++ spowoduje x = 0.
Colin Cochrane
Rup ma rację w tej sprawie. W tym konkretnym przypadku chodzi o kolejność sklepów. y = x ++ to nie to samo co x = x ++; W drugim przypadku x ma przypisane 2 wartości w tym samym wyrażeniu. Lewej ręce x przypisuje się wynik oceny wyrażenia x ++, która wynosi 0. Prawa strona x jest zwiększana do 1. W jakiej kolejności występują te 2 przypisania, na czym polega problem. Z poprzednich postów jasno wynika, że ​​działa to tak: eval = x ++ => eval == 0: przyrost w prawo x => x == 1: lewo x = eval => x == 0
Michael Ekoka
7

Skutecznie uzyskujesz następujące zachowanie.

  1. pobierz wartość x (która wynosi 0) jako „wynik” prawej strony
  2. zwiększ wartość x (więc x wynosi teraz 1)
  3. przypisz wynik prawej strony (który został zapisany jako 0) do x (x jest teraz 0)

Chodzi o to, że operator post-inkrementacji (x ++) zwiększa tę zmienną PO zwróceniu jej wartości do wykorzystania w równaniu, w którym jest używana.

Edycja: Trochę dodaje z powodu komentarza. Rozważ to w następujący sposób.

x = 1;        // x == 1
x = x++ * 5;
              // First, the right hand side of the equation is evaluated.
  ==>  x = 1 * 5;    
              // x == 2 at this point, as it "gave" the equation its value of 1
              // and then gets incremented by 1 to 2.
  ==>  x = 5;
              // And then that RightHandSide value is assigned to 
              // the LeftHandSide variable, leaving x with the value of 5.
RHSeeger
źródło
OK, ale co określa kolejność kroków 2 i 3?
Rup
@Rup - Język to definiuje. Prawa strona równania jest oceniana jako pierwsza (w tym przypadku „x ++”), a wynik jest przypisywany do zmiennej po lewej stronie. Tak działa język. Jeśli chodzi o „x ++” „zwracanie” x równania, tak działa operator inkrementacji Postfiksa (zwróć wartość x, a następnie ją zwiększ). Gdyby to było „--x”, to byłoby (zwiększyć x, a następnie zwrócić wartość). Zwrot nie jest właściwym słowem, ale masz pomysł.
RHSeeger
7

Tak naprawdę nie potrzebujesz kodu maszynowego, aby zrozumieć, co się dzieje.

Zgodnie z definicjami:

  1. Operator przypisania ocenia wyrażenie po prawej stronie i przechowuje je w zmiennej tymczasowej.

    1.1 Bieżąca wartość x jest kopiowana do tej zmiennej tymczasowej

    1.2 x jest teraz zwiększane.

  2. Zmienna tymczasowa jest następnie kopiowana do lewej strony wyrażenia, czyli przypadkowo x! Dlatego właśnie stara wartość x jest ponownie kopiowana do siebie.

To jest dość proste.

houman001
źródło
5

Jest tak, ponieważ w tym przypadku nigdy się nie zwiększa. x++użyje jego wartości najpierw przed inkrementacją, jak w tym przypadku będzie to wyglądało tak:

x = 0;

Ale jeśli to zrobisz ++x;, wzrośnie.

mezzie
źródło
jeśli wykonasz test, zobaczysz, że najpierw zwiększa, a później atrybuty. Więc nie powinien przypisywać zera.
Tom Brito
1
@Tom: patrz moja odpowiedź - w teście pokazuję, że x ++ faktycznie zwraca starą wartość x. Tam się psuje.
Robert Munteanu,
„jeśli wykonasz test” - niektórzy ludzie sądzą, że test napisany w C mówi nam, co zrobi Java, a nawet nie powie nam, co zrobi C.
Jim Balter
3

Wartość pozostaje na 0, ponieważ wartość x++wynosi 0. W tym przypadku nie ma znaczenia, czy wartość parametru xjest zwiększona, czy nie, przypisanie x=0jest wykonywane. Spowoduje to zastąpienie tymczasowej wartości przyrostowej x(która wynosiła 1 przez „bardzo krótki czas”).

Progman
źródło
Ale x ++ jest operacją po. Więc x musiałoby być zwiększane po zakończeniu przypisania.
Sagar V
1
@Sagar V: tylko dla wyrażenia x++, nie dla całego zadaniax=x++;
Progman
Nie, myślę, że należy ją zwiększyć tylko po odczytaniu wartości x używanej w przydziale.
Rup
1

Działa to tak, jak oczekujesz tego drugiego. Jest to różnica między przedrostkiem i postfiksem.

int x = 0; 
while (x < 3)    x = (++x);
Patrick
źródło
1

Pomyśl o x ++ jako wywołaniu funkcji, która „zwraca” to, co X był wcześniej inkrementem (dlatego nazywa się to inkrementacją post-inkrement).

Zatem kolejność operacji jest następująca:
1: buforuj wartość x przed inkrementacją
2: inkrement x
3: zwróć buforowaną wartość (x przed jej inkrementacją)
4: zwracana wartość jest przypisywana do x

jhabbott
źródło
OK, ale co określa kolejność kroków 3 i 4?
Rup
„zwraca to, co X było przed przyrostem” jest niepoprawne, zobacz moją aktualizację
Tom Brito
W rzeczywistości kroki 3 i 4 nie są oddzielnymi operacjami - tak naprawdę nie jest to wywołanie funkcji, które zwraca wartość, po prostu pomaga myśleć o tym w ten sposób. Ilekroć masz przypisanie, prawa strona jest „oceniana”, a następnie wynik jest przypisywany do lewej strony, wynik oceny może być traktowany jako wartość zwrotna, ponieważ pomaga zrozumieć kolejność operacji, ale tak naprawdę nie jest .
jhabbott
Ups, prawda. Miałem na myśli kroki 2 i 4 - dlaczego zwracana wartość jest przechowywana powyżej wartości przyrostowej?
Rup
1
Jest to część definicji operacji przypisania, najpierw prawa strona jest całkowicie oceniana, a następnie wynik jest przypisywany do lewej strony.
jhabbott
1

Gdy ++ jest na rhs, wynik jest zwracany przed zwiększeniem liczby. Zmień na ++ x i byłoby dobrze. Java zoptymalizowałoby to, aby wykonać pojedynczą operację (przypisanie x do x) zamiast przyrostu.

Steven
źródło
1

O ile widzę, błąd występuje z powodu przypisania zastępującego wartość przyrostową wartością przed przyrostem, tzn. Cofa przyrost.

W szczególności wyrażenie „x ++” ma wartość „x” przed inkrementem, w przeciwieństwie do „++ x”, który ma wartość „x” po inkrementacji.

Jeśli jesteś zainteresowany badaniem kodu bajtowego, przyjrzymy się trzem pytaniom:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7:
iload_1 # Umieści wartość 2. zmiennej lokalnej na stosie 8: iinc 1,1 # zwiększy 2. zmienną lokalną o 1, zauważ, że pozostawia stos nietknięty!
9: istore_1 # Wyskoczy na górze stosu i zapisze wartość tego elementu w drugiej zmiennej lokalnej
(Możesz przeczytać efekty każdej instrukcji JVM tutaj )

Dlatego powyższy kod będzie się zapętlał w nieskończoność, podczas gdy wersja z ++ x nie. Kod bajtowy dla ++ x powinien wyglądać zupełnie inaczej, o ile pamiętam z kompilatora Java 1.3 napisanego nieco ponad rok temu, kod bajtowy powinien wyglądać mniej więcej tak:

iinc 1,1
iload_1
istore_1

Zatem zamiana dwóch pierwszych wierszy zmienia semantykę, tak że wartość pozostawiona na górze stosu po inkrementie (tj. „Wartość” wyrażenia) jest wartością po inkrementie.

micdah
źródło
1
    x++
=: (x = x + 1) - 1

Więc:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

Natomiast

   ++x
=: x = x + 1

Więc:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

Oczywiście wynik końcowy jest taki sam jak sam x++;lub ++x;na linii.

Paweł
źródło
0
 x = x++; (increment is overriden by = )

z powodu powyższego wyrażenia x nigdy nie osiąga 3;

Praveen Prasad
źródło
0

Zastanawiam się, czy jest coś w specyfikacji Java, która precyzyjnie określa zachowanie tego. (Oczywiście implikacją tego stwierdzenia jest to, że jestem zbyt leniwy, by to sprawdzić).

Uwaga z kodu bajtowego Toma kluczowe linie to 7, 8 i 11. Linia 7 ładuje x do stosu obliczeniowego. Przyrosty linii 8 x. Wiersz 11 przechowuje wartość ze stosu z powrotem do x. W normalnych przypadkach, gdy nie przypisujesz sobie wartości z powrotem, nie sądzę, aby istniał jakiś powód, dla którego nie można załadować, zapisać, a następnie zwiększyć. Otrzymasz ten sam wynik.

Załóżmy, że miałeś bardziej normalny przypadek, w którym napisałeś coś takiego: z = (x ++) + (y ++);

Czy to powiedział (pseudokod, aby pominąć dane techniczne)

load x
increment x
add y
increment y
store x+y to z

lub

load x
add y
store x+y to z
increment x
increment y

powinno być nieistotne. Wydaje mi się, że każde wdrożenie powinno być ważne.

Byłbym bardzo ostrożny przy pisaniu kodu, który zależy od tego zachowania. Wygląda mi na bardzo zależne od implementacji, między pęknięciami w specyfikacjach. Jedyne, co zrobiłoby różnicę to to, że zrobiłeś coś szalonego, na przykład tutaj, lub jeśli działały dwa wątki i były zależne od kolejności oceny w wyrażeniu.

Sójka
źródło
0

Myślę, że ponieważ w Javie ++ ma wyższy priorytet niż = (przypisanie) ... Czy tak? Spójrz na http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...

Ten sam sposób, jeśli piszesz x = x + 1 ... + ma wyższy priorytet niż = (przypisanie)

Cubob
źródło
To nie jest kwestia pierwszeństwa. ++ma wyższy priorytet niż =w C i C ++, ale instrukcja jest niezdefiniowana.
Matthew Flaschen
0

x++Wyrażenie to x. ++Część wpływu na wartość po ocenie , nie po oświadczeniu . więc x = x++jest skutecznie przetłumaczone na

int y = x; // evaluation
x = x + 1; // increment part
x = y; // assignment
tia
źródło
0

Przed zwiększeniem wartości o jeden wartość jest przypisywana do zmiennej.

kumara
źródło
0

Dzieje się tak, ponieważ jest zwiększany. Oznacza to, że zmienna jest zwiększana po ocenie wyrażenia.

int x = 9;
int y = x++;

x wynosi teraz 10, ale y wynosi 9, wartość x przed zwiększeniem.

Zobacz więcej w Definicja przyrostu postu .

BrunoBrito
źródło
1
Twój x/ yprzykład różni się od prawdziwego kodu, a różnica jest istotna. Twój link nawet nie wspomina o Javie. W przypadku dwóch języków, o których wspomina, stwierdzenie w pytaniu jest niezdefiniowane.
Matthew Flaschen
0

Sprawdź poniższy kod,

    int x=0;
    int temp=x++;
    System.out.println("temp = "+temp);
    x = temp;
    System.out.println("x = "+x);

wyjście będzie,

temp = 0
x = 0

post incrementoznacza zwiększenie wartości i zwrócenie wartości przed przyrostem . Właśnie dlatego wartość tempjest 0. Co jeśli temp = ii to jest w pętli (z wyjątkiem pierwszego wiersza kodu). tak jak w pytaniu !!!!

główny
źródło
-1

Operator przyrostu jest stosowany do tej samej zmiennej, do której przypisujesz. To prosi o kłopoty. Jestem pewien, że możesz zobaczyć wartość swojej zmiennej x podczas uruchamiania tego programu ... to powinno wyjaśnić, dlaczego pętla nigdy się nie kończy.

SIVAKUMAR.J
źródło