2013-03-08 10 views
8

W poniższym przykładzie, co się właściwie dzieje?Przypisywanie zmiennej, co tak naprawdę się dzieje, Java

int a = 1; 
a += (a = 2); 

Dane wyjściowe to 3, ale chciałem wiedzieć, co faktycznie dzieje się pod kołdrą. Na przykład wiem, że nawiasy mają wyższy priorytet niż +, tak więc pierwsze (a = 2) wyrażenie powinno stać się a = 2 + 2. W czasie wykonywania należy wykonać wyrażenie w nawiasach, a następnie a 2. Wydaje się, że pierwszy a po lewej stronie do + zostaje "załadowany" przed (a = 2), a to ostatnie wyrażenie nie wydaje się przesłonić poprzedniego ładowania. Innymi słowy jestem bardzo zdezorientowany tym, co dokładnie dzieje się za kulisami.

Jeśli ktoś wie, bardzo dziękuję z góry.

+1

pokrewne: http://stackoverflow.com/questions/11324850/why-swapping-integer-variable-by-xor-doesnt-work-in-a-single-line/11325458 – nhahtdh

+0

samo co 'int a = 1; int tmpvar = (a = 2); a + = tmpvar; ' –

+0

Przekłada się na' a = a + (a = 2); ', a operandy są oceniane od lewej do prawej. –

Odpowiedz

2

Zobacz przykład odniesienia 15.7.1-2 z http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1, który jest prawie identyczny z podanym przykładem. W szczególności:

Jeżeli operator jest operatorem związek przypisania (§15.26.2), a następnie oceny argumentu lewą pamiętając obejmuje zarówno zmiennej że lewy argument operacji oznacza i pobieranie i zapisywanie tej wartości zmiennej do użycia w domniemanej operacji binarnej.

Z powodu tego pierwszeństwa, lewa ręka + = jest oceniana jako pierwsza.

To może być mylące dla Ciebie, ponieważ z nawiasami, ale należy zwrócić uwagę na rozdział poświęcony ocenie nawiasie: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3, aw szczególności:

Język programowania Java przestrzega kolejności oceny wskazane wyraźnie w nawiasie i domyślnie przez operatora.

W tym przypadku domyślny priorytet ustawiony przez operator + = wskazuje, że lewy operand zostanie zapamiętany zgodnie ze specyfikacją. O ile prawdą jest, że operatory przypisania, w tym "+ =" mają najniższy priorytet, specyfikacja dla + = wskazuje, że lewy operand zostanie zapamiętany na 15.26.2.

+1

właściwie priorytet + = jest najmniejszy i powinien być oceniony jako ostatni – Rollerball

+0

Proszę przeczytać tekst przykładu: W poniższym programie dwie instrukcje przypisania pobierają i zapamiętują wartość lewego argumentu, który wynosi 9, przed wyliczeniem prawostronnego operandu operatora dodawania, w którym to momencie zmienna jest ustawiona na 3. – Kirby

+0

Ten przykład jest taki sam jak twój, ale z różnymi numerami. – Kirby

4

Z na trasę section §15.26.2 Compound Assignment Operators:

Wyrażenie zadanie związku o op postać E1 = E2 odpowiada E1 = (T) ((E1) OP (E2)), gdzie T oznacza typ E1, z tym wyjątkiem, że E1 jest oceniany tylko jeden raz.

Tak dla przykładu mamy:

a = (a) + (a = 2) 

Z wypowiedzi oceniano lewej do prawej. W związku z tym wyjście z 3

+0

Cytowane oświadczenie nie mówi nic o kolejności oceny. – nhahtdh

+0

@ rich.okelly ok, więc w zasadzie lewy (a) zostaje umieszczony w nawiasie w sposób pośredni? – Rollerball

+0

@nhahtdh To jest wnioskowane - kolejność pierwszeństwa jest gwarantowana przez uporządkowanie w instrukcji z nawiasami zdań ujednoznaczniających. –

2

Rzućmy okiem na kod bajtowy następującego programu:

package A; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     int a = 1; 
     a += (a = 2); 
    } 
} 

Po prostu trzeba uruchomić polecenie:

javap -c Test.class 

aby uzyskać następujące kodu bajtowego:

public class A.Test { 
    public A.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_1 
     1: istore_1 
     2: iload_1 
     3: iconst_2 
     4: dup 
     5: istore_1 
     6: iadd 
     7: istore_1 
     8: return 
} 

Objaśnienie:

Będziemy po prostu skupić się na dwóch liniach wewnątrz główne metody:

int a = 1; 
a += (a = 2); 

[int a = 1; zaczyna tutaj]

0: iconst_1 
  • nawiewy int 1 na stosie .
------------- 
|   | 
------------- 
|   | 
------------- 
|  1  | 
------------- 
    STACK 

1: istore_1 
  • tato Int wartość od stosu variable 1 (variable 1 oznacza a)
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  1  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[int a = 1; wykończenia tutaj]


[a += (a = 2); zaczyna tutaj]

2: iload_1 
  • wczytuje wartość int z lokalnego variable 1 i wypycha go na stosie.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

3: iconst_2 
  • nawiewy int 2 na stos.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

4: dup 
  • powielać wartość na górze stosu.
------------- 
|  2  |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

5: istore_1 
  • Pops int wartość ze stosu do variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |  2  | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

6: iadd 
  • Dodaje dwie górne wartości razem.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  2  | 
-------------   -------------- 
|  3  | 
------------- 
    STACK 

7: istore_1 
  • Pops int wartość ze stosu do variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  3  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[a += (a = 2); wykończenia tutaj]


8: return 
  • Główne powraca metody.

Wniosek:

a = a + (a = 2) odbywa się za pomocą kilku operacji. 2: iload_1 jest wykonywane jako pierwsze polecenie a += (a = 2);, które odczytuje pierwszy operand równania a = a + (a = 2) i przesuwa na stos.

Następnie wykonywane są 3: iconst_2 i 4: dup, które w zasadzie wypychają dwa razy int 2 na stos; jeden do załadowania go do a, a drugi jako drugi operand. Następnie wykonywany jest 5: istore_1, który ładuje 2 do a (a = 2).

Wreszcie 6: iadd i 7: istore_1 są wykonywane gdzie 6: iadd Dodaje pierwszy operand i drugiego argumentu i odkłada wynik na stos, a 7: istore_1 wyskakuje wynik i ładuje go do a.


Dla uproszczenia załóżmy rzucić okiem na ten kod:

int a = 1; 
int b = 3; 
a += b; 

i tu jest jego kod bajtowy:

public class A.Test { 
    public A.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_1 
     1: istore_1 
     2: iconst_3 
     3: istore_2 
     4: iload_1 
     5: iload_2 
     6: iadd 
     7: istore_1 
     8: return 
} 

Jak widać, po prostu wykonuje następujące czynności:

  • Obciążenia int 1 do a.
  • Ładuje int 3 w b.
  • Przesuwa a następnie b na stos.
  • Wykonuje dodawanie na nich i przesuwa wynik na stos.
  • Wyrzuca wynik ze stosu i zapisuje go w postaci a.
Powiązane problemy