Abschnitt 2.2 als pdf-File

Werbung
Computer und Physik
1 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Computer und Physik, version dated 2005-11-15 13:44:15
2.2 Programmiersprachen
2.2.1
Einführung
2.2.2
Maschinensprachen und Assembler
2.2.3
Höhere Sprachen
2.2.4
Unterschiede zwischen den Programmiersprachen
2.2.5
Softwareprojekte
Adults worry a lot these days. Especially, they worry about how to make other people learn more
about computers. They want to make us all "computer-literate". Literacy means both reading and
writing, but most books and courses about computers only tell you about writing programs.
(Marvin Minsky, 1984, Media Lab, Massachusetts Institute of Technology)
2.2.1 Einführung
Wie wir im ersten Teil gesehen haben, ist der Befehlsvorrat eines Prozessors ziemlich beschränkt,
im Einzelfall auf einige Dutzend oder vielleicht einige Hundert Operationen logischer oder
arithmetischer Art. Alle Programme müssen daher letztendlich in viele kleine Teilschritte zerlegt
und auf diesen grundlegenden Befehlsvorrat zurückgeführt werden. Der wichtigste Schritt bei
Erstellung eines Programmes ist daher die logische Analyse der Befehlsabfolge. Bei Ausführung
eines Programms müssen alle sich ergebenden Entscheidungsmöglichkeiten vorhergeplant und
entsprechende Verzweigungen vorgesehen werden. In den meisten Fällen wird das Programm
Daten von externen Speichern oder Eingabegeräten lesen und verarbeiten. Auch hier muss der
Programmierer auf alle sich ergebende Möglichkeiten Bedacht nehmen.
Einfachheit ist ein sich wandelnder Begriff. Versuche einmal, einem Schulanfänger die Addition
von Dezimalzahlen "einfach" zu erklären; es wird nur möglich sein, wenn du die Bedeutung von
ganzen Zahlen erklärst, dann die Addition ganzer Zahlen, dann die Dezimalzahlen und schließlich
deren Addition. Das Kind wird im Normalfall weit überfordert sein. Es aber vermutlich verstehen,
dass 1+1=2 ist. "Lernen" ist solch ein Prozess. Als Kleinkind lernen wir einfache Zusammenhänge
des täglichen Lebens: wenn ich ins Feuer greife, tut es weh; wenn ich von zwei Rippen
Schokolade eine esse, so bleibt mir nur mehr eine übrig. Später lernen wir kompliziertere
Zusammenhänge zu verstehen, indem wir sie gedankenschnell auf einfache zurückführen: wenn
ich das Bügeleisen nicht ausschalte, so habe ich morgen vielleicht kein Haus mehr; wenn ich in ein
teures Hotel ziehe, so kann ich nur zwei Wochen Urlaub machen. Wir könnten unsere Aussagen in
viele kleine Teilschritte zerlegen, aber wir finden so eine Zerlegung überflüssig und umständlich.
Etwas, was wir kaum unserem Kind klar machen könnten, kann uns "einfach" erscheinen.
15.11.2005 14:01
Computer und Physik
2 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Zerlegung in kleinste Teile, die Maschinenbefehle eines speziellen Computers, ist heute den
Systemprogrammierern der Produktionsfirma und anderen Spezialisten vorbehalten. Wir wollen in
größeren Schritten denken, Manipulationen mit Dezimalzahlen sind für uns eine
Selbstverständlichkeit, die wir nicht in viele kleine Einzelschritte zerlegen wollen. Unsere Sprache
soll solch eine Zerlegung nicht notwendig haben, wenn wir sagen "Addiere 3.14 und 5.27", so
wissen wir genau, was gemeint ist. Wir verwenden eine "höhere Sprache" als die
Maschinensprache es ist. Ein Wissenschaftler verwendet für seine Probleme wiederum
Spezialausdrücke, die über die Alltagssprache hinausgehen, und selbst innerhalb einer
Wissenschaftsdisziplin kann es weitere Spezialisierungen geben.
Diese Hierarchie von Sprachen gibt es auch bei den Programmiersprachen. Es gibt verschiedene
Niveaus von Sprachen:
Level 1: Maschinensprachen und Assemblersprachen
Bei Maschinensprachen entspricht ein Maschinenbefehl einem "Satz" dieser
Sprache. Da man sich nur ungern alle Zahlenverschlüsselungen von Befehlen merkt,
gibt es mnemotechnische Wortbefehle, die genau den verschiedenen
Maschinenbefehlen entsprechen. Dies führt zu den sogenannten
Assemblersprachen.
Ein Programm in einer Assemblersprache ist für den Programmierer wesentlich
einfacher "lesbar", er kann oft mehrere Befehle durch ein Wort abkürzen, aber es
gibt immer einen unmittelbar erkennbaren Zusammenhang zwischen seinen
Befehlszeilen und der Folge von Maschinenbefehlen.
Level 2: Höhere Sprachen
Viele Befehlsfolgen kommen in bestimmten Problemen immer wieder vor und es ist
zweckmäßig, sie durch zusammenfassende Befehle abzukürzen. Dies führt zu den
höheren Sprachen wie C, C++ oder FORTRAN.
Level 3: Anwendungsorientierte Sprachen.
Die Weiterentwicklung der höheren Sprachen und die Spezialisierung auf bestimmte
Problemkreise führt zu diesen Sprachen, zu denen man zum Beispiel Maple oder
MATHEMATICA zählen kann, Sprachen, mit deren Hilfe man algebraische
Rechnungen durchführen kann, bei denen man nicht nur Zahlen, sondern exakte
analytische Ergebnisse erhält.
Was ist eine Programmiersprache?
Was ist eine Sprache? Nun, von ihrer Struktur her betrachtet besteht sie auf einer endlichen
Menge von Symbolen und Regeln, die es erlauben, diese Symbole miteinander zu Sätzen zu
verknüpfen. Die Symbole können Ziffern, Buchstaben, Sonderzeichen, aber auch Kombination
davon wie etwa bestimmte Befehlsworte sein. Das Regelsystem bestimmt die Syntax der Sprache.
Die ersten Programmiersprachen wurden von Praktikern entworfen und folgten in ihrer Struktur
dem Gesetz der Stunde. Erst später machte man sich über die Logik der Sprache mehr
Gedanken. Sprachwissenschaftler wie Noam Chomsky haben über die wichtigen Eigenschaften
der Sprachen nachgedacht: Wie kann man aus einem einfachen Subjekt-Verb-Objekt Satz ein
syntaktisch richtiges Satzmonstrum bilden? Aus "Ich lese ein Buch" wird "Wenn ich Zeit und Lust
dazu habe, lese ich abends nach den Nachrichten ein Buch über Tauchen, da dies eines meiner
Hobbys ist".
Die meisten Umgangssprachen kann man in kein einfaches Strukturkonzept zwängen. Es gibt
zwar einige Regeln, aber zu fast allen Regeln gibt es Ausnahmebestimmungen und Fußnoten.
15.11.2005 14:01
Computer und Physik
3 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Sätze des täglichen Lebens können vieldeutig wie das Orakel von Delphi sein. Selbst wenn keine
Vieldeutigkeit beabsichtigt ist, kann zum Beispiel eine Aussage eines Juristen einen Physiker
durchaus verwirren - und umgekehrt. Solche Unklarheiten sollen bei Computersprachen
ausgeschlossen werden. Die Mitteilung eines Satzes soll eine eindeutige Information über eine
wohldefinierte Befehlsfolge sein. Dazu gehört als Voraussetzung eine wohldefinierte Syntax.
Bei Maschinensprachen ist diese Forderung einfach zu erfüllen, da ja einem Satz der Sprache
genau ein Maschinenbefehl entspricht. In der Praxis bestehen diese "Sätze" daher oft nur aus
einigen Zeichen. Der große Nachteil ist dabei die Abhängigkeit von dem speziellen Prozessor. Bei
jedem neuen Prozessortyp haben sich die Konstrukteure neue Maschinenbefehle ausgedacht, die
neue Aufgaben erfüllen oder alte Aufgaben erleichtern sollen. Es ist aber ziemlich aufwendig, für
einen neuen Computer völlig neue Programme zu schreiben. Daher kam schnell der Wunsch nach
"höheren" Sprachen auf.
Eine höhere Sprache soll eine weitgehende Unabhängigkeit von dem speziellen Prozessortyp
gewährleisten. Sie soll eine genaue Kenntnis der jeweiligen Maschinensprache oder spezieller
Ausstattungsmerkmale unnötig machen. Und schließlich soll sie rationell sein, man sollte nach
Möglichkeit eine lange Kette von Maschinenbefehlen durch einige wenige Befehl in der höheren
Sprache ersetzen können.
Die sich aus diesen Bedingungen ergebenden Vorteile sind vielfältig. Die höhere Sprache ist
einfacher zu lernen. Nach Möglichkeit ist der Aufbau der Befehle einer Umgangssprache verwandt,
im Idealfall ist ein Befehl in einer höheren Sprache also im Klartext verständlich: "If X less than 2
then goto Label". Ein Programm in dieser Sprache ist einfacher zu strukturieren, man kann es
leichter entwerfen, ändern, und andere können es leichter verstehen. Dieser letzte Aspekt wird
immer wichtiger. Viele Programmsysteme bestehen aus vielen Hundert Programmteilen
(Unterprogrammen), die von verschiedenen Programmierern geschrieben und getestet wurden.
Durch die angestrebte Unabhängigkeit vom Maschinentyp kommt es zu Programmbibliotheken, in
denen Programme und Programmteile, die immer wieder benötigt werden, bis auf Abruf wie
Bücher aufbewahrt werden.
Compiler oder Interpreter?
Wo Licht ist, ist auch Schatten; natürlich haben höhere Sprachen auch Nachteile. Einer davon ist,
dass die in einer höheren Sprache geschriebenen Programme natürlich letztendlich irgendwie in
die dem Computer einzig verständliche Maschinensprache übersetzt werden müssen. Dazu
allerdings kann man wiederum Programme verwenden, da diese Übersetzung aufgrund der klaren
logischen Struktur automatisiert werden kann. Programme, welche in höheren Sprachen
geschriebene Programme in Maschinensprache übersetzen, heißen Compiler. Wenn die
Übersetzung nur aus einer einfachen Umschreibung besteht, wie es bei den Level-1 Sprachen der
Fall ist, so nennt man die Übersetzungsprogramme Assembler, daher der Ausdruck
Assemblersprachen.
Eine Möglichkeit ist es, mit Hilfe eines Compilers aus dem vollständigen Programm in einer
höheren Sprache zuerst eines in Maschinensprache zu machen, und dieses dann ausführen zu
lassen. Eine andere Möglichkeit ist es, diesen Vorgang Schritt für Schritt durchzuführen, also
Befehl für Befehl in der höheren Sprache zuerst zu übersetzen und sofort auszuführen.
Programme, die diese Vorgangsweise erlauben, nennt man Interpreter, und es gibt Sprachen wie
etwa BASIC oder LOGO, die speziell dafür gedacht sind. Der Vorteil dabei ist, dass man etwaige
logische Programmfehler besser erkennen kann, der Nachteil ist, dass dabei wiederholt
durchlaufene Befehle auch wiederholt neu übersetzt werden müssen, was Computerzeit kostet.
Das MATHEMATICA-System etwa kann sowohl interpretieren als auch kompilieren, je nach
Wunsch.
In diesem Zusammenhang tauchen noch einige Begriffe auf. Der lesbare Text, also die in der
jeweiligen Sprache geschriebenen Zeilen, wird Quellentext oder Quellencode (nach dem
englischen Begriff Source Code) genannt. Häufig übersetzt das Compilerprogramm dieses
Quellenprogramm zuerst nicht sofort in ein ausführbares Programm in Maschinensprache
15.11.2005 14:01
Computer und Physik
4 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
(welches die Bezeichnung Executable-Code oder ausführbarer Code trägt), sondern in eine Art
Zwischenprogramm, in dem noch nicht die tatsächlichen Speicheradressen eingetragen sind.
Programme oder Programmteile in diesem Zwischencode ( früher Relative Binary Code oder
Relocatable Binary Code, heute meist Object Code genannt) können, obwohl unabhängig
voneinander übersetzt, dann relativ leicht zu einem gemeinsamen Maschinenprogramm kombiniert
und unmittelbar vor der Ausführung mit den tatsächlichen Speicheradressen versehen werden.
Häufig sind gerade Programmbibliotheken, in denen man immer wieder benötigte
Unterprogramme findet, schon in dem teilübersetzten Object Code angelegt. Dies hilft dem
Hersteller der entsprechenden Software bei der Sicherung vor Raubkopien; er stellt oft überhaupt
nur diese Version seiner Programme zur Verfügung, die dann nicht ohne weiteres auf andere
Maschinentypen oder, bei geeigneter Programmierung, selbst andere Maschinen gleichen Typs
übertragen werden kann.
Zurück zu möglichen Nachteilen des Einsatzes von höheren Sprachen. Die automatische
Übersetzung mit Compilern kann meist nicht optimal sein, also nicht auf alle Eigenheiten des
jeweiligen Computertyps oder der speziellen Problemstellung Rücksicht nehmen. Wie groß dieser
Nachteil ist, hängt de facto vom Compiler-Programm ab, und diese werden in zunehmendem
Masse "intelligenter". Ein guter Compiler hat eine Optimierungsphase, in der das Programm oft
noch deutlich verbessert werden kann. Diese Verbesserung kann oft Fehler des Programmierers
reparieren. Sollte er zum Beispiel eine bestimmte Rechenoperation unnötig wiederholt ausführen,
so kann dies der Compiler erkennen, und auf die Wiederholung verzichten. Auch bei der
Fehlersuche kann die vorhandene Software wichtige Hilfe leisten.
Programme
Ein Nachteil ist evident: die Programmiersprache ist eine eigene Sprache, die man erlernen muss,
um sie richtig zu gebrauchen. Ideal wäre es, wenn man dem Computer die Problemstellung und
den vorgeschlagenen Lösungsweg in der Umgangssprache mitteilen könnte. Von diesem Ziel sind
wir weit entfernt, und in dieser Form wird es wohl nie erreichbar sein. Die Umgangssprache ist
dazu viel zu vieldeutig. Dennoch ist noch viel Raum für Schritte in diese Richtung.
Ein Programm ist ähnlich einem Handbuch zur Bedienung oder Reparatur einer komplizierten
Maschine. Es besteht aus Teilprogrammen, die den Kapiteln und Absätzen entsprechen. Diese
wiederum bestehen aus Befehlen, dem Äquivalent von Sätzen. Die logische Form der Befehle ist
durch die Regeln der Sprache, die Syntax vorgeschrieben; die Befehle müssen also zumindest der
Form nach richtig sein. Die Anordnung dieser Sätze unseres Buches muss Sinn machen, wenn
man den Befehlen des Programmes folgt und alle Anweisungen richtig ausführt, so muss das
Ergebnis unserer Bemühungen das gewünschte sein, also zum Beispiel die erfolgreiche Reparatur
der Maschine.
In einem Bedienungshandbuch finden wir verschiedene Typen von Anweisungen. Denke an einen
Fotokopierapparat. Es könnte notwendig sein, Zähler der Maschine abzulesen oder Messungen
durchzuführen. Man könnte aufgefordert werden, einige Zahlen auf einer Tastatur einzugeben.
Man muss vielleicht auch bestimmte Handgriffe tätigen. Entsprechend gibt es verschieden Typen
von Anweisungen in einem Programm. Sie alle folgen der Struktur der Von-Neumann Maschine.
Man kann
(a) Daten lesen oder speichern,
(b) Daten in Registern manipulieren, also zum Beispiel zwei Datenregister addieren oder
subtrahieren,
(c) die Abfolge der Befehle beeinflussen, also zum Beispiel die Adresse des nächsten
Befehls in Abhängigkeit von Werten bestimmter Daten verändern.
Je höher das Niveau einer Programmiersprache ist, umso vielfältiger werden die Befehle wirken.
Oft werden sie Kombinationen der drei genannten Gruppen sein.
Zusätzlich zu diesen ausführbaren Anweisungen ist es aber notwendig, zunächst festzulegen, wie
die Daten, die man bearbeiten möchte, denn eigentlich beschaffen sind. Soll ich die Ziffern eines
Zähler meiner Fotokopiermaschine von oben nach unten oder von unten nach oben ablesen?
15.11.2005 14:01
Computer und Physik
5 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Handelt es sich um eine Dezimalzahl und wenn ja, wo ist der Dezimalpunkt? Gibt der Zähler die
Anzahl der Kopien oder die Dicke der Papierblätter an? All das hat Analogien in einem Programm,
und daher gibt es die Gruppe der nicht ausführbaren Anweisungen, der sogenannten
Vereinbarungsanweisungen. Sie sind im Grunde nur für den Compiler wichtig, da bei der
Übersetzung in die Maschinensprache entschieden werden muss, welcher Art die zu
verarbeitenden Daten sind.
Aufgabe 2.2.1.A1
Aufgabe 2.2.1.A2
Aufgabe 2.2.1.A3
Aufgabe 2.2.1.A4
Aufgabe 2.2.1.A5
Hier is ein Link zu einer grafischen Übersicht zur Entwicklung der Computersparchen "
Es ist dies ein Auszug aus dem Original: Computer Languages History from
http://www.levenez.com/lang/ .
Timeline ".
2.2.2 Maschinensprache und Assembler
Die Vielfalt der Befehle eine Maschinensprache hängt von den Ausstattungsmerkmalen des
Prozessors ab. Je mehr Bits für die interne Darstellung von Befehlen vorgesehen wurden, desto
mehr Befehle kann der Hersteller einplanen. In der ersten Generation von Personalcomputern fand
man häufig Prozessorchips des Typs M-6502 oder INTEL-8085A, das sind 8-Bit Prozessoren.
Beide haben einen ähnlichen Satz von Befehlen und wir wollen den Befehlsvorrat am Beispiel des
M-6502 vorstellen.
Im Intel 80386 Reference Programmer's Manual findet man eine Liste der Maschinenbefehle des
Intel 80386 Prozessors und hier ist das Reference Manual zum Motorola 68000 Prozessor ..
Befehle des einfachen 8-Bit Prozessors Motorola 6502 (Mitte der 80er Jahre)
Befehl
Bedeutung
Lade- und Speicherbefehle
LDA
A-Register laden
LDX
X-Register laden
LDY
Y-Register laden
STA
A-Register speichern
STX
X-Register speichern
STY
Y-Register speichern
TAX
A nach X übertragen
TAY
A nach Y übertragen
TSX
Stackzeiger nach X übertragen
TXS
X nach Stackzeiger übertragen
TXA
X nach A übertragen
TYA
Y nach A übertragen
Stackbefehle
Befehl
Bedeutung
Rechenbefehle
ADC
Addieren mit Übertrag
SBC
Subtraktion mit Übertrag
SEC
Setze Übertragsbit
CLC
Übertragsbit löschen
SED
Setze Dezimal-Modus
CLD
Dezimalindikator löschen
CLV
Überlauf löschen
BIT
Bit testen
CMP
mit A-Register vergleichen
CPX
mit X-Register vergleichen
CPY
mit Y-Register vergleichen
Verzweigen, wenn Übertragsbit
BCC
gelöscht
15.11.2005 14:01
Computer und Physik
6 of 14
PHA
PUSH: A-Register ins Stack bringen
POP: Untersten Stack-Wert ins
PLA
A-Register bringen
PUSH: Befehlsadresse ins Stack
PHP
bringen
Sprungbefehle
JMP
Unbedingte Verzweigung
JSR
Verzweigung in ein Unterprogramm
RTS
Rückkehr aus Unterprogramm
Interruptbefehle
RTI
Rückkehr nach Interrupt
Interrupt-Möglichkeit wird
SEI
abgeschaltet
CLI
Interruptindikator löschen
BRK
Unterbrechung
NOP
Leerbefehl (keine Aktion)
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
BCS
BVC
BVS
BEQ
BMI
BNE
BPL
DEC
DEX
DEY
INC
INX
INY
AND
ORA
EOR
ASL
LSR
ROL
ROR
Verzweigen, wenn Übertragsbit
gesetzt
Verzweigen, wenn Überlauf gelöscht
Verzweigen, wenn Überlauf gesetzt
Verzweigen, wenn Ergebnis gleich
Null
Verzweigen, wenn Ergebnis negativ
Verzweigen, wenn Ergebnis ungleich
Null
Verzweigen, wenn Ergebnis positiv
Speicher dekrementieren
X-Register dekrementieren
Y-Register dekrementieren
Speicher inkrementieren
X-Register inkrementieren
Y-Register inkrementieren
Logisches UND
Logisches ODER
Exklusives ODER
Arithmetisches Verschieben nach
links
Logisches Verschieben nach rechts
zyklisch nach links verschieben
zyklisch nach rechts verschieben
(a) Vom Speicher zum Prozessor und zurück
Das ist eine Gruppe von Befehlen, die mit den Worten LOAD (LD) und STORE (ST)
abgekürzt werden. Wie im ersten Teil besprochen, gibt es ja neben dem eigentlichen
Speicher, in dem Daten und Programme liegen, auch Register, das sind die
schnellen Rechenspeicher im Prozessor. Mit dieser Befehlsgruppe kann ein
bestimmtes Speicherwort in ein Register gebracht werden, oder, umgekehrt, der
Inhalt eines Registers an eine bestimmte Stelle im Speicher.
Es gibt verschiedene Variationen in dieser Befehlsfamilie. So kann beispielsweise
auch ein als Teil des Befehls angegebenes Datenwort direkt in ein Register geladen
werden. Eine weitere Möglichkeit sind indirekte Adressangaben: der Registerinhalt
soll an die Speicheradresse gebracht werden, die an einer anderen Speicheradresse
angegeben ist. Die Zieladresse kann auch indiziert werden, also automatisch um
den in einem weiteren Register gefundenen Wert erhöht werden.
Die Transferbefehle gehören auch zu dieser Gruppe; dabei wird der Inhalt eines
Registers in ein anderes Register kopiert. Diese Befehle können direkt im Prozessor
ausgeführt werden, und sie gehören daher zu den schnellsten.
(b) Arithmetische und logische Rechenbefehle
Hier werden vorwiegend Registerinhalte manipuliert. Man kann eine Zahl von der in
einem Register gespeicherten Zahl abziehen oder sie dazu zählen, man kann beim
M-6502 auch Zahlen, die auf bestimmten Speicheradressen zu finden sind, zu
15.11.2005 14:01
Computer und Physik
7 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Registern addieren oder davon subtrahieren.
Daneben gibt es die Gruppe von Befehlen, bei denen Bit für Bit zweier Datenworte
miteinander durch die logischen AND ("logisches Und"), OR ("logisches Oder") und
XOR ("exklusives Oder") Operationen verknüpft werden. Auch Verschiebung oder
zyklische Vertauschung der Bits in einem Register ist möglich.
Schließlich gehören in diese Gruppe die Vergleichsbefehle, in denen ein
Registerinhalt mit einem Datenbyte verglichen wird und je nach Ergebnis bestimmte
Registerbits gesetzt oder gelöscht werden. Zusammen mit der Gruppe der
Sprungbefehle erlaubt dies die Programmierung von Entscheidungsschritten und
Verzweigungen im Programm.
(c) Stackbefehle
Das "Stack" ist eine besondere, reservierte Reihe von Speicherplätzen, die als
erweiterter Adressspeicher eingesetzt wird. Bei Programmbeginn wird es gelöscht,
oder mit bestimmten Befehlsadressen belegt. Während der Programmausführung
kann es sich nützlich erweisen, die momentane Befehlsadresse abzuspeichern, sich
also "zu merken", um dann später wieder an dieser Stelle fortsetzen zu können. Das
ist immer dann der Fall, wenn man eine Zwischenrechnung ausführen möchte, also
zwar das Ergebnis braucht, aber den entsprechenden Rechnungsablauf in einem
Unterprogramm angegeben hat. Man speichert die momentane Adresse im
Hauptprogramm an die letze Stelle des Stacks und arbeitet im Unterprogramm
weiter. Am Ende des Unterprogramms holt man sich die letzte Stackadresse, um
dort, im Hauptprogramm weiterzumachen.
Sollte es sich in dem Unterprogramm notwendig erweisen, ein weiteres
Unterprogramm aufzurufen, so wiederholt man das Spiel. Das Stack wird um eine
Stelle nach oben verschoben (die oberste Adresse geht dabei verloren), und an
unterster Stelle wird wieder die Adresse der Verzweigungsstelle gespeichert und
dann ins Unterunterprogramm verzweigt. Nach Erledigung dieser Zwischenrechnung
wird wieder auf die an unterster Stelle im Stack angegebene Adresse (also zurück
ins Unterprogramm) gewechselt, und das Stack entsprechend um eine Stelle nach
unten verschoben. Auf diese Weise hält man automatisch Buch über die
Verzweigungsstruktur.
Die entsprechenden Prozessorbefehle heißen PUSH und POP, sie bewirken das
Verschieben des Stacks um eine Stelle hinauf oder hinunter. Bei PUSH wird die der
momentane Inhalt des Befehlsadressregisters dem Stack hinzugefügt, bei POP wird
die an unterster Stelle im Stack stehende Adresse ins Befehlsadressregister geladen
und aus dem Stack gelöscht.
(d) Sprungbefehle
Bei ihnen wird einfach der Inhalt des Befehlsadressregisters verändert und so der
nächste auszuführende Befehl bestimmt. Bei den bisher genannten Befehlen wird
das Adressregister einfach um eine Stelle erhöht, also als nächstes der folgende
Befehl im Speicher ausgeführt. Nun kann auch ein beliebiger Wert in dieser spezielle
Register gebracht werden. Eine Variante der einfachen Sprungbefehle (JUMP) sind
bedingte Sprungbefehle, bei denen die Befehlsadresse nur dann geändert wird,
wenn beispielsweise ein bestimmtes Bit in einem Register gesetzt ist. Zusammen mit
den Vergleichsbefehlen erlaubt dies eine vergleichsabhängige Verzweigung im
Programm, also einen echten Entscheidungsschritt.
Sprungbefehle, bei denen gleichzeitig die Ursprungsadresse an einem bestimmten
Platz abgespeichert wird, erlauben die spätere Rückkehr an diese Stelle
("Return-Jump" und "Return"). Diese Kombination wird oft bei Verzweigung in und
Rückkehr aus einem Unterprogramm verwendet.
15.11.2005 14:01
Computer und Physik
8 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
(e) Interrupt- und I/O-Befehle
Neben dem Programm- und Datenspeicher gibt es noch andere "Speicher", das
können sowohl echte externe Speicher wie Diskettenstation oder
Magnetbandspeicher sein, aber auch Eingabe- und Ausgabemedien wie Tastatur,
Maus oder andere Systeme. Die Kommunikation mit diesen Medien erfolgt oft über
bestimmte, dafür reservierte Speicheradressen und über Interruptleitungen. Mit
Befehlen dieser Gruppe kann die Reaktion des Programms auf einen Interrupt, der
das Anliegen von externen Signalen anzeigt, gesteuert werden. Es ist dann Aufgabe
des Programms, durch entsprechende Aktionen auf diesen Interrupt zu reagieren,
also etwa Daten zu übertragen oder andere Aktionen einzuleiten.
Interessant ist ein eigenartiger Befehl, den man in allen Prozessoren braucht und der
nichts bewirkt: NOP (die Abkürzung für "no operation")! Dieser Befehl setzt den
Prozessor für genau einen Rechentakt in Ruhe. Dies kann zum Beispiel dann wichtig
sein, wenn man zeitabhängige Programmschritte durchführt. Es kann sich als
notwendig erweisen, eine Reihe von Rechentakten "zu schlafen", um etwa auf das
Eintreffen eines Interrupt-Signals zu warten.
Prozessoren mit größerer Bandbreite haben einen größeren Befehlsvorrat. Der Motorola-68000
etwa, als 16-Bit Prozessor, hatte bei der Gruppe der arithmetisch-logischen Operationen auch
Multiplikationsbefehle. Beim einfachen M-6502 mußte eine Multiplikation noch auf eine Reihen von
Additionen zurückgeführt werden. Auch weitere logische Befehle und raffiniertere Abfragen waren
beim 68000-er eingebaut, ebenso die Möglichkeit der Manipulation von mehreren Registern mit
einem Befehl. Zusätzliche "Co-Prozessoren" erlaubten die schnelle Verwendung von
Gleitkommazahlen (Floating Point Accelerators) und speziellen mathematischen Funktionen.
Neuere Intel-Prozessoren (seit Intel 80386) haben eine Breite von 32 Bit und in jeder Generation
kommen ein paar spezieller Befehle dazu.
Die in größeren Computern eingesetzten Prozessoren sind Spezialanfertigungen, die nur für den
entsprechenden Computertyp gebaut werden und haben dementsprechend oft spezielle Befehle,
die der zur Verfügung stehenden Hardware angepasst sind.
So ist auf dem Niveau der Maschinensprache keinerlei Standard in Sicht, im Gegenteil, jeder neue
Prozessortyp bringt neue Befehlssätze und es herrscht bereits eine babylonische
Sprachverwirrung, die nur durch die Dominanz eines Herstellers (Intel) gemindert wird. Eine kleine
Hilfe bieten Assemblersprachen, die zumindest die wichtigsten Befehlstypen einheitlich zu
bezeichnen erlauben, und diese dann in die entsprechende Maschinensprache übersetzen.
Dennoch müssen bei jedem neuen Prozessortyp viele Systemprogramme neu geschrieben werde.
Man ist daher dazu übergegangen, auch Systemprogramme in einer etwas höheren Sprache wie
etwa C zu schreiben. Diese beiden Sprachen sind in der Grauzone zwischen eigentlichen
Maschinensprachen und höheren Sprachen angesiedelt. Sie sind maschinennahe, aber nicht an
eine bestimmte Hardware gebunden und so universell verwendbar. Man muss dann für einen
neuen Prozessor nur mehr ein Programm schreiben, das zum Beispiel den C-Compiler in die
Maschinensprache übersetzt. Ja, man kann sogar noch raffinierter vorgehen und auch den
größten Teil des C-Compilers in C schreiben, und nur einen kleinen Teil (der dann später die
Übersetzung des Rests besorgt) in Maschinensprache zu programmieren. Dieser Trick erinnert an
Freiherr von Münchhausen, der sich an seinen Schnürsenkeln aus dem Sumpf zog - oder es
zumindest behauptete - und wird daher "Bootstrap" (Schnürsenkel) genannt. Das verbreitete
Betriebssystem UNIX ist in C geschrieben.
Aufgabe 2.2.2.A1
2.2.3 Höhere Sprachen
15.11.2005 14:01
Computer und Physik
9 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Zunächst ein kurzer Überblick. Es gibt Sprachen für die unterschiedlichsten Anwendungsbereiche.
Die für uns wichtigsten Gebiete sind wohl numerisches und symbolisches (auch: algebraisches)
Rechnen. Numerische Rechnungen arbeiten mit Zahlen und die Ergebnisse sind Zahlen, wie etwa
1+2.55 ergibt 3.55. Symbolische Rechnungen arbeiten mit Symbolen, also zum Beispiel formalen
Ausdrücken mit Variablennamen wie etwa (a+b) 2 ergibt a2 + 2 a b + b2.
In der Tabelle findet man höhere Sprachen, die vorwiegend für numerisches Programmieren
vorgesehen sind.
Sprache
Erstentwicklung
Kommentar
Ada
Reference manual July
1980
Approved implementations
1983, 1995
Sponsored by US Dept. Defense
ALGOL
1960
1968 (much modified)
Mostly Europe; relatives like Pascal, little used
nowadays
SIMULA
1967
ALGOL extension
APL
Iverson's 1962 book
APL/360-1967
Concise, very symbolic, little used nowadays
BASIC
1965
Developed for student use at Dartmouth College.
COBOL
Initial design-1959 First
running versions -1960
Primarily for business applications, outdated
FORTRAN 1957; 1977, 1990, 1995
FORmula TRANslator, Standards f77, f90, f95; vector
operations since f90; first successful "algebraic"
language
LISP
1960
List programming language, basis for computer algebra
languages (e.g. Reduce) and artifical intelligence
projects
LOGO
1967
Junior high level; turtle geometry
Pascal
1971, ISO Standard
General purpose, ALGOL relative; promoted through
Borlands TurboPascal, later offspring: Borland Delphi
PL/I
1968 (IBM compiler)
Attempts at a comprehensvie language, outdated
SNOBOL
1962
Symbolic (string manipulation) language, outdated
C
1978 (Kernigham, Ritchie)
General purpose, closer to machine language than e.g.
FORTRAN, excellent for string processing, but included
arithmetic, heavily used
C++
1985 (AT&T, Strostrup; no
ANSI standard yet)
C-based and Object oriented language (Class structure)
Daneben gibt es zahlreiche special-purpose Sprachen wie
TeX/LaTex (für Publikationen)
POSTSCRIPT als Druckdefinitionssprache
HTML für Hypertext Seiten
C(Shell), PHP, PERL etc. als Scripting Sprachen
usw.usw. Diese gehören aber nicht im engeren Sinn zu den in der Physik relevanten höheren
Sprachen. Sehr wohl relevant sind die sogenannten "symbolischen Sprachen", über die weiter
unten noch Informationen folgen.
Wozu höhere Sprachen verwenden? Mir fallen folgende Vorteile ein:
Einfachere Strukturierung und kürzere Programme: Einzelne Befehle (z. B. do...while,
15.11.2005 14:01
Computer und Physik
10 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
print usw.) entsprechen meist umfangreichen "Unterprogrammen". Ein möglicher Nachteil
ist, dass die allgemeinere Art dieser standardisierten "Unterprogramme" die Ausführung
verlangsamt. Andrerseits wird im Design viel mehr Zeit in die Optimierung dieser
"Unterprogramme" investiert.
Einfacheres Debugging: In einer klaren Programmiersprache treten weniger Fehler auf
und wenn doch, sind sie leichter zu finden
Höhere Produktivität: Durch die modulare Bauweise können große Projekte einfacher und
schneller abgewickelt werden
Falls du dich mit ein der der Programmiersprachen beschäftigen willst, sie zum Beispiel lernen
willst: hier sind ein paar Links für ein Selbststudium.
Brian Brown: C-course on the web (Vienna mirror)
A. D. Marschall: Programming in C (Vienna mirror)
MAN-T&EC: F90 Kurs fü F77-Programmierer: Original oder Karlsruhe Mirror
F90 Kurs Univ Liverpool: F90 Course
World Lecture Hall: e.g. Courses on Computer Science and on Physics
Auch bei uns wird ein Kurs zu C und C++ angeboten:
U. Hohenester: Programmierung in C und C++
Natürlich gibt es noch mehr Sprachen, die man als höhere Sprachen bezeichnen kann. Das hier
sollte nur eine Liste der im Bereich der Physik häufig verwendeten sein. Hier nicht vergessen,
sondern unter "Symbolische Sprachen" diskutiert sind Mathlab, Maple und Mathematica.
Sprachen sollten nicht mit Programmbibliotheken verwechselt werden. OpenGL oder DirectX sind
Sammlungen von Programmen, die einen hardwareunabhängigen Zugriff auf die Grafik erlauben.
2.2.4 Unterschiede zwischen den Programmiersprachen
Obwohl das Konzept des Computers seit den 30-er Jahren in mathematisch-abstrakter
Formulierung vorgelegen war, wurden Programmiersprachen zuerst doch eher ad hoc eingeführt,
als Abkürzungen, die dem "an der Basis arbeitenden" Programmierer das Leben erleichtern
sollten. Die ersten Sprachen waren daher zunächst nicht sehr klar durchdacht und wurden in
darauf folgenden Jahren immer wieder in ihrer Struktur verändert oder um neue Befehle erweitert.
Erst Mitte der 50-er Jahre begann man, sich Gedanken zur Syntax einer Programmiersprache zu
machen und Noam Chomsky, einer der bekanntesten Linguisten, hat wohl die wesentlichsten
Beiträge dazu geliefert. Man versuchte, die Sprache mathematisch sauber als formale Sprache zu
definieren und diese Definition in eine sogenannte "Normalform" zu bringen. So gibt es etwa seit
1960 die Backus-Naur-Normalform für die Programmiersprache ALGOL. Es ist in dieser
Formulierung möglich, eindeutig und mit kleinstmöglichem Aufwand die formale Richtigkeit von
Sätzen der jeweiligen Sprache zu überprüfen.
Bei einer Klassifikation von Programmiersprachen kann man vier Aspekte anführen.
1.
2.
3.
4.
Das Alphabet der Sprache, die zulässigen Befehle und Variablennamen
Die Struktur und Art der zulässigen Daten und nicht ausführbaren Anweisungen
Die Art und Form der arithmetischen und anderen Ausdrücke und Anweisungen.
Die Möglichkeiten der Programmstruktur
Um einen groben "Geschmack" für die unterschiedlichen Programmiersprachen zu vermitteln,
gebe ich hier einige wenige typische Beispiele für (ausführbare und nicht-ausführbare)
Anweisungen in F90 und C.
15.11.2005 14:01
Computer und Physik
11 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
Fortran 90
C
Alphabet
Alle Buchstaben und Ziffern sowie eine
Reihe von Zeichen wie etwa
=+-~/(),.%': _!"%&;<>?
dazu noch []|^
Beispiele für Variablennamen
High, Low, what_a_mess, a(20), num23 High, Low, what_a_mess, a[20]
Vereinbarungsanweisungen
integer :: i,j,k
real, dimension(0:100) ::x
int i,j,k;
float x[101];
Ausführbare Anweisungen
a=a+5.3*b+sin(c)
a=a+5.3*b+sin(c);
i=i+1;
i++;
Abfragen
if (a<0) then
a=27.3
b=a*a
endif
if(a<0)
{a=27.3; b=a*a;}
Schleifen
do i=1,100
a=a+i
enddo
for (i=1; i<101; i++) a=a+1;
Programmbeispiele C und Fortran90
Am Beispiel der Erstellung einer Liste von Werten und der anschließenden Sortierung soll nun
eine Idee davon gegeben werden, wie C und F90 Programme aussehen.
Programmierbeispiel F90
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Sortieren der Werte von sin(i) für i=0 bis 9 (in Schritten von 1)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Vereinbarungsanweisungen:
real, dimension(0:9)
real
integer
:: a
:: b
:: i,j
! Berechnung der Werte in eine Liste
do i=0,9
a(i)=sin(float(i))
enddo
!
Printout der unsortierten Liste
15.11.2005 14:01
Computer und Physik
12 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
print *,"Unsortierte Liste: ", a
! Sortieren (der jeweils kleinste der restlichen Werte
! wird nach vorne gebracht)
do i=0,8
do j=i+1,9
if(a(j).lt.a(i)) then
b=a(i)
a(i)=a(j)
a(j)=b
endif
enddo
enddo
!
Printout der sortierten Liste
print *,"Sortierte Liste:
stop
end
", a
Programmierbeispiel C
/**********************************************************************
* Sortieren der Werte von sin(i) f0r i=0 bis 9 (in Schritten von 1)*
**********************************************************************/
/* Header Files einlesen:
*/
#include <stdio.h>
#include <math.h>
void main()
{
/* Vereinbarungsanweisungen:
*/
int i,j;
float a[10],b;
/* Berechnung der Werte in eine Liste */
for(i=0; i<10; i++) a[i]=sin((float) i);
/* Printout der unsortierten Liste
*/
printf("Unsortierte Liste:\n");
for(i=0; i<10; i++) printf("%10.5f\n",a[i]);
/* Sortieren (der jeweils kleinste der restlichen Werte
wird nach vorne gebracht)
*/
for(i=0; i<9; i++ )
for(j=i+1; j<10; j++ )
if( a[j] < a[i] ) { b=a[i]; a[i]=a[j]; a[j]=b;}
15.11.2005 14:01
Computer und Physik
13 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
/* Printout der sortierten Liste
*/
printf("Sortierte Liste:\n");
for(i=0; i<10; i++) printf("%10.5f\n",a[i]);
}
2.2.5 Softwareprojekte
Wie bei Kriminalromanen, geht es bei der Planung von (größeren) Softwareprojekten um die
berühmten W:
Was
Wann
Wieviel
Wo
Wer
Ziel des Projekts, Randbedingungen, Prioritäten;
Arbeitsplan
Budget, Aufwand
Welche Computer, Arbeitsräume usw.?
Wer soll die Arbeit durchführen, welche Organisationsstrukur
Von Beginn darf man nicht auf die Dokumentation vergessen. Einerseits ist sie am Weg hilfreich,
andrerseits wird man vermutlich auch nach Fertigstellung immer wieder Fragen haben, deren
Antwort man dort finden kann:
Proposal (Schriftliche Darstellung der großen W), Arbeitsverlauf, interne Dokumentation, Manual.
Das Programm solls sich selbst dokumentieren, also ausreichend viel Erklärungstext enthalten.
Tips zur effizienten Programmerstellung
Für Variablen mnemonische Namen verwenden (Temperatur=37.5), ebenso für
Programmnamen und Datenfiles, u.U. mit einer Versionsbezeichnung versehen
(GaussIntegral_01.c)
"Selfdocumenting programs": Dokumentation direkt ins Programm einbauen. Die
verwendeten Algorithmen klar bezeichnen, u,U, mit Literaturhinweis. Versionsbezeichnung
am Beginn.
Alle Variablen explizit definieren
Text durch Einrückung strukturieren
Strukturiert programmieren
Der Begriff "strukturiert programmieren" Das muss etwas erläutert werden. Strukturiert bedeutet
eine Trennung von Aufgaben in wohldefinierte Blöcke (Unterprogramme mit klarem Input und
Output). Das erlaubt ein unabhängiges Testen dieser Blöcke und auch die Weiterverwendung in
anderen Programme.
Früher wurde oft ein sogenannte Flussdiagramm verwendet, dessen Bedeutung aber überschätzt
wird. Wichtig ist, ein grobes Schema des Programmes, möglichst auf einer Seite, zu haben. Die
wichtigsten Teilaufgaben tauchen dort zum Beispiel als Kästchen mit Kurzbeschreibung auf. Man
kann diese Kästchen dann auf getrennten Blättern weiter ausarbeiten und detaillieren (Aufgabe,
Methode, Parameter und Variable etc.).
Ich beginne meist mit einem einfachen Hauptprogramm, in dem die Aufgaben der Unterblöcke
zunächst nur in Kommentarform auftauchen. Schrittweise wird dann dieses Skelett mit Fleisch
gefüllt, also die Unterblöcke entworfen, getestet und eingefügt. Kompliziertere Teile kann man in
einem Zwischenschritt durch "Dummy-Programme" ersetzen, die nichts tun, aus die Funktion der
15.11.2005 14:01
Computer und Physik
14 of 14
http://physik.uni-graz.at/~cbl/C+P/admin/mk_combined_fi...
tatsächlichen Unterprogramme (die noch nicht fertig sind) "vorzugeben, also zum Beispiel zufällige
Testdaten liefern.
So "wächst" das Programm bis zur Fertigstellung. Es wird also nicht Stein auf Stein "gebaut",
sondern es "wächst" - in der Tat - durch schrittweise Erweiterung.
Gleiches gilt für die Optimierung. Es empfiehlt sich, zuerst sehr einfache, vor allem übersichtliche,
Methoden der Rechnung zu programmieren. Wenn diese Programme funktionieren, kann man
einzelne Teile schrittweise optimieren, zum Beispiel durch Umorganisation von Schleifen und
ähnlichem ("progressive refinement").
Aus all dem wird klar, dass man wichtige Strukturänderungen später kaum mehr durchführen
kann. Daher soll vor Beginn vor allem
die allgemeine Programm Struktur
die Struktur der wichtige Daten
genau planen.
Ein amüsantes Buch (und ein Klassiker) zum Thema Projektplanung und Programmentwicklung ist
F. P. Brooks: The mythical man-month (Addison-Wesley, 1995)
15.11.2005 14:01
Herunterladen