Navigation überspringen

Computersprache

Wer programmiert, "sagt" dem Computer, was er zu tun hat. Dabei spricht der Computer offensichtlich eine andere Sprache, als wir Menschen und "tickt" auch etwas anders als wir. Um gut mit dem Computer kommunizieren zu können (und das meint guten Code schreiben zu können) ist es wichtig zu verstehen, welche Sprache Maschinen sprechen, wie mein Code in die Maschinensprache übersetzt wird und worauf ich achten muss, damit mein Code verstanden wird.

Maschinensprache

Das Kernstück eines Computers ist der Hauptprozessor oder auch CPU (Central Processing Unit). Diese wichtigsten Komponenten der CPU sind das Rechenwerk und das Steuerwerk und haben unterschiedliche Aufgaben:

  • Das Rechenwerk kann arithmetische und logische Operationen im Binärsystem mit 0 und 1 durchfühen.
  • Das Steuerwerk  hingegen arbeitet verschiedene Befehle aus dem Befehlsregister ab und kümmert sich um den Datentransfer.

Die Maschinenbefehle, die in der CPU verarbeitet werden, sind für den Menschen praktisch unlesbar, da diese aus 0 und 1 bestehen, die Sprache der Maschinen. Hier ein Beispiel für einen 16-Bit Maschinenbefehl:

0000000011000010

Durch die Zuordnung von symbolische Namen (sog. Assembler-Befehle, z.B. ADD für die Addition zweier Werte) für Maschinenbefehle entstand eine erste abstrakte „Programmiersprache“. In dieser wurde ganz früher auch programmiert (für komplexe Programme ist das aber absolut unbrauchbar). Mit der Zeit wurden noch höhere Abstraktionen dieser Maschinensprache entwickelt: die höheren Programmiersprachen. Davon gibt es heutzutage sehr viele. Eine davon ist Java, welche wir in diesem Modul lernen. Da jede Plattform (Windows, Linux, Mac, ...) allerdings verschiedene Maschinenbefehle verwendet, müssen Programme in höheren Programmiersprachen erst für die jeweilige Plattofrm "übersetzt" werden. Diese Übersetzung übernehmen Compiler oder Interpreter.

Übersetzung des Programmcodes


Compiler & Interpreter


Der Compiler erzeugt vor dem Ausführen des Programms Plattform-abhängigen Maschinencode, der nur auf bestimmten Rechnerarchitekturen/Betriebssystemen ausgeführt werden kann. Das bedeutet, dass für jede Plattform ein eigenes Programm übersetzt werden muss. Beispiel:

Programmcode Compiler Ausgabe im Programm
print("Hello World!"); 0010100011101110 Hello World!


Der Interpreter hingegen interpretiert zur Ausführungszeit, sprich während das Programm läuft, den Maschinencode und übersetzt ihn "live". Diese Programme sind plattformunabhängig, aber der Sourcecode bleibt unübersetzt und sichtbar, was in vielen Anwendungen nicht erwünscht ist. Ob ein Compiler oder ein Intepreter verwendet wird, hängt von der jeweiligen Programmiersprache ab.

Wie ist das bei Java?

Java geht hier einen kleinen "Kompromiss" ein:

  • Der Quellecode wird übersetzt in Bytecode, der plattformunabhängig verwendet werden kann.
  • Bytecode wird von einer "virtuellen Maschine" ausgeführt (interpretiert).
    • Virtuelle Maschinen (kurz VM) sind virtuelle Computer, die dieselben Funktionen wie physische Rechner bieten. Genau wie diese führen virtuelle Maschinen Anwendungen und ein Betriebssystem aus. Bei virtuellen Maschinen handelt es sich jedoch um Computerdateien, die auf einem physischen Computer ausgeführt werden. Oder einfach gesagt: Virutelle Maschinen sind sowas wie ein Computer in einem Computer.
    • Es gibt viele verschiedene VMs - je nach Einsatzgebiet. Für Java-Anwendungen heißt die VM "Java Virtual Machine" (kurz JVM).
  • Der Bytecode sorgt dafür, dass das Programm nicht mehr (Menschen-)lesbar ist, aber für die JVM. Die JVM übersetzt den Bytecode dann in die speziellem Maschinenbefehle der CPU.
    • Da jedes Betriebssystem andere Maschinenbefehle nutzt, gibt es unterschiedliche JVMs je nachdem, ob man Window, Linux oder MacOS nutzt.

Syntax

Damit Compiler oder Interpreter in der Lage sind die Programmiersprache richtig zu übersetzen, ist es wichtig sich an gewisse Regeln und Vorgaben bei der Programmierung zu halten. Diese bezeichnet man auch als Syntax. Jede Programmiersprache hat ihre eigene Syntax. Hier findet ihr eine Übersicht über die Syntaxregeln von Java im Abschnitt Syntax & Konventionen.

Wichtig: Die Maschine denkt nicht mit! Wenn die Maschine etwas nicht versteht, dann kann sie den jeweiligen Befehl bzw. auch das gesamte Programm nicht ausführen. Aus diesem Grund muss Code immer korrekt und eindeutig sein!

Speicher und Register

Gerade haben wir gesagt, dass eine Maschine nicht mitdenkt. Sie führt blind einen Befehl nach dem anderen aus. Wir, als ProgrammiererInnen, geben der Maschine die Befehle und die Maschine führt die Befehle einfach "blind" aus.

Beispiel: Abakus

Stell dir einen Abakus vor, mit 4 Reihen. Diese nennen wir Register. Sie sind unsere "Zahlenspeicher". Stell dir dabei den Abakus als Akteur vor, der Befehle blind ausführt.

Register 1
Register 2
Register 3
Register 4
Abakus


Programmieren bedeutet in diesem Bespiel Anweisungen zu schreiben, die vom Akteur verstanden und ausgeführt werden.

Beispiel 1: Addiere 2 Zahlen

Wie würdest du "dem Akteur" diese Aufgabe in wenigen (3-4) einfachen Anweisungen beschreiben? Versuche es zunächst selber und schau dir dann die bzw. eine Lösung an.

Anweisungen

  1. Nimm die erste Zahl und "speichere" sie in Register 1 (also auf der obstersten Stange)
  2. Nimm die zweite Zahl und "speichere" in Register 2 (2. Stange von oben)
  3. Addiere beide Zahlen und "speichere" das Ergebnis ins (Ergebnis-) Register 3 (die 3. Stange)

Wir sehen: Wir müssen nicht jede mögliche Addition fest in des Programm reinprogrammieren. Unabhängig davon welche Zahlen in Register 1 und 2 stehen, führt der Akteur den Befehl aus und liefert das Ergebnis.


Beispiel 2: Berechne den Mittelwert einer Liste positiver Zahlen und gebe die größte Zahl aus

Liste: 13, 2, 456, 23, 53, 34, 91, 12

Anweisungen an den Aktuer

Bei dieser Aufgabe wollen wir erstmal so anfangen, wie wir es einem Menschen beschreiben würden - also im Fließtext - und dann Schritt für Schritt näher an das herankommen, wie ein Computer es benötigt.

Version 1: Wie würdest du die Aufgabe einem Akteur in einfachem Fließtext beschreiben? Versuche es zunächst selber und schau dir dann die bzw. eine Lösung an.

Version 1

Das Register 1 soll verwendet werden, um die laufende Summe der Zahlen zu speichern, Register 2 soll die Anzahl der bearbeiteten Zahlen speichern und Register 3 speichert die größte der bearbeiteten Zahlen. Für jede Zahl aus der Liste ist folgendes zu tun: addiere sie zu Register 1; addiere den Wert 1 zu Register 2; wenn die aktuelle Zahl größer ist als die Zahl in Register 3, speichere sie in Register 3. Wenn alle Zahlen gelesen sind, dividiere die Zahl in Register 1 durch die Zahl in Register 2, um den Mittelwert zu errechnen, und lese die Zahl aus Register 3 als größte Zahl aus.

Version 2:

So ein Fließtext ist allerings sehr unübersichtlich. Wenn wir ihn etwas strukturieren, ist er einfacher zu lesen. Wie kansst du diesen (oder deinen) Fließtext strukturieren (z.B. durch Absätze oder Eindrückungen), damit er übersichtlicher ist? Versuche es zunächst selber und schau dir dann die bzw. eine Lösung an.

Version 2

Register 1 soll verwendet werden, um die laufende Summe der Zahlen zu speichern,
Register 2 soll die Anzahl der bearbeiteten Zahlen speichern
Register 3 speichert die größte der bearbeiteten Zahlen.

Für jede Zahl aus der Liste ist folgendes zu tun:
         addiere sie zu Register 1;
         addiere den Wert 1 zu Register 2;
         wenn die aktuelle Zahl größer ist als die Zahl in Register 3,
                  speichere sie in Register 3.

Wenn alle Zahlen gelesen sind,
         dividiere die Zahl in Register 1 durch die Zahl in Register 2, um den Mittelwert zu errechnen
         lese die Zahl aus Register 3 als größte Zahl aus.


Nun sind die Anweisungen übersichtlicher. Aber ist jede Zeile eine echte Anweisung? Sind alle Anweisungen klar? Schau dir diese (oder deinen) Text an und übrlege:

  • Welche "Anweisungen" sind Kommentare/Beschreibungen, die der Akteur eigentlich gar nicht braucht? --> Färbe diese Stellen orange
  • Welche Anweisungen sind vage oder ungenau? --> Färbe diese Stellen blau.

Versuche es zunächst selber und schau dir dann die bzw. eine Lösung an.

Version 2 mit Hervorhebungen

Register 1 soll verwendet werden, um die laufende Summe der Zahlen zu speichern,
Register 2 soll die Anzahl der bearbeiteten Zahlen speichern
Register 3 speichert die größte der bearbeiteten Zahlen.

Für jede Zahl aus der Liste ist folgendes zu tun:
         addiere sie zu Register 1;
         addiere den Wert 1 zu Register 2;
         wenn die aktuelle Zahl größer ist als die Zahl in Register 3,
                  speichere sie in Register 3.

Wenn alle Zahlen gelesen sind,
         dividiere die Zahl in Register 1 durch die Zahl in Register 2, um den Mittelwert zu errechnen
         lese die Zahl aus Register 3 als größte Zahl aus.

Anmerkungen:

  • Die orange markierten Zeilen sind Kommentare und für die Ausführung nicht wichtig. Die Maschine braucht sie nicht.
  • Die blau markierten Stellen sind vage oder ungenau. Der Bezug auf die Liste in Zeile 4 setzt voraus, dass der Akteur Wissen über die Liste hat (Anfang, Ende, Beschaffenheit, ...). In den folgenden Zeilen ist die Bezeichnugn von "die Zahl" nicht eindeutig.

Außerdem wurden die Register nicht "initialisiert", sprich es wurde kein Start-Wert eingetragen. Bei der ersten Durchführung würde dann in Zeile 6 die erste Zahl in der Liste mit Register 1 addiert werden - aber womit? In Register 1 befindet sich nichts. Hier muss der Startwert 0 eingetragen werden.

Version 3:

Nun "Bereinigen" wir den Text. Die orangenen Stellen können weg und was blau ist muss eindeutig formuliert werden. Außerdem muss den Registern ein Start-Wert zugewiesen werden und es muss klar sein, was passiert, wenn der Akteur am Ende der Liste angekommen ist.

Versuche in diesem letzten Schritt diesen (oder deinen) Text so genau wie möglich zu formulieren, so dass zu jeder Zeit unmissverständlich klar ist, was zu tun ist. Versuche es zunächst selber und schau dir dann die bzw. eine Lösung an.

Version 3

Speichere 0 in die Register 1, 2 und 3.

Lies die nächste Zahl aus der Liste.
Falls keine ungelesene Zahl mehr vorhanden ist,
         sind wir fertig.

Andernfalls,
         speichere die Zahl in Register 4.
         Addiere Register 4 zu Register 1.
         Addiere den Wert 1 zu Register 2.
         Wenn Register 4 größer ist als Register 3,
                  speichere Register 4 in Register 3.

Wenn wir fertig sind,
         dividiere Register 1 durch Register 2 und drucke das Ergebnis.
         Drucke den Inhalt von Register 3.


Man könnte dieses Bespiel auch noch weiter verbessern und verfeinern, aber für den Moment reicht das erstmal. Anhand von diesem "maschinennahem" Beispiel können wir folgendes sehen:

  • Anweisungen beim Programmieren müssen präzise und eindeutig sein - der Computer interpretiert nicht.
  • Der Detailgrad eines Programms geht weit über den Detailgrad von Anweisungen im normalen Leben hinaus.
  • Strukturierte Anweisungen sind besser lesbar und somit verständlicher.

Zusammenfassung

  • Das Kernstück eines Rechners ist die CPU. Die CPU verarbeitet Befehle in form von Maschinenbefehlen (bestehend 0 und 1)
  • Compiler und Interpreter "übersetzen" Programmiersprachen in Maschinencode. Jede Plattformbenötigt ihre eigen Übersetzung.
  • Damit Compiler und Interpreter den Programmcode richtig übersetzen können, müssen Programmierer sich an Syntax-Vorgaben halten.
  • Computer (bzw. die CPU) speichert Daten in Registern. Diese können dann mit logischen oder arithmetischen Anweisungen verarbeitet werden.
  • Programmieranweisungen muss präzise und eindeutig sein und weisen einen hohen Detailgrad auf.

Made with eXeLearning (New Window)