Im Folgenden werden wir den Maschinencode betrachten, den der Compiler aus Ihrem Programm erzeugt hat.
Erstellen Sie eine Datei gauss.c
mit folgendem Inhalt:
#include <stdio.h>
int main(int argc, char **argv) {
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
printf("sum = %d\n", sum);
return 0;
}
Kompilieren Sie das Programm wie folgt: gcc -o gauss gauss.c
Verwenden Sie nun den Befehl objdump
, um den Maschinencode der kompilierten Datei in lesbarer Form anzuzeigen: objdump -d -M intel gauss | less
Sie können die Ansicht von less
mit der Taste q
beenden. Die Repräsentation der Ausgabe von objdump
ist:
<address>: instruction_bytes instruction_mnemonic
Die lesbare Repräsentation des Programms findet sich in instruction_mnemonic
.
Suchen Sie in der Ausgabe von objdump
(der sogenannten Disassembly) nach der Funktion main
. Können Sie die Schleife aus der Hochsprache im Assemblercode lokalisieren?
Die Funktion main
sieht wie folgt aus; die Schleife befindet sich zwischen den Adressen 0x670
und 0x67e
. Zu erkennen ist diese am Sprung zum Compare und dem bedingten Sprung zurück. Das Programm ist einfacher zu verstehen, wenn man sich klar macht, dass die Variable i
an der Adresse rbp-0x4
steht und die Variable sum
an der Adresse rbp-0x8
.
000000000000064a <main>:
// ...
659: c745f800000000 mov DWORD PTR [rbp-0x8],0x0 // sum = 0
660: c745fc00000000 mov DWORD PTR [rbp-0x4],0x0 // i = 0
667: c745fc01000000 mov DWORD PTR [rbp-0x4],0x1 // i = 1
66e: eb0a jmp 67a <main+0x30> // Sprung zu Vergleich
670: 8b45fc mov eax,DWORD PTR [rbp-0x4] // eax = i
673: 0145f8 add DWORD PTR [rbp-0x8],eax // sum = sum + eax
676: 8345fc01 add DWORD PTR [rbp-0x4],0x1 // i = i + 1
67a: 837dfc64 cmp DWORD PTR [rbp-0x4],0x64 // Vergleich i, 100
67e: 7ef0 jle 670 <main+0x26> // Sprung, wenn i<=100
// ...
69c: c3 ret
Kompilieren Sie Ihr Programm erneut unter der Verwendung der Optionen -O0
, -O1
oder -O2
. Wie verändert sich die Disassembly?
Bei höheren Optimierungsstufen (GCC unterstüzt die Stufen bis -O3
) fällt auf, dass die Schleife nicht mehr existert; sie wurde zur Compile-Zeit berechnet und im Programm durch eine Konstante ersetzt.
Als Vorgriff: Zudem werden nicht alle Variablen ständig auf dem Stack gespeichert und wieder geladen.