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!


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.


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;

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.


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.


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.


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

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;”.


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.


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


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

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.


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

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

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!


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.


-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)


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.