2015-05-30 12 views
10

Sto cercando di capire meglio come i compilatori producono codice per le espressioni indefinite ad es. per il seguente codice:Errore di assembler dell'espressione non definita

int main() 
{ 
    int i = 5; 
    i = i++; 
    return 0; 
} 

Questo è il codice assembler generato da gcc 4.8.2 (ottimizzazione è fuori -O0 e ho inserito i miei numeri di linea a fini di riferimento):

(gdb) disassemble main 
Dump of assembler code for function main: 
(1) 0x0000000000000000 <+0>: push %rbp 
(2) 0x0000000000000001 <+1>: mov %rsp,%rbp 
(3) 0x0000000000000004 <+4>: movl $0x5,-0x4(%rbp) 
(4) 0x000000000000000b <+11>: mov -0x4(%rbp),%eax 
(5) 0x000000000000000e <+14>: lea 0x1(%rax),%edx 
(6) 0x0000000000000011 <+17>: mov %edx,-0x4(%rbp) 
(7) 0x0000000000000014 <+20>: mov %eax,-0x4(%rbp) 
(8) 0x0000000000000017 <+23>: mov $0x0,%eax 
(9) 0x000000000000001c <+28>: pop %rbp 
(10) 0x000000000000001d <+29>: retq 
End of assembler dump. 

L'esecuzione di questo codice comporta il valore di i rimanendo sul valore di (verificato con una dichiarazione printf()), ovvero i non sembra essere mai stato incrementato. Capisco che diversi compilatori valuteranno/compileranno espressioni indefinite in modi differnet e questo potrebbe essere solo il modo in cui gcc lo fa, cioè potrei ottenere un risultato diverso con un compilatore diverso.

Per quanto riguarda il codice assembler, se ho capito:

Ignorando linea - 1-2 creazione di puntatori dello stack/base, ecc linea 3/4 - è come viene assegnato il valore di a i.

Qualcuno può spiegare cosa sta succedendo sulla linea 5-6? Sembra che i sarà infine riassegnato il valore di (linea 7), ma è l'operazione di incremento (necessario per le operazioni post incremento i++) semplicemente abbandonato/saltato dal compilatore nel caso?

risposta

8

Queste tre righe contengono la risposta:

lea 0x1(%rax),%edx 
mov %edx,-0x4(%rbp) 
mov %eax,-0x4(%rbp) 

L'operazione di incremento non è saltato. lea è l'incremento, prendendo il valore da %rax e memorizzando il valore incrementato in %edx. %edx viene memorizzato ma sovrascritto dalla riga successiva che utilizza il valore originale da %eax.

La chiave per capire questo codice è sapere come funziona lea. Rappresenta lo load effective address, quindi, anche se sembra una dereferenziazione puntatore, in realtà richiede solo la matematica per ottenere l'indirizzo finale di [qualunque cosa] e quindi mantiene l'indirizzo, invece del valore all'indirizzo. Ciò significa che può essere utilizzato per qualsiasi espressione matematica che può essere espressa in modo efficiente utilizzando le modalità di indirizzamento, in alternativa agli opcode matematici. È spesso usato come un modo per ottenere una moltiplicazione e aggiungere in una singola istruzione per questo motivo. In particolare, in questo caso viene utilizzato per incrementare il valore e spostare il risultato in un registro diverso in un'istruzione, laddove invece lo inc lo sovrascrive sul posto.

5

Riga 5-6, è il i++. Lo lea 0x1(%rax),%edx è i + 1 e mov %edx,-0x4(%rbp) scrive che torna a i. Tuttavia, riga 7, mov %eax,-0x4(%rbp) scrive il valore originale su i. Il codice è simile:

(4) eax = i 
(5) edx = i + 1 
(6) i = edx 
(7) i = eax 
+0

Correlati: [punto di sequenza] (http://stackoverflow.com/q/3575350/2564301) – usr2564301

Problemi correlati