2015-05-30 9 views
10

Ich versuche, ein besseres Verständnis von, wie Compiler Code für undefined Ausdrücke z. für den folgenden Code:Assembler Debug von undefiniertem Ausdruck

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

Dies ist der von gcc erzeugt Assembler-Code 4.8.2 (Optimierung off -O0 ist, und ich habe meine eigenen Zeilennummern für Referenzzwecke eingesetzt):

(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. 

Die Ausführung dieses Codes führt dazu, dass der Wert i auf dem Wert bleibt (verifiziert mit einer printf()-Anweisung), dh i scheint nicht inkrementiert zu werden. Ich verstehe, dass verschiedene Compiler nicht definierte Ausdrücke auf unterschiedliche Weise auswerten/kompilieren werden und dies kann einfach die Art sein, wie gcc das tut, d.h. ich könnte ein anderes Ergebnis mit einem anderen Compiler bekommen.

Im Hinblick auf die Assembler-Code, wie ich sie verstehe:

Ignorieren line - 1-2 up-Stack/Basiszeiger Einstellung usw. Linie 3/4 - ist, wie der Wert von zugeordnet ist zu i.

Kann jemand erklären, was in Zeile 5-6 passiert? Es sieht so aus, als ob i schließlich der Wert von (Zeile 7) neu zugewiesen wird, aber ist die Inkrementierung (erforderlich für die Post-Inkrement-Operation i++) einfach vom Compiler in dem Fall aufgegeben/übersprungen?

Antwort

8

Diese drei Zeilen enthalten Ihre Antwort:

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

Der Zuwachs Betrieb nicht übersprungen. lea ist das Inkrement, wobei der Wert von %rax übernommen und der inkrementierte Wert in %edx gespeichert wird. %edx wird gespeichert, aber dann von der nächsten Zeile überschrieben, die den ursprünglichen Wert von %eax verwendet.

Sie Schlüssel zum Verständnis dieser Code ist zu wissen, wie lea funktioniert. Es steht für load effective address, und obwohl es wie eine Zeigerdereferenz aussieht, benötigt es nur die Mathematik, um die endgültige Adresse von [was auch immer] zu erhalten, und behält dann die Adresse statt des Werts bei diese Adresse. Dies bedeutet, dass es für jeden mathematischen Ausdruck verwendet werden kann, der effizient mit Adressierungsmodi als Alternative zu mathematischen Opcodes ausgedrückt werden kann. Es wird häufig verwendet, um aus diesem Grund eine Multiplikation zu erhalten und zu einer einzelnen Anweisung hinzuzufügen. In diesem Fall wird es verwendet, um den Wert zu erhöhen und das Ergebnis in ein anderes Register in einer Anweisung zu verschieben, wobei inc stattdessen es direkt überschreiben würde.

5

Zeile 5-6, ist die i++. Die lea 0x1(%rax),%edx ist i + 1 und mov %edx,-0x4(%rbp) schreibt das zurück auf i. In Zeile 7 schreibt der mov %eax,-0x4(%rbp) den ursprünglichen Wert jedoch zurück in i. Der Code sieht so aus:

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

Verwandte: [Sequenzpunkt] (http://Stackoverflow.com/q/3575350/2564301) – usr2564301