Co to jest x po „x = x ++”?

285

Co się dzieje (za zasłonami), gdy jest to wykonywane?

int x = 7;
x = x++;

To znaczy, kiedy zmienna jest zwiększana i przypisywana do siebie w jednej instrukcji? Skompilowałem i wykonałem to. xwynosi wciąż 7, nawet po całym oświadczeniu . W mojej książce jest napisane, że xjest zwiększane!

Michael
źródło
9
Spróbuj tego: int x = 7; x = ++x;oczywiście nadal jest to okropny kod, nie musisz zmieniać przypisania. int x = 7; x++;wystarczy.
stivlo
7
Jest to naprawdę zła praktyka, nie zwiększaj zmiennej w tej samej linii, w której jej używasz.
Yousf
5
Wolałbym używać x += 1, chyba że w pętlach. for(int x=0; x<7; x++)
Svish
2
@andyortlieb nie ma obiektu, tylko podstawowa wartość.
fortran

Odpowiedzi:

301

xzwiększa się. Ale przypisujesz starą wartość z xpowrotem do siebie.


x = x++;
  1. x++zwiększa xi zwraca starą wartość.
  2. x = przypisuje starą wartość z powrotem do siebie.

W końcu xzostaje przypisany z powrotem do wartości początkowej.

Tajemniczy
źródło
3
Co powiesz na temat x = ++ x;
Hisham Muneer
3
@HishamMuneer xjest najpierw zwiększany, zanim zostanie odczytany w takim przypadku, więc skończysz x + 1.
@HishamMuneer Jest za późno. Ale umieszczam to tutaj, ponieważ może być pomocne dla innych osób, które będą patrzeć w przyszłości. Najlepszym sposobem na zrozumienie tego problemu jest sprawdzenie kodu zestawu utworzonego dla x = x ++ i x = ++ x. Zobacz także odpowiedź Thinkingcap.
nantitv
Wiem, że to jest bardzo stare, ale mam pytanie. Czy powyższa kolejność operacji jest gwarantowana przez standard? Czy to możliwe, że przypisanie jest wykonywane przed przyrostem?
Szmaragdowa broń
@EmeraldWeapon Jest zdefiniowany w Javie. Tylko w C / C ++ widzisz tego rodzaju shenanigany.
Mysticial
385
x = x++;

jest równa

int tmp = x;
x++;
x = tmp;
Książę John Wesley
źródło
46
Lol, yay dla definicji rekurencyjnych. prawdopodobnie powinieneś to zrobić x=x+1zamiastx++
user606723
8
@ user606723: Nie. Miałem na myśli całe oświadczenie x = x++, a nie tylko przyrost postu x++.
Prince John Wesley
20
Nie sądzę, żeby to wszystko było przydatne bez dalszych wyjaśnień. Na przykład nieprawdą x = ++x;jest to, że jest to równoważne int tmp = x; ++x; x = tmp;, więc jaką logiką możemy wywnioskować, że twoja odpowiedź jest poprawna (która to jest)?
kvb 27.10.11
4
jeszcze bardziej jasne jest to w asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker
3
@forker: Myślę, że byłoby bardziej zrozumiałe, gdybyś użył instrukcji montażu, które dotyczą procesora, którego używa Michael;)
Carl
258

Twierdzenie:

x = x++;

jest równa:

tmp = x;   // ... this is capturing the value of "x++"
x = x + 1; // ... this is the effect of the increment operation in "x++" which
           //     happens after the value is captured.
x = tmp;   // ... this is the effect of assignment operation which is
           //     (unfortunately) clobbering the incremented value.

Krótko mówiąc, oświadczenie nie ma wpływu.

Kluczowe punkty:

  • Wartość wyrażenia inkrementacji / dekrementacji Postfix jest wartością operandu przed inkrementacją / dekrementacją. (W przypadku formularza Prefiks wartość jest wartością argumentu po operacji),

  • RHS wyrażenia przypisania jest całkowicie oceniane (w tym wszelkie przyrosty, zmniejszenia i / lub inne skutki uboczne) przed przypisaniem wartości do LHS.

Zauważ, że w przeciwieństwie do C i C ++, kolejność oceny wyrażenia w Javie jest całkowicie określona i nie ma miejsca na zmienne specyficzne dla platformy. Kompilatory mogą zmieniać kolejność operacji tylko wtedy, gdy nie zmienia to wyniku wykonania kodu z perspektywy bieżącego wątku. W takim przypadku kompilator byłby w stanie zoptymalizować całą instrukcję, ponieważ można udowodnić, że nie działa.


W przypadku, gdy nie jest to już oczywiste:

  • „x = x ++;” jest prawie na pewno błędem w jakimkolwiek programie.
  • OP (pierwotne pytanie!) Prawdopodobnie oznaczało „x ++;” zamiast „x = x ++;”.
  • Stwierdzenia, które łączą auto inc / decrement i przypisanie do tej samej zmiennej są trudne do zrozumienia i dlatego należy ich unikać niezależnie od ich poprawności . Po prostu nie ma potrzeby pisania takiego kodu.

Mamy nadzieję, że weryfikatory kodu, takie jak FindBugs i PMD, oznaczą ten kod jako podejrzany.

Stephen C.
źródło
7
Na marginesie, OP, prawdopodobnie chcesz po prostu powiedzieć x++zamiast x = x++.
Jon Newmuis,
3
Prawidłowo, ale może należy podkreślić, że przyrost następuje po ocenie ekspresji prawej ręki, ale przed przypisaniem do lewej strony, stąd pozorne „nadpisanie”
czeski
2
to wydaje się być jednym z tych licealnych twisterów programistycznych ... dobrze wyjaśnić swoje podstawy!
kumarharsh
1
@Alberto - Dobrze jest słyszeć, że nie traktujesz wypowiedzi „ekspertów” jako „prawdy ewangelii”. Jednak lepszym sposobem na potwierdzenie tego, co powiedziałem, byłoby skonsultowanie się z JLS. Twój test kompilacji / dekompilacji pokazuje tylko, że to, co powiedziałem, dotyczy jednego kompilatora Java. Inni mogli (hipotetycznie) zachowywać się inaczej ... poza tym, że JLS na to nie pozwala.
Stephen C
4
Tylko FYI: pierwotnie zostało to opublikowane na inne pytanie, które zostało zamknięte jako duplikat tego pytania, a teraz zostało połączone.
Shog9
33
int x = 7;
x = x++;

Niezdefiniowane zachowanie w C i dla Javy zobacz tę odpowiedź . To zależy od kompilatora, co się stanie.

użytkownik712092
źródło
4
Nie, to nie zależy od kompilatora zgodnie z cytowaną odpowiedzią - edytuj - na razie -1
Mr_and_Mrs_D 29.09.2013
@Mr_and_Mrs_D To zależy od czego?
Mac
2
Dla C_ jest to niezdefiniowane zachowanie. Mimo to powiedzenie, że zależy od kompilatora, jest mylące - oznacza to, że kompilator powinien określić to zachowanie. Cofam swój głos, ale zastanawiam się nad edycją twojej odpowiedzi - edytuj: oops nie mogę - musisz ją najpierw edytować: D
Mr_and_Mrs_D
16

Taka konstrukcja x = x++;wskazuje, że prawdopodobnie nie rozumiesz, co ++robi operator:

// original code
int x = 7;
x = x++;

Przepiszmy to, aby zrobić to samo, w oparciu o usunięcie ++operatora:

// behaves the same as the original code
int x = 7;
int tmp = x; // value of tmp here is 7
x = x + 1; // x temporarily equals 8 (this is the evaluation of ++)
x = tmp; // oops! we overwrote y with 7

Teraz przepiszmy to, aby zrobić (co myślę), co chciałeś:

// original code
int x = 7;
x++;

Subtelność polega na tym, że ++operator modyfikuje zmiennąx , w przeciwieństwie do wyrażenia takiego jak x + x, który oceniałby na wartość int, ale pozostawiał xsamą zmienną bez zmian. Rozważmy konstrukcję taką jak czcigodna forpętla:

for(int i = 0; i < 10; i++)
{
    System.out.println(i);
}

Zwróć uwagę i++na tam? To ten sam operator. Moglibyśmy przepisać tę forpętlę w ten sposób i zachowałby się tak samo:

for(int i = 0; i < 10; i = i + 1)
{
    System.out.println(i);
}

Odradzam również używanie ++operatora w większych wyrażeniach w większości przypadków. Z powodu subtelności, kiedy modyfikuje oryginalną zmienną w przyrostach przed i po ( ++xi x++odpowiednio), bardzo łatwo jest wprowadzić subtelne błędy, które są trudne do wyśledzenia.

FMM
źródło
13

Zgodnie z kodem bajtu uzyskanym z plików klas,

Oba przypisania zwiększają x, ale różnicą jest czas when the value is pushed onto the stack

W trybie Case1Push występuje (a następnie przypisywany) przed przyrostem (co oznacza, że ​​przyrost nie robi nic)

W Case2Przyrost występuje najpierw (co daje 8), a następnie wypychany na stos (a następnie przypisywany do x)

Przypadek 1:

int x=7;
x=x++;

Kod bajtowy:

0  bipush 7     //Push 7 onto  stack
2  istore_1 [x] //Pop  7 and store in x
3  iload_1  [x] //Push 7 onto stack
4  iinc 1 1 [x] //Increment x by 1 (x=8)
7  istore_1 [x] //Pop 7 and store in x
8  return       //x now has 7

Przypadek 2:

int x=7; 
x=++x;

Kod bajtowy

0  bipush 7     //Push 7 onto stack
2  istore_1 [x] //Pop 7 and store in x
3  iinc 1 1 [x] //Increment x by 1 (x=8)
6  iload_1  [x] //Push x onto stack
7  istore_1 [x] //Pop 8 and store in x
8  return       //x now has 8
  • Stos tutaj odnosi się do stosu operandu, lokalny: x indeks: 1 typ: int
evenprime
źródło
Czy możesz szczegółowo wyjaśnić swoją odpowiedź.
Nihar
Pls spójrz na odnośnik i komentarze
nawet
8

Jest zwiększany po „ x = x++;”. Byłoby to 8, gdybyś zrobił „ x = ++x;”.

FJ
źródło
4
Jeśli później zostanie zwiększony x = x++, powinien wynosić 8.
R. Martinho Fernandes
8

Operator Post Increment działa w następujący sposób:

  1. Przechowuj poprzednią wartość argumentu.
  2. Zwiększ wartość argumentu.
  3. Zwraca poprzednią wartość argumentu.

Więc oświadczenie

int x = 7;
x = x++; 

zostanie oceniony w następujący sposób:

  1. x jest inicjowany wartością 7
  2. operator przyrostu post przechowuje poprzednią wartość x, tj. 7, do zwrócenia.
  3. Zwiększa x, więc teraz x wynosi 8
  4. Zwraca poprzednią wartość x, tj. 7, i jest ona przypisywana z powrotem do x, więc x ponownie staje się 7

Tak więc x jest rzeczywiście zwiększony, ale ponieważ x ++ przypisuje wynik z powrotem do x, więc wartość x jest zastępowana do poprzedniej wartości.

GauravLuthra
źródło
Ale w msvc x wynosi 8. Tak w gcc i clang x to 7.
Summer Sun
7

Inkrementacja następuje po wywołaniu x, więc x nadal wynosi 7. ++ x będzie równe 8, gdy x zostanie wywołane

JonPM
źródło
7

Po ponownym przypisaniu wartości xnadal jest to 7. Spróbuj, x = ++xa dostaniesz 8 innych do zrobienia

x++; // don't re-assign, just increment
System.out.println(x); // prints 8
Vishal
źródło
6

ponieważ x ++ zwiększa wartość PO przypisaniu jej do zmiennej. i podczas wykonywania tego wiersza:

x++;

varialbe x nadal będzie miał oryginalną wartość (7), ale użyje x ponownie w innym wierszu, takim jak

System.out.println(x + "");

da ci 8.

jeśli chcesz użyć zwiększonej wartości x na wyciągu przypisania, użyj

++x;

To zwiększy x o 1, NASTĘPNIE przypisze tę wartość do zmiennej x.

[Edytuj] zamiast x = x ++, to tylko x ++; ten pierwszy przypisuje sobie oryginalną wartość x, więc w rzeczywistości nie robi nic w tym wierszu.

Józef
źródło
Ten, który mówi, że zwiększa się po przypisaniu, i ten, który mówi, że wydrukuje 8. Przyrasta przed przypisaniem i drukuje 7.
R. Martinho Fernandes
jeśli x ma pierwotnie 7, System.out.println (String.valueOf (x ++)); drukuje 7. jesteś pewien, że mówimy o tym samym języku programowania?
Józef
Tak, jestem. Ta ideone.com/kj2UU nie drukuje 8, jak twierdzi ta odpowiedź.
R. Martinho Fernandes,
tak, myliłem się. x = x ++ przypisze najpierw 7 do x przed zwiększeniem x. ponieważ x ++ (które samo w sobie jest przypisaniem) rozwiązuje się najpierw przed x = (cokolwiek), nastąpi wartość przypisana do x w x = (cokolwiek). przepraszam, nie widziałem tego.
Józef
1
W rzeczywistości przyrost jest pierwszą rzeczą, która się dzieje. ideone.com/xOIDU
R. Martinho Fernandes,
4

Co się stanie kiedy int x = 7; x = x++;?

ans -> x++oznacza najpierw użyj wartości x dla wyrażenia, a następnie zwiększ ją o 1.
Tak dzieje się w twoim przypadku. Wartość x na RHS jest kopiowana do zmiennej x na LHS, a następnie wartość xjest zwiększana o 1.

Podobnie ++x oznacza ->zwiększenie wartości x najpierw o jeden, a następnie użycie wyrażenia.
Więc w twoim przypadku x = ++x ; // where x = 7
otrzymasz wartość 8.

Dla większej przejrzystości spróbuj dowiedzieć się, ile instrukcji printf wykona następujący kod

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend
samprat
źródło
niepoprawne „Wartość x na RHS jest kopiowana do zmiennej x na LHS, a następnie wartość x jest zwiększana o 1” - to xdaje 8, ale jest to 7 - przyrost następuje między odczytem a przypisaniem
użytkownik85421
3

++xjest przyrostem ->x jest zwiększany przed użyciem
x++jest przyrostem ->x jest zwiększany po użyciu

int x = 7; -> x get 7 value <br>
x = x++; -> x get x value AND only then x is incremented
Roberto Martelloni
źródło
1

Oznacza to, że: x++nie jest równyx = x+1

ponieważ:

int x = 7; x = x++;
x is 7

int x = 7; x = x = x+1;
x is 8

a teraz wydaje się to trochę dziwne:

int x = 7; x = x+=1;
x is 8

bardzo zależy od kompilatora!

linuxeasy
źródło
2
kto powiedział, że to było na pierwszym miejscu?
fortran
1
Gdybym był tobą, natychmiast wyrzuciłbym te książki xD W każdym razie byłoby tak jak (x = x + 1, x-1)w C, gdzie dozwolone są wyrażenia oddzielone przecinkami.
fortran
3
@fortran: Cóż, w mojej dziesięcioletniej kopii „The Java Programming Language, Third Edition” na stronie 159 jest napisane: „„ Wyrażenie i ++ jest równoważne z i = i + 1, tyle że jest oceniane tylko raz ”. Kto powiedział Wydaje się, że po pierwsze - James Gosling. Ta część tej specyfikacji specyfikacji Java jest wyjątkowo niejasna i słabo sprecyzowana; przypuszczam, że późniejsze edycje wyczyściły język, aby wyraźniej wyrazić semantykę rzeczywistego operatora
Eric Lippert
2
@fortran: przez „z wyjątkiem tego, że wartość jest oceniana tylko raz” standard próbuje przekazać, że wyrażenie takie jak „M (). x ++” wywołuje M () tylko raz. Mniej niejasne i dokładniejsze sformułowanie podkreśliłoby, że istnieje różnica między oceną i jako zmiennej w celu ustalenia jej miejsca przechowywania - co rozumiemy przez określenie „ocenione tylko raz” tutaj - a czytaniem lub pisaniem w tym miejscu przechowywania - - którykolwiek z nich może być rozsądną, ale niepoprawną interpretacją „ocenionego”. Oczywiście miejsce przechowywania musi być zarówno odczytane, jak i zapisane!
Eric Lippert
1
„bardzo zależne od kompilatora” - wcale!
Stephen C
-1

x = x ++;

To jest operator post-increment. Należy to rozumieć jako „Użyj wartości operandu, a następnie zwiększ operand”.

Jeśli chcesz, aby miało miejsce odwrotność, tj. „Zwiększ operand, a następnie użyj wartości operandu”, musisz użyć operatora wstępnej inkrementacji, jak pokazano poniżej.

x = ++ x;

Ten operator najpierw zwiększa wartość x o 1, a następnie przypisuje wartość z powrotem do x.

deepak
źródło
-1

Myślę, że ten spór można rozwiązać bez wchodzenia w kod i myślenia.

Rozważ i ++ i ++ i jako funkcje, powiedzmy Func1 i Func2.

Teraz i = 7;
Func1 (i ++) zwraca 7, Func2 (++ i) zwraca 8 (wszyscy to wiedzą). Wewnętrznie obie funkcje zwiększają i do 8, ale zwracają różne wartości.

Więc i = i ++ wywołuje funkcję Func1. Wewnątrz funkcji zwiększa się do 8, ale po zakończeniu funkcja zwraca 7.

Ostatecznie 7 zostaje przydzielonych do i. (Więc w końcu i = 7)

Pranav Mahajan
źródło
2
Nie ma tu żadnej „kontrowersji”. Kod wyraźnie zachowuje się w określony sposób, a zachowanie jest zgodne z JLS. Każdy, kto uważa, że ​​zachowuje się inaczej, albo tego nie próbował, albo ma złudzenie. (To trochę tak, jakby powiedzieć, że 7 x 7 to 49, jest „kontrowersyjne”, gdy ktoś zapomina o swoich tabelach czasowych ...)
Stephen C
-2

Wynika to z faktu, że użyłeś operatora post-increment. W następującym wierszu kodu

x = x++;

Tak się składa, że ​​przypisujesz wartość x do x. przyrosty x ++ x po przypisaniu wartości x do x. Tak działają operatorzy działający po dodaniu. Działają po wykonaniu instrukcji. Więc w twoim kodzie x jest zwracane najpierw, a następnie zwiększane.

Jeśli tak

x = ++x;

Odpowiedź brzmiałaby 8, ponieważ użyłeś operatora wstępnego przyrostu. To zwiększa wartość najpierw przed zwróceniem wartości x.

Zaraz
źródło