Compilers are Blood Sugar Sex Magick!

„Und jetzt verstehe ich endlich, was für ausgefuchste Dinger diese Compiler sind! ich meine… wtf, wie unglaublich komplex und so…“

Sagte ich auf der Autofahrt von Berlin nach Hinterspandauland, nach der ersten richtigen C-Session. Und der Fahrer stimmte mir zu und gemeinsam fuhren wir auf dem magischen Regenbogen in Richtung Nirvana, wo alle die hinkommen, die von der Erkenntis ihrer eigenen Bedeutungslosigkeit erleuchtet den Rest ihres Daseins über die hohe Kunst der Compileroptimierung nachdenken.

Natürlich ist das Quatsch. Ich würde nie an einem spirituellen Ort mit Andreas Bogk und Fefe rumhängen.

Der Pfad zu meiner Erleuchtung begann letzte Woche im Lenbachpark. Ich war ziemlich übel gelaunt, es war viel zu warm, ich hatte tausend andere Dinge für die Uni zu tun, ne Menge Herzschmerz und Erlehmann nervte über alle Kanäle die er nicht aus ethischen Gründen ablehnte. Irgendwie sowas wie meine privilegierte Defaultsituation.
Ich war etwas skeptisch bezüglich der bevorstehenden Session. Wir hatten nämlich eigentlich schon eine gehabt, die aber vor allem darauf hinauslief dass wir uns wiederum nur C-Programme in Assemblercode angeschaut haben. Da konnte ich ja noch nicht ahnen, was kompilierter Code mal für mich bedeuten würde. Stattdessen ärgerte ich mich, weil die Hoffnung auf leicht verdauliche assemblerlose Sessions ausblieb.

Plom und ich fanden uns ca. 45 Minuten nach der verabredeten Zeit. Wir waren zwar beide vor Ort allerdings zu faul um einmal um den gerade mal ein halbes Fußballfeld großen Park zu laufen. Stattdessen gingen wir immer wieder mal in den IRC-Chat, motzten rum, weil wir schon ewig warteten, und gingen wieder raus und verpassten uns.
Aber irgendwann schlossen wir uns zusammen und fanden ein sonniges Plätzchen wo wir erstmal versuchten herauszufinden, wie wir mit unseren alten Thinkpadlaptops eine Position finden könnten in der uns sie Sonne nicht am Programmieren hindern würde. Plom stellte fest, dass es am Besten funktioniert, wenn man den Laptopbildschirm auf den Boden legt.
Das Gute am Friedrichshain ist ja, dass merkwürdiges Verhalten entweder als ironische Performance interpretiert oder arroganterweise ignoriert wird. Denn wer Aufmerksamkeit will nimmt mir Aufmerksamkeit weg! Also saßen wir über unsere Laptops gebeugt und hackten das erste Programm ein.
Ein sehr simples aber schönes Temperatur-Konvertierungs-Programm, das Celsius in Fahrenheit umrechnet und in die Standardausgabe schreibt – also ins Terminal.
Das Programm beruht auf einer recht simplen Formel:

celsius = 5 * (fahrenheit-32)  / 9

Das ist soweit verständlich oder? Die dargestellen Rechenoperationen, also Division, Subtraktion und Multiplikation können so als Befehle erkannt werden. So wie es auch jedeR in der Schule gelernt hat. Im Gegensatz natürlich zu Assembler, wo Rechenoperationen durch Registerjonglage und mit speziellen Befehlen dargestellt wurden.

#include <stdio.h>

/* print fahrenheit celsius table */

main ()
{
        int fahr, celsius;
        int lower, upper, step;
        
        lower = 0;      /*lower limit*/
        upper = 300;    /*upper limit*/
        step = 20;      /*step size*/

        fahr = lower;
        while  (fahr <= upper) {
                celsius = 5 * (fahr-32)  / 9;
                printf("%d\t%d\n", fahr, celsius);
                fahr = fahr + step;
        }       
}



Für alle die noch nienienie was mit C zu tun hatten (und nachvollziehbarer Weise meinem Ratschlag, Assembler zu lernen nicht nachgekommen sind) sieht das Programm vermutlich genauso fremd, kryptisch und unästhetisch aus wie jeder andere Code. Aber das vergeht ganz schnell, wenn wir das Programm nach und nach analysieren.
Als erstes gilt es immer zu klären, was das Programm eigentlich für mich tun soll. Wie gesagt.
1. Rechne einen Fahrenheitwert um in Celsius
2. Fang mit dem F-Wert 300 an und ende bei 0°Fahrenheit
3. Mach das automatisch, wenn ich dich aufrufe, und zwar in einer Schleife, die mit Fahr=300 anfängt und dann in 10-er-Schritten runtergeht bis Fahr=0
4. Gib die Ergebnisse in einer Tabelle in meiner Konsole mit der ich den Programmgeist anrief aus
5. Bitte. Danke.

Aber das Programm wäre nicht ein Programm (sondern ein Mensch oder sowas), wenn ich nicht noch ein hübsches Framework um diese Anweisungen bauen müsste.
Die erste Zeile,

#include <stdio.h>

ist eine Anweisung an den Compiler. Sie sagt ihrer studentischen Hilfskraft:
„Hallo Herr Compiler, kannst du mal eben in die große unsichtbare Bibliothek gehen und dieses Buch da, „Standard-Input-Output“ in h-form für mich besorgen, durchlesen und für meine Recherchen verfügbar machen? Verlag und so weiß ich jetzt nicht, aber das kriegst du schon hin. Und wenn du schon dabei bist – wenig Milch, viel Zucker, ok? Danke!“
Nächste Zeile:

/* print fahrenheit celsius table */

Ist nichts weiter als ein Kommentar. In Assembler wurden die Kommentare mit eine Raute markiert, was ich persönlich schöner fand. Aber gegen jahrhunderte alte Konventionen kann man so viel machen wie gegen einen Berg.
Was als Kommentar in meinem Programm steht wird nicht ausgeführt und nicht vom Kernel berücksichtigt.
Nunja, eigentlich ist „Ist nichts weiter als ein Kommentar.“ eine etwas problematische Formulierung. Ich habe zwar schon darüber geschrieben, aber für die Neuzugänge wäre es durchaus wichtig nocheinmal zu betonen

KOMMENTIERE DEINE PROGRAMME, FOOL

Das hat was mit Solidarität und sozialer Verantwortung zu tun, jawohl.
Und du bist es deinem ICH in ein, zwei oder 30 Jahren schuldig. In den Kommentaren kannst du in einem selber verfassten Text dein Programm erklären und nachvollziehbar machen. Für Andere und für dich. Und da wir natürlich alle unsere Programme offenlegen und teilen, ist das dringend notwendig. Daher empfiehlt sich auch, die Kommentare auf Englisch zu verfassen. Außerdem, kleiner Pro-Tip am Rande: Auch wenn ein Programm schon Kommentare hat, z.B. in einem Lehrbuch, würde ich immer nochmal selber die Kommentare mit eigenen Worten ergänzen oder abändern.

Nächster Punkt:

main ( )
{

Zur Erklärung: Ein C-Programm besteht immer aus Funktionen und Variablen, also Werten. In den Funktionen stecken Statements die eben sagen, was genau für Operationen durchgeführt werden sollen. Main ist so eine Funktion, die in jedem C-Programm auftauchen muss, denn dort beginnt die Ausführung des Programms. Hinter main sind leere Klammern, das heißt, dass keine Argumente vorhanden sind. Das ist aber erstmal nicht wichtig. In den Schnörkelklammerdingern da folgen die Statements.
Die erste Klammer zeigt also an, dass es jetzt losgeht:

        int fahr, celsius;
        int lower, upper, step;

Die ints bedeuten einfach, dass die Werte, die später für „fahr“, „celsius“ etc. eingesetzt werden, Integer sind. Also ganze Zahlen, kein Bullshit mit Kommata und Dezimal und so.

        lower = 0;      /*lower limit*/
        upper = 300;    /*upper limit*/    
        step = 20;      /*step size*/

 

Für die Rechnung die gleich unten anfängt werden hier die Werte festgesetzt.
Lower ist der der Wert bis zu dem Fahrenheit runtergeschraubt werden soll. Das heißt der niedrigste Fahrenheitwert der umgerechnet wird ist 0° Fahrenheit. Dasselbe gilt für upper, der höchste Fahrenheitwert der berechnet wird ist 300° Fahrenheit.
Step ist der Wert vom Intervall, also in welchen Schritten wird Fahrenheit runtergesetzt. Wenn 300°Fahrenheit der erste Wert ist, wird als nächstes 280° Fahrenheit berechnet usw. Das kann man dann natürlich beliebig ändern, auch in 1 oder 5 oder 10.

        fahr = lower;
        while  (fahr <= upper) {

 

Und jetzt: Glitzerglitzermagie! Wir fangen an mit Fahrenheit=lower, also bei 0° Fahrenheit. Die nächste Zeile bildet eine Schleife. Sie sagt, solange es wahr ist, dass Fahr kleiner oder genauso groß ist wie upper wird folgende Operation durchgeführt. Also solange der Wert von Fahrenheit den wir umrechnen kleiner oder gleich 300 ist. Wenn er drüber ist, wird die ganze Aktion abgebrochen.

                celsius = 5 * (fahr-32)  / 9;

Hier, die Formel, wie Celsius errechnet werden kann. Hatten wa schon. (Das dauert ja ewig hier : / )

                printf("%d\t%d\n", fahr, celsius);
            

Das sieht hässlicher aus als es ist. Printf ist eine Funktion, die nicht C-Spezifisch ist, und sie ist aus der Standard-Library.  In diesem Falle bewirkt sie eben, dass die Dinger in der Klammer in mein Terminal geschrieben werden.
Besagte Dinger sehen erstmal etwas kryptisch aus aber man kann sie aufteilen in Argumente. Die Zeichen in den Anführungszeichen bilden das erste Argument. Nach jedem Komma folgt ein weiteres Argument.

%d – d ist ein Platzhalter für das respektive zweite Argument, also fahr. „d“ weil es sich um einen Dezimalinteger handelt. Es könnte auch stattdessen ein %f dort stehen, für „floating point“ um solche Zahlen wie 13.8 darzustellen.
\t   – Backslash t heißt, dass ein tab zwischen den beiden „d“s stehen soll. Also wird ein bisschen Platz gelassen dazwischen.
%d – steht wiederum für einen Wert der geprintet wird, in diesem Fall das dritte Argument, also celsius.
\n – Backslash n heißt, dass eine neue Zeile begonnen wird

Und die Klammer machen wir wieder zu. Es folt:

    fahr = fahr + step;
        }       
}

Das heißt schlauerweise, dass jetzt fahr um 20 erhöhrt wird. Sonst würden wir ja ewig errechnen, wieviel  0°Fahrenheit in Celsius sind. Also ist nach dem ersten Durchgang der Schleife Fahrenheit bei 20°. Und so weiter, nach jedem Durchgang wird Fahrenheit wieder um 20 erhöht… bissssss Fahrenheit bei 300° angekommen ist. Denn, ihr erinnert euch, sobald  Fahrenheit mehr als 300° ist, ist die Bedingung der Schleife ( fahr <= 300) nicht mehr erfüllt und das Programm ist zuende.
Und jetzt noch die Klammern schließen, nochmal schließen…. und fertig!

Damit das Programm aber überhaupt seinen einprogrammierten Nutzen erfüllt, muss es ausgeführt werden.
Für blutige Anfänger: Dafür wird das Programm erst in einen Editor geschrieben. Das mache ich z.B. mit nano. Dafür tippe ich in mein Terminal

 nano tempcalc.c

Es wird eine Datei namens tempcalc.c erstellt und geöffnet und da schreibe ich das Programm rein. Dann sichere ich sie unter diesem Namen und schließe das Programm. In meinem Terminal gebe ich jetzt ein

cc tempcalc.c -o tempcalc

Mit dem Befehl „cc“ wird das Programm kompiliert. Mit -o gebe ich den Befehl, dass die neue Datei, die dabei entsteht tempcalc heißen soll. Also genauso nur ohne die .c -Endung

./tempcalc

. / ist der Befehl um Programme auszuführen. Das mach ich auch in meinem Terminal. Und HEUREKAOMFG

0       -17
20      -6
40      4
60      15
80      26
100     37
120     48
140     60
160     71
180     82
200     93
220     104
240     115
260     126
280     137
300     148

Mir ist halt irgendwie auch klar, dass ihr jetzt genausogut diese Seite aufrufen könnt, wenn ihr in die unangenehme Situation kommt, dass euch mal jemand fragt, wieviel 240° Fahrenheit in °Celsius sind. Aber darum geht es ja nicht und so. Wichtiger ist, dass der Programmcode – ich wage zu sagen, anders als bei Assembler, schon jetzt als Schablone für ganz andere Programme dienen kann. Ich kann die Formel auch umändern und die Begriffe „fahr“ und „celsius“ durch „euro“ und „yen“  ersetzen. Oderoderoder.
Ich kann nur empfehlen, das mal nachzuschreiben und dann etwas abzuändern. Dadurch verinnerliche ich zumindest immer am Besten die Funktionsweise.

Bleibt zu sagen: Die Strategie, mit etwas absurd schwerem anzufangen, damit C umso leichter erscheint ist wunderbar aufgegangen.
Wir waren am Ende der Session in eine verrauchte Sporkneipe umgezogen, wo wir misstrauisch beäugt wurden. Ich kann es ihnen nicht verübeln. Ein Nerd und ein Mödchen, die zum Trinken Orangensaft bestellen und auf ihre Laptops einhacken statt Dart zu spielen. Aber das störte uns nicht weiter. Wir waren berauscht von unserem gemeinsamen Erfolg! Wir vergaßen sogar, dass Erlehmann mittlerweile bereits vier Stunden zu spät war. (Am Telefon erklärte er uns noch später, er hätte sich nur kurz hingelegt und würde jetzt los) Stattdessen führte mich Plom noch ein wenig in das altehrwürdige Handwerk der unleserlichen C-Programmierung. Man kann nämlich den kompletten Code auch einfach in einer Zeile schreiben!
Wir schauten uns auch wieder einmal den in Assembler kompilierten Code an. Wo mir dann besagte Erleuchtung kam. Bzw. die trat ein paar Stunden später ein, zu diesem Zeitpunkt war ich zu keiner anstrengenderen Emotion als „binmüde“ mehr fähig.

Jedenfalls – auch wenn ich wirklich noch nicht viel von Compilern verstehe – habe ich doch eine Ahnung davon, was für eine wunderbare Welt der Komplexität dahinter steckt.
Ein Programm kann ja nicht eins zu eins übersetzt werden. Dann würde ähnlich sinnvolles Chop Suey wie bei Google Translate dabei entstehen, wenn eine Sprache Wort für Wort in eine andere übersetzt wird. Wenn ich einen deutschen Text ins Englische übersetze muss ich ja etliche Konventionen beachten und inhaltlich trotzdem versuchen dasselbe zu vermitteln. Das könnte ein Programm nicht ohne Weiteres. Aber ein Compiler kriegt das eben doch hin. Der Assemblercode hat Schleifen erkannt und in Assembler übersetzt und alle dafür notwendigen Herumfrickeleien mit den Registern „erdacht“.
Um präziser auszudrücken was mich daran so fasziniert fehlt mir noch das Vokabular. Aber genau für solche Dinge, um das zu erkennen, zu sehen was ein Compiler eigentlich leistet, dafür hat es sich schon gelohnt, Assembler ansatzweise gelernt zu haben. Und das nicht nur zu erkennen, sondern auch maßlos fasziniert zu sein!🙂

 

Getaggt mit
%d Bloggern gefällt das: