2013-03-08 15 views
8

Nell'esempio seguente cosa succede realmente?Assegnazione di una variabile, cosa succede realmente, Java

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

L'uscita è 3, tuttavia volevo sapere cosa succede effettivamente sotto le copertine. Ad esempio, so che le parentesi hanno priorità più alta su + quindi, per prima cosa (a = 2) l'espressione dovrebbe diventare a = 2 + 2. In fase di esecuzione prima l'espressione tra parentesi deve essere eseguita e quindi a diventa 2. Sembra che il primo + a 10 venga "caricato" prima di (a = 2) e che quest'ultima espressione non sovrascriva il caricamento precedente. In altre parole, sono abbastanza confuso da ciò che accade esattamente dietro le quinte.

Se qualcuno lo sa, grazie mille in anticipo.

+1

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

+0

Idem come 'int a = 1; int tmpvar = (a = 2); a + = tmpvar; ' –

+0

Si traduce in' a = a + (a = 2); ', e gli operandi vengono valutati da sinistra a destra. –

risposta

2

Vedere l'esempio di riferimento 15.7.1-2 da http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1, che è quasi identico all'esempio fornito. In particolare:

Se l'operatore è un operatore composto assegnazione (§15.26.2), allora valutazione dell'operando sinistro comprende sia ricordando la variabile che l'operando sinistro denota e prendendo e salvare il valore di quella variabile da utilizzare nell'operazione binaria implicita.

A causa di questa precedenza, la mano sinistra del + = viene valutata per prima.

Potrebbe essere fonte di confusione per voi a causa delle parentesi, ma nota la sezione sulla valutazione parentesi: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3, ed in particolare:

Il linguaggio di programmazione Java rispetta l'ordine di valutazione indicata esplicitamente tra parentesi e implicitamente dall'operatore precedenza.

In questo caso, la precedenza implicita impostata dall'operatore + = indica che l'operando di sinistra verrà ricordato per le specifiche. Mentre è vero che gli operatori di assegnazione, tra cui "+ =" hanno la precedenza più bassa, la specifica per + = indica che l'operando di sinistra verrà ricordato per 15.26.2.

+1

in realtà la priorità di + = è la minima e dovrebbe essere valutata per ultimo – Rollerball

+0

Si prega di leggere il testo dell'esempio: Nel seguente programma, le due istruzioni di assegnazione recuperano e memorizzano il valore dell'operando di sinistra, che è 9, prima viene valutato l'operando di destra dell'operatore di addizione, a quel punto la variabile è impostata su 3. – Kirby

+0

Questo esempio è uguale al tuo, ma con numeri diversi. – Kirby

4

Dal JLS section §15.26.2 Compound Assignment Operators:

Un'espressione assegnazione composta del op modulo E1 = E2 è equivalente a = (T) ((E1) op (E2)) E1, dove T è il tipo di E1, ad eccezione del fatto che E1 viene valutato solo una volta.

Quindi per la vostra esempio abbiamo:

a = (a) + (a = 2) 

Con l'espressione valutata da sinistra a destra. Da qui l'output di 3

+0

La dichiarazione che hai citato non dice nulla sull'ordine di valutazione. – nhahtdh

+0

@ rich.okelly ok in modo sostanziale la sinistra (a) viene messa a sua volta tra parentesi implicitamente? – Rollerball

+0

@nhahtdh Si deduce - l'ordine di precedenza è garantito dall'ordinamento nell'istruzione con parentesi di dichiarazioni disambiguanti. –

2

Diamo uno sguardo al bytecode del seguente programma:

package A; 

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

Abbiamo solo bisogno di eseguire questo comando:

javap -c Test.class 

per ottenere il seguente bytecode:

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 
} 

Spiegazione:

Ci concentrarsi solo sulle due linee all'interno del principale metodo:

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

[int a = 1; comincia qui]

0: iconst_1 
  • Spinge int 1 nello stack .
------------- 
|   | 
------------- 
|   | 
------------- 
|  1  | 
------------- 
    STACK 

1: istore_1 
  • botti valore int dallo stack a variable 1 (variable 1 rappresenta a)
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  1  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[int a = 1; finiture qui]


[a += (a = 2); comincia qui]

2: iload_1 
  • carica un valore intero da locale variable 1 e spinge in pila.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

3: iconst_2 
  • Spinge int 2 nello stack.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

4: dup 
  • duplicare il valore in cima alla pila.
------------- 
|  2  |    variable 1 
-------------   -------------- 
|  2  |   |   | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

5: istore_1 
  • botti valore int dalla pila a variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|  2  |   |  2  | 
-------------   -------------- 
|  1  | 
------------- 
    STACK 

6: iadd 
  • aggiunge i primi due valori insieme.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  2  | 
-------------   -------------- 
|  3  | 
------------- 
    STACK 

7: istore_1 
  • botti valore int dalla pila a variable 1.
------------- 
|   |    variable 1 
-------------   -------------- 
|   |   |  3  | 
-------------   -------------- 
|   | 
------------- 
    STACK 

[a += (a = 2); finiture qui]


8: return 
  • il principale metodo restituisce.

Conclusione:

a = a + (a = 2) viene fatto attraverso diverse operazioni. 2: iload_1 viene eseguito come primo comando di a += (a = 2); che legge il primo operando dell'equazione a = a + (a = 2) e spinge nello stack.

Successivamente, vengono eseguiti 3: iconst_2 e 4: dup che sostanzialmente spingono int 2 due volte nello stack; uno per caricarlo su a e l'altro come secondo operando. Successivamente viene eseguito 5: istore_1 che sta caricando 2 in a (a = 2).

Infine, 6: iadd e 7: istore_1 vengono eseguiti dove 6: iadd aggiunge il primo operando e il secondo operando e spinge il risultato nello stack, e 7: istore_1 schiocca il risultato e carica nella a.


Per semplicità, diamo un rapido sguardo a questo codice:

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

e qui è il suo bytecode:

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 
} 

Come si può vedere, lo fa semplicemente il seguente:

  • Carichi int 1 in a.
  • Carichi int 3 in b.
  • Spinge a quindi b sullo stack.
  • Esegue l'aggiunta su di loro e spinge il risultato in pila.
  • Carica il risultato dallo stack e lo memorizza in a.
Problemi correlati