EDV1 Fortran95 Skript als PDF

Werbung
Einführung in die Informationstechnik für Ingenieure
– Fortran95–Skript –
Herrmann–Föttinger–Institut für Strömungsmechanik
Technische Universität Berlin
W. Baumann, C. Böhning, T. Schmidt
31. März 2003
Inhaltsverzeichnis
4 Programmiersprachen, FORTRAN
4.1
4.2
Allgemeines zu Programmiersprachen
1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
4.1.1
Wahl einer Programmiersprache . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
4.1.2
Sprachniveaus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
4.1.3
Höhere Programmiersprachen: Besonderheiten, Übersicht und Historisches . . . . .
3
FORTRAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
4.2.1
Einfache FORTRAN–Programmbeispiele . . . . . . . . . . . . . . . . . . . . . . . .
8
4.2.2
Sprachelemente von Fortran95 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
4.2.2.1
Zeichensatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
4.2.2.2
FORTRAN–Quelltextzeile . . . . . . . . . . . . . . . . . . . . . . . . . .
10
4.2.2.3
Symbolische Namen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
4.2.2.4
Konstanten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
4.2.2.5
Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
4.2.2.6
Anordnungsreihenfolge von Anweisungen . . . . . . . . . . . . . . . . . .
12
Vereinbarungsanweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
4.2.3.1
Typvereinbarungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
4.2.3.2
Explizite Typvereinbarungen . . . . . . . . . . . . . . . . . . . . . . . . .
15
4.2.3.3
Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
4.2.3.4
Speicherung von Feldern . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
4.2.3.5
Dynamische Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
4.2.3.6
Zeiger und Ziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
4.2.4
Initialisierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
4.2.5
Arithmetische Ausdrücke und Anweisungen . . . . . . . . . . . . . . . . . . . . . .
22
4.2.5.1
Arithmetische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
4.2.5.2
Arithmetische Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . .
24
4.2.3
ii
INHALTSVERZEICHNIS
4.2.6
iii
Logische Ausdrücke und Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . .
25
4.2.6.1
Logische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
4.2.6.2
Logische Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
4.2.7
Zeichenausdrücke und -anweisungen . . . . . . . . . . . . . . . . . . . . . . . . . .
27
4.2.8
Ausdrücke und Anweisungen mit Feldern und Teilfeldern . . . . . . . . . . . . . .
28
4.2.8.1
Ausdrücke mit Feldern und Teilfeldern
. . . . . . . . . . . . . . . . . . .
28
4.2.8.2
Anweisungen mit Feldern und Teilfeldern . . . . . . . . . . . . . . . . . .
29
Steueranweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
4.2.10 Verzweigungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
4.2.10.1 Block–IF–Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
4.2.10.2 CASE–Verzweigung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
4.2.11 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
4.2.11.1 Zählschleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
4.2.11.2 Schleifen mit Eintrittsbedingung . . . . . . . . . . . . . . . . . . . . . . .
38
4.2.11.3 Schleifen mit Austrittsbedingung . . . . . . . . . . . . . . . . . . . . . . .
39
4.2.12 CONTAINS– und END–Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
4.2.13 Ein-/Ausgabe (Standard-Ein-/Ausgabe) . . . . . . . . . . . . . . . . . . . . . . . .
41
4.2.14 Erweiterte Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
42
4.2.14.1 Formatierte Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . .
42
4.2.14.2 Externe Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
4.2.14.3 Interne Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
4.2.15 Programmeinheiten und Anweisungen . . . . . . . . . . . . . . . . . . . . . . . . .
51
4.2.16 Parameterlisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
4.2.17 Vordefinierte Standardfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
4.2.18 Benutzerdefinierte Funktionen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
4.2.19 Benutzerdefinierte Unterprogramme – Subroutinen . . . . . . . . . . . . . . . . . .
57
4.2.20 Speicherplatz-Zuordnung und Einbettung von Programmzeilen . . . . . . . . . . .
58
4.2.21 Kommunikation zwischen Programmeinheiten . . . . . . . . . . . . . . . . . . . . .
60
4.3
Weitere Sprachmittel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
4.4
Tips zum (nicht nur FORTRAN–) Programmieren . . . . . . . . . . . . . . . . . . . . . .
64
4.5
FORTRAN–Knigge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
4.5.1
68
4.2.9
Vorbemerkung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iv
INHALTSVERZEICHNIS
4.5.2
Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
4.5.3
Projektorganisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5.4
Programme und Unterprogramme . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5.5
Format des Quelltextes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5.6
Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5.7
Groß- und Kleinschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
4.5.8
Verwendung von Leerzeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
4.5.9
Wahl von Bezeichnern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
4.5.10 Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
4.5.11 Logische Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4.5.12 Arithmetische Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4.5.13 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4.5.14 Kontrollstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
4.5.15 Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
4.5.16 COMMON–Blöcke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
4.5.17 Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
4.5.18 Abschlußbemerkung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
4.6
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
4.7
Fragen und Übungen zu Kapitel 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
4.8
Antworten zu den Fragen und Übungen zu Kapitel 4 . . . . . . . . . . . . . . . . . . . . .
76
Literatur
79
Kapitel 4
Programmiersprachen, FORTRAN
4.1
4.1.1
Allgemeines zu Programmiersprachen
Wahl einer Programmiersprache
Im Kapitel Programmiertechnik“ haben wir Techniken gelernt, wie man ausgehend von einer Aufga”
benstellung schrittweise ein Programm entwickelt. Wir haben uns dazu einer grafischen Entwurfssprache,
der Struktogrammtechnik bedient. Der nächste Schritt in der Programmentwicklung ist die sprachliche
Formulierung (Codierung) in einer Programmiersprache. Der Ausdruck Codierung weist darauf hin, daß
es sich vor allem um eine Umsetzung handelt, und die gedankliche Hauptarbeit bereits vorher geleistet
wurde. Eine Programmiersprache ist eine künstliche Sprache, die so entworfen ist, daß einerseits Menschen darin Programme formulieren können und andererseits ein Rechner diese Programme ausführen
kann. Die verwendete Programmiersprache muß über Ausdrucksmittel verfügen, die eine entsprechende,
problemnahe Beschreibung des Problems gestatten, z.B. mathematische Formeln direkt in Formelsprache
umzusetzen. Sie sollte außerdem den Prinzipien der strukturierten Programmierung entsprechen.
Die Wahl der Programmiersprache beeinflußt stark die Portabilität (Übertragbarkeit auf andere Rechnersysteme) und damit die Verbreitung eines Programms. Beschränkung auf (genormte) Standards begünstigt die Portabilität, kann aber auch den Verzicht auf attraktive, effiziente bzw. elegante Spracherweiterungen bedeuten. Die Auswahl der Programmiersprache ist jedoch oft nicht durch logische sondern durch
pragmatische Gründe bestimmt und an unterschiedliche Randbedingungen geknüpft wie
• bestehende Programmier- und Nutzungsgewohnheiten im jeweiligen Anwendungsbereich
• Festlegung auf Sprachen innerhalb bestimmter Institutionen
• Portabilität auf andere Rechnerfamilien
• Verfügbarkeit und Effizienz der Compiler (prinzipiell sind alle gängigen Programmiersprachen auf
den Standard–Betriebssystemen implementiert)
• Einschränkungen z.B. bezüglich Speicherplatz, Compilations- und Rechenzeiten.
4.1.2
Sprachniveaus
Für die Codierung von Programmen existiert eine große Zahl von Programmiersprachen, die man nach
ihren stark unterschiedlichen Ausdrucksmöglichkeiten entsprechenden Sprachniveaus zuordnet, Abb. 4.1.
2
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Zu den sogenannten maschinennahen Sprachen zählen die Assemblersprachen. Sie bilden die Befehlsstruktur der Hardware ab und sind daher an einen bestimmten Rechner oder an eine Rechnerfamilie
gebunden. Assemblerprogrammierung setzt Kenntnisse über den Aufbau und die Wirkungsweise des jeweils zugrundeliegenden Prozessors voraus. Dem Programmierer wird nur ein geringer Programmierkomfort geboten. Zwar wird ihm dadurch die Arbeit erleichtert, daß die binär codierten Maschinen–Befehle
durch eingängige Abkürzungen (mnemonics) ersetzt werden, der Programmierer muß die Aufgabe jedoch selbst in maschinennahe Befehle zerlegen, was zeitaufwendig und fehleranfällig ist. Dies führt oft
zu langen Testzeiten. Die mögliche hohe Effizienz bei der Ausführung von Assemblerprogrammen erreichen tatsächlich nur geübte Programmierer. Da zudem das Problem des Speicherplatzbedarfs weitgehend
entschärft ist, und auch höhere Programmiersprachen die Möglichkeit bieten, hardwarenahe Funktionen
zu programmieren (z.B. die Sprache C), sollten Anwenderprogramme heute nicht mehr in Assembler geschrieben werden. Eine Ausnahme bilden zeitkritische Abschnitte, die eine so optimal wie irgend möglich
programmiert werden müssen. Dann bietet sich auch eine Lösung in Assembler an.
anwendungsorientierte Sprachen
verfahrensorientierte Sprachen
Assemblersprachen
Mikroprogrammiersprachen
@
¡@
maschinenunabhängige (höhere)
Programmiersprachen
¡
@
¡@
maschinenabhängige
Programmiersprachen
¡
Hardware
Abbildung 4.1: Sprachniveaus
Die höheren Programmiersprachen gehören zur Ebene der problemnahen Sprachen. Sie ermöglichen
eine problemangepaßte, für den Anwender durchschaubare Darstellung, die der Forderung nach Verständlichkeit und Anwenderfreundlichkeit entgegenkommt. Ihre Befehlswörter sind meist der englischen Sprache entnommen, und Ausdrücke für Berechnungen entsprechen der mathematischen Schreibweise. Die
maschinelle Übersetzung der problemnahe formulierten Anweisungen in Maschinenbefehle (sogenannter
Objektcode) durch den Compiler entlastet den Programmierer von dieser mühsamen Aufgabe.
Der vom Compiler erzeugte Maschinencode ist in der Regel weniger effizient als ein gutes Assemblerprogramm.
Dies liegt an den allgemeingültig gehaltenen Übersetzungsalgorithmen. Aufgrund des stetigen Fortschritts beim
Bau“ moderner hochoptimierender Compiler wird sich diese Effizienzlücke jedoch weiter schließen. So exi”
stieren bereits leistungsfähige Compilationssysteme insbesondere für Parallel- und Vektorrechner, die zunächst
Datenabhängigkeiten sowohl innerhalb von Programmodulen als auch global analysieren. Die Berücksichtigung
der Analyse gestattet dann eine Optimierung des Maschinencodes auch über Module hinweg.
Die Verwendung höherer Programmiersprachen hat also folgende Vorteile:
• deutliche Verkürzung der Programmier- und Testzeiten,
4.1. ALLGEMEINES ZU PROGRAMMIERSPRACHEN
3
• geringere Fehleranfälligkeit und bessere Übersichtlichkeit durch problemnahe Formulierung (Selbstdokumentation),
• weitgehende Portabilität auf andere Rechnertypen durch maschinenunabhängige Darstellung der
Algorithmen.
4.1.3
Höhere Programmiersprachen: Besonderheiten, Übersicht und Historisches
Seit Ende der siebziger Jahre haben sich auch im Bereich der Mikrorechner höhere Programmiersprachen aufgrund der im vorstehenden Abschnitt angeführten Vorzüge und der ständig wachsenden Leistungsfähigkeit der Rechner durchgesetzt. Voraussetzung für die Implementierung sind ausreichende
Hardware–Ressourcen, d.h. ein genügend großer Arbeitsspeicher und externe Massenspeicher (Festplatte, Diskette), sowie ein leistungsfähiges Betriebssystem.
Ein großer Teil der höheren Programmiersprachen wurde von den größeren Rechenanlagen übernommen, z.B. FORTRAN, Pascal, COBOL und C. Diese Sprachen sind damit auf fast allen Rechnerfamilien verfügbar. Es gibt verschiedene Möglichkeiten Programmiersprachen in Kategorien einzuteilen. Eine
häufig benutzte Einordnung hat vier Klassen:
• imperative Programmiersprachen
Dazu gehören z.B. Pascal, Modula, C, Fortran.
• funktionale Programmiersprachen Dazu gehören z.B. Opal, Gopher.
• logische Programmiersprachen
• objekt–orientierte Programmiersprachen Objektorientierung ist eine relativ neue Entwicklung. Zu
diesen Sprachen gehören: C++, Smalltalk, Java.
Betrachtet man Programmiersprachen nach formalen Gesichtspunkten, so sind ihre Definition sowie ihre
Sprachelemente wichtig.
Eine Programmiersprache wird definiert durch folgende Informationen:
• Die Syntax: sie wird entweder nur verbal beschrieben (FORTRAN) oder auch formal angegeben
(z.B. Syntaxdiagramme (Backus–Naur–Form) für Pascal),
• Das Vokabular: reservierte Schlüsselworte wie DO und SUBROUTINE sowie benutzerdefinierte
symbolische Namen, die in der Sprache nach bestimmten Regeln gebildet werden, z.B. FORTRAN–
Variablennamen,
• Der Zeichensatz: z.B. FORTRAN: 26 alphabetische, 10 numerische, 22 Sonderzeichen.
Die Leistungsfähigkeit einer Programmiersprache wird bestimmt durch die Mächtigkeit ihres Befehlssatzes
und die Flexibilität ihrer elementaren Bestandteile (Konstrukte). Programmiersprachen setzen sich
aus folgenden Elementen zusammen [c’t 1/89].
• Kontrollstrukturen
– Entscheidungskonstrukte für Fallunterscheidungen wie die bedingte Anweisung IF–THEN
oder die Auswahlanweisung CASE.
– Schleifen wie die DO–, WHILE–, REPEAT– oder FOR–Anweisungen.
4
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
– Blöcke im Rahmen von Schleifen und Auswahlanweisungen oder als abgeschlossene Unterprogramme.
– Rekursionen, wenn Blöcke sich wechselseitig oder selbst aufrufen.
– Sprünge
• Datentypen
– Felder (sequentielle Folgen von Elementen eines Typs, die über einen Index direkt adressiert
werden können).
– Mengen (in Programmiersprachen nur selten vorhanden)
– Zeiger, zum Aufbau dynamischer, sich während des Programmlaufs verändernder Datenstrukturen, dienen als symbolische Bezeichnung der Speicheradresse eines Objekts.
– Initialisierung von Datenobjekten innerhalb einer Deklaration zur Vermeidung von Fehlern beim Arbeiten mit nichtdefinierten Daten.
– Erweiterbarkeit der vorgegebenen Typen durch selbstdefinierte Typen.
• Parameter
Parameter vermeiden Seiteneffekte in Unterprogrammen. Programmeinheiten sollen möglichst nur
über ihre eigenen lokalen Datenstrukturen und die als Parameter übergebenen Daten oder Algorithmen verfügen. Anzustreben ist die Definition von Eingangs-, Ausgangs-, sowie Ein-/Ausgangsparametern. Eingangsparameter reichen nur einen Wert in die Routine hinein, Ausgangsparameter
sind Funktionsresultate. Ein-/Ausgangsparameter reichen einen Wert in die Routine hinein und
erscheinen dort als lokale Variable, deren letzter Wert wieder zurückgegeben wird.
• Module
Module sind ganz allgemein Sammlungen von Datenobjekten, Datentypen, Routinen, Prozessen und
Anweisungen und stellen in der Regel die Lösung einer Teilaufgabe innerhalb des Gesamtprogramms
dar. Module dienen damit der Strukturierung und Abstraktion des globalen Programmflusses. Für
häufig auftretende Standardprobleme gibt es meist fertige, in Modulen zusammengefaßte Lösungen
(Bibliotheken), so daß man als Programmierer nicht jedesmal das Rad neu erfinden muß“.
”
• Fehlerbehandlung
Als Ausnahme (engl. exception) wird die Behandlung eines Fehlers, eines unerwarteten Ereignisses
oder Zustandes bezeichnet. Auslöser können sowohl Hardware- als auch Softwarefehler sein.
Nach diesen mehr formalen Gesichtspunkten wollen wir nun einen kurzen Blick auf die Historie der
gängigsten höheren Programmiersprachen werfen und eine kurze Charakterisierung geben [2][1/89], [10].
FORTRAN
Die älteste höhere Programmiersprache ist FORTRAN (FORmula TRANslator), eine Compilersprache.
Sie wurde 1953 bis 1956 entwickelt für die numerische Behandlung naturwissenschaftlicher und technischer
Probleme in herkömmlicher Formelsprache. Die einfache Sprachgestaltung, effiziente Übersetzbarkeit der
Programme und hohe Rechengeschwindigkeit des Objektcodes sorgten für die rasche Verbreitung der
Sprache, und es entstand ein immenser Fundus an leistungsfähigen Programmen vor allem im technisch–
wissenschaftlichen Bereich. FORTRAN hat sich in weiterentwickelten standardisierten (genormten) Versionen (Fortran90 und Fortran95) bis zum heutigen Tag auch in der Mikrocomputertechnik behauptet.
Darüberhinaus existiert ein High Performance Fortran (HPF), welches insbesondere auf Hochleistungscomputer wie Vielprozessorrechner zugeschnitten ist und die massiv parallele Programmverarbeitung
durch rechnerunabhängige Sprachelemente hierfür unterstützen soll.
ALGOL
4.1. ALLGEMEINES ZU PROGRAMMIERSPRACHEN
5
Für das gleiche Anwendungsgebiet wie FORTRAN wurde 1958 bis 1960 die Sprache ALGOL (ALGOrithmic Language), ebenfalls eine Compilersprache, konstruiert. Mit ALGOL wurde erstmals eine Programmiersprache nach informationstheoretischen Gesichtspunkten auf der Basis einer formalen Grammatik
entwickelt. ALGOL verfügte gegenüber FORTRAN schon damals über erweiterte Ausdrucksmittel, wie
dynamische Speicherverwaltung, Elemente der strukturierten Programmierung und Blockstruktur der
Programme. Die Sprache zeichnet sich durch sprachliche Geschlossenheit und Transparenz aus und hat
die weitere Sprachentwicklung, vor allem PL/1 und Pascal, beeinflußt, und wurde schließlich von diesen
Sprachen verdrängt.
Pascal
Die Sprache Pascal (benannt nach dem Mathematiker Blaise Pascal) wurde 1970 von N. Wirth als Lehrsprache zur Beschreibung und Programmierung von Algorithmen entwickelt. Die Compilersprache Pascal
besitzt eine genau definierte Struktur, unterstützt konsequent die strukturierte Programmierung und erlaubt die Definition auch von nicht-numerischen Datentypen und Mengen und deren Zusammenfassung zu
noch komplexeren Daten-Strukturen. Das Zeigerkonzept (symbolische Bezeichnung von Speicheradressen)
erlaubt sogar die dynamische Erzeugung von Datenstrukturen zur Laufzeit. Die Sprache hat vor allem
auf Klein- und Mikrorechnern (CP/M– und MS–DOS–Rechner) Anwendung gefunden und liegt neben
einer genormten Standardversion ISO 7185–1982 in einer Vielzahl von Dialekten vor (z.B. der de–facto–
Standard Turbo–Pascal). Pascal hat maßgeblich die Sprachen MODULA–2 und ADA beeinflußt.
COBOL
COBOL (COmmon Business Oriented Language) wurde Ende der fünfziger Jahre im Auftrag des USVerteidigungsministeriums entwickelt. Im Jahr 1960 wurde vom Committee of the Conference on Data
Systems Languages (CODASYL) die erste Version veröffentlicht. Die Compilersprache COBOL ist auf
die Anforderungen im kaufmännischen Bereich zugeschnitten. Sie verfügt über eine Dezimalarithmetik (BCD, siehe Kapitel 5) und unterstützt den Umgang mit großen Datenmengen. Ihre ausführliche
Syntax liest sich fast wie Klartext. Ab Anfang der sechziger Jahre wurde COBOL zunehmend in der
kaufmännisch/kommerziellen Datenverarbeitung verwendet und ist dort heute die meistbenutzte Sprache. Sie ist auch auf Mikrorechnern implementiert.
BASIC
Die gegenwärtig auf Klein- und Mikrorechnern am weitesten verbreitete Interpretersprache ist BASIC
(Beginners All–Purpose Symbolic Instruction Code), von der es auch Compilerversionen gibt. BASIC ist
eine sehr einfache Sprache und wurde 1964 von John Kemeny und Thomas Kurtz als Lehrsprache für
Anfänger auf Kleinrechnern entwickelt. Sie ist wegen des beschränkten Befehlsumfangs leicht erlernbar
und eignet sich sowohl für kaufmännische Verwaltungsaufgaben als auch für technisch–wissenschaftliche
Anwendungen. Zur Behebung seiner ursprünglichen Beschränkungen ist BASIC wie FORTRAN auch
ständig weiterentwickelt worden. Zusammen mit den preiswerten Homecomputern verbreitete sich BASIC
in einer Fülle von Dialekten, die jedoch im Gegensatz zu FORTRAN stark vom verwendeten Rechner
abhängen.
C
Modern und in zunehmender Verbreitung begriffen ist die Programmiersprache C, die 1972 im Hinblick
auf das Betriebssystem UNIX entwickelt und 1973 erstmals implementiert worden ist. Ken Thompson
entwickelte unter Anlehnung an die Programmiersprache BCPL (1967 von Richards zum Compilerbau
entwickelt) und mit der Systementwicklern eigenen Vorliebe für kurze, knappe Bezeichner zunächst die
Sprache B. Da B kein Typenkonzept hatte, erwies sie sich als zu wenig mächtig. Dennis Ritchie modifizierte deshalb diese Programmiersprache, und als Weiterentwicklung entstand C. Heute sind UNIX selbst (zu
ca. 90%) und viele seiner Dienstprogramme in C geschrieben. Stärke und Schwäche zugleich ist die Maschinennähe, die dem Programmierer sehr viele Freiheiten läßt und C in die Nähe von Assemblersprachen
rückt. Dank standardisierter Bibliotheken sind C–Programme gut portierbar. Die Sprache liegt in zwei
Standards vor: dem von den meisten C–Compilern unterstützten Pseudo-Standard, wie er ursprünglich
von Kernighan und Ritchie in der ersten Ausgabe von The C Programming Language 1977 veröffentlicht
6
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
wurde, und dem 1989 veröffentlichten ANSI–Standard X3.159-1989. Die Syntax ist sehr kompakt und
für den Ungeübten nicht leicht nachvollziehbar (siehe z.B. der Obfuscated C Contest). C übernahm von
Pascal die definierbaren Datentypen, die lokalen Variablen und alle Möglichkeiten zur strukturierten Programmierung. Unterprogramme können jedoch nicht geschachtelt werden (!). Die Möglichkeit, Variablen
bestimmten CPU–Registern fest zuweisen zu können und die maschinennahen Operatoren, wie etwa zum
Inkrementieren und Dekrementieren von Variablen machen C-Programme vergleichsweise schnell in der
Ausführung. C wurde im Hinblick auf objektorientierte Programmierung zu C++ weiterentwickelt.
LISP
Nahezu gleichzeitig mit FORTRAN entwickelte John Mc Carthy am MIT (Massachusetts Institute of
Technology) die Grundidee von LISP (LISt Processer). 1959 gelang es, LISP auf einem Rechner zu
implementieren. LISP ist eine Sprache zur Lösung nichtnumerischer Probleme. Sie eignet sich hervorragend zum Be- und Verarbeiten von Zeichenketten. Gleichzeitig ist LISP eine offene Sprache, d.h. ihr
Sprachumfang läßt sich ständig erweitern, was die Entwicklung zahlreicher unterschiedlicher Dialekte
förderte und Standardisierungsversuche lange Zeit scheitern ließ. Erst 1992 gelang die Fertigstellung des
ANSI–Standards Common LISP. LISP gehört zu den Sprachen der Künstlichen Intelligenz (KI), als deren
typische Eigenschaft gilt, einzelne Objekte weitgehend abstrakt beschreiben zu können. Dadurch lassen
sich diese in Struktur und Bezeichnung den ’realen’ Objekten aus der Aufgabe und dem Einsatzgebiet
der Software nachempfinden. LISP ist eine listenverarbeitende Sprache. Listen, zu denen auch die LISP–
Programme selbst gehören, stellen eine Aufzählung von Atomen (Zahlen oder Zeichenfolgen) dar, mit
der beliebige komplexe Datenstrukturen erzeugt werden können. Die Programme bestehen nicht, wie
bei prozeduralen Sprachen, aus einer Aneinanderreihung von Befehlen, sondern aus Funktionen, die auf
Argumente (Atome, Funktionsausdrücke oder andere Listen) angewendet werden. Diese Funktionsapplikation wertet der LISP-Interpreter aus und gibt das Ergebnis zurück. Dieses Sprachkonzept eignet sich
insbesondere für rekursive Programmierung. Wegen seiner Andersartigkeit und dem häufigen Einsatz in
der KI genießt LISP ein hohes Ansehen.
4.2. FORTRAN
7
FORTRAN
BASIC
ALGOL 60
COBOL
ALGOL 68
PL/1
LOGO
Pascal
BCPL
SIMULA
PROLOG
ADA
PEARL
C
SmallTalk
APL
Modula-2
LISP
C++
OBERON
Z
E
I
T
E ¦
E¦
E¦
Abbildung 4.2: Der Stammbaum der Programmiersprachen [c’t 1/89]
Betrachtet man den Stammbaum der Programmiersprachen, Abb. 4.2, so kann man erkennen, daß sich
mehrere Entwicklungslinien herausgebildet haben, innerhalb derer eine Gruppierung in eine erste (bis
1970) und eine zweite Sprachgeneration (nach 1970) vorgenommen werden kann. Während die erste Generation, wie FORTRAN66 und BASIC, noch durch zeilenorientierte Anweisungen, einfache Datentypen
(und Felder), arithmetische und logische Operationen gekennzeichnet sind, verfügt die zweite Sprachgeneration u.a. über strukturierte Datentypen und entsprechende Operatoren sowie Kontrollanweisungen,
die den Prinzipien der strukturierten Programmierung entsprechen. Dies trifft insbesondere auf Pascal
und seine Abkömmlinge zu. Typisch für die Entwicklung nach 1975 sind die Bemühungen, die Sprachen
der ersten Generation auf das Niveau der zweiten Sprachgeneration zu bringen, z.B. mit der Entwicklung
von FORTRAN77 bis zum neuen Standard Fortran95.
Wir werden uns im folgenden der Sprache FORTRAN widmen, da sie im Ingenieurbereich (Aerodynamik, Thermofluiddynamik, Mechanik, usw.) die am weitesten verbreitete Sprache ist. Für viele (nicht
nur Ingenieurs-) Probleme liegen teils umfangreiche FORTRAN–Programmsysteme und Unterprogrammsammlungen zur Lösung vor, die nicht etwa eingemottet sind, sondern die benutzt und gepflegt werden.
FORTRAN ist eine lebendige Sprache, die sich ständig weiterentwickelt; sie bietet zur Zeit die beste
Unterstützung zur effizienten Ausnutzung spezieller Hardwareeigenschaften von Supercomputern (z.B.
Vektor– und Pipeline–Rechnern).
4.2
FORTRAN
Von der Mitte der fünfziger Jahre entwickelten ersten höheren Programmiersprache FORTRAN war 1958
eine überarbeitete und erweiterte Version FORTRAN II verfügbar, die u.a. auch die Unterprogrammtechnik zuließ. FORTRAN IV erschien 1962 als Quasi-Standard und blieb über ein Jahrzehnt die am
weitesten verbreitete FORTRAN–Version. Zur Eindämmung der Entwicklung unterschiedlichster Ver-
8
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
sionen wurde 1966 der Sprachstandard FORTRAN66 verabschiedet, der weitgehend mit FORTRAN IV
übereinstimmt. FORTRAN77 besaß gegenüber seinem Vorläufer bereits Erweiterungen zur Unterstützung
strukturierten Programmierens sowie Verbesserungen in der Dateiverwaltung und bei der Verarbeitung
von Zeichenketten. Nach FORTRAN77 und dem schon wesentlich weiterentwickelten Fortran90, liegt
nun mit Fortran95 ein neuer Fortran–Standard vor, der unter der DIN–Norm ISO/IEC 1539-1 bekannt
ist. Die Entwicklung der Programmiersprache FORTRAN wird damit aber sicher noch nicht am Ende
angelangt sein, da bereits Fortran2000 in den Startlöcher steht und um weitere Fähigkeiten, wie z.B. die
Objektorientierte–Programmierung erweitert sein wird.
Die im folgenden erläuterten Sprachelemente sind alle Bestandteil des Standards Fortran95. Dieses Skript
soll nur einen Überblick über die wichtigsten Elemente des Sprachstandards liefern und Hinweise zur
sprachlichen Umsetzung der Struktogrammelemente liefern; es kann damit ein vollständiges FORTRAN–
Handbuch oder Lehrbuch nicht ersetzen. Zur erschöpfenden Beschreibung des vollen Sprachstandards sei
daher auf die zahlreiche Literatur zu FORTRAN verwiesen (z.B. [RRZN, . . . ]).
4.2.1
Einfache FORTRAN–Programmbeispiele
Die folgenden Programmbeispiele sind gedacht als erstes Anschauungsmaterial. Die Funktion der Programmzeilen wird nur grob erläutert, für die genaue Beschreibung der einzelnen Anweisungen wird auf
die folgenden Abschnitte dieses Kapitels verwiesen.
Zum Einstieg sehen wir uns unser erstes, sehr einfaches FORTRAN–Programm an. Es ist die FORTRAN–
Version des bekannten Hello world“–Programms:
”
!
! Mein erstes FORTRAN-Programm
!
PROGRAM HALLO
PRINT *, ’Hallo, Du da!’
END
Nach drei als Kommentar markierten Zeilen (erkennbar am Zeichen ’!’), die für die Funktion des Programms keinerlei Bedeutung haben, folgen die eigentlichen Programmanweisungen: Vereinbarung eines
Programmnamens (HALLO), Ausgabe der Zeichenkette Hallo, Du da!“ auf dem Bildschirm und schließlich
”
die Programmende-Anweisung.
Die genaue Form eines FORTRAN–Programms ist festgelegt; Zitat DIN: Ein Programm ist eine zur
”
Lösung einer Aufgabe vollständige Anweisung mit allen erforderlichen Vereinbarungen.“ Ein FORTRAN–
Programm besteht also aus einer Folge von ausführbaren und nichtausführbaren Anweisungen. Ausführbare Anweisungen bewirken Aktionen des Programms. Die Vereinbarungen sind nichtausführbare Anweisungen, sie enthalten Angaben für den Compiler oder das Laufzeitsystem, z.B. über die Eigenschaften
oder die Art der Konvertierung von Daten. Ein weiteres einfaches Beispiel soll dies genauer zeigen. Das
Programm soll zwei über die Tastatur einzugebende Zahlen lesen, deren Summe berechnen und auf dem
Bildschirm ausgeben.
!
! Ein einfaches FORTRAN-Programm
!
PROGRAM addier
INTEGER :: zahl1, zahl2, summe
READ *, zahl1, zahl2
summe = zahl1 + zahl2
PRINT *, summe
END
4.2. FORTRAN
9
Nach wiederum drei Kommentarzeilen erfolgt die Vereinbarung eines Programmnamens (addier), dann
werden die Namen der Variablen zahl1, zahl2 und summe vereinbart und die Variablen werden als vom
Typ Ganze Zahl definiert. Die READ–Anweiung liest die beiden Zahlen von der Tastatur ein, dann wird
deren Summe berechnet, auf dem Bildschirm ausgegeben und das Programm beendet.
Abschließend hier nun ein weiteres Programm–Beispiel, das einerseits durch Kommentare und sinnvolle
Variablennamen recht gut selbstdokumentierend ist, andererseits durch Bildschirm-Meldungen gut mit
dem Benutzer kommuniziert. Es soll aus einem über die Tastatur einzugebenden Wert des Radius die
Fläche des entsprechenden Kreises berechnen. Dieses Beispiel demonstriert auch gut das EVA“–Schema
”
(Eingabe, Verarbeitung, Ausgabe).
!
! Ein FORTRAN-Programm mit allen
! Grundelementen
! -- PROGRAM-Anweisung
PROGRAM circle
! Vereinbarungsteil
REAL
:: radius, flaech
REAL, PARAMETER :: PI = 3.14159
INTEGER
:: wert, n
! Ende des Vereinbarungsteils
! Meldung auf Bildschirm, Einlesen Radius
PRINT *, ’Programm zur Kreisflaechenberechnung’
PRINT *, ’Bitte den Radius eingeben!’
READ *, radius
! Ende des Einlesens
! Es folgen die Anweisungen und Berechnungen
n = 2
flaech = PI * radius ** n
wert
= PI * radius ** n
! Ausgabe der Ergebnisse
PRINT *, ’Radius =’, radius
PRINT *, ’Flaeche =’, flaech
PRINT *, ’WERT
=’, wert
! -- Programm-Ende
END
In der ersten Anweisung nach den einleitenden Kommentarzeilen wird wieder der Programmname circle
vereinbart. Im Vereinbarungsteil werden die Variablen radius, flaech und PI als reelle Variablen, wert
und n als ganzzahlige Variablen festgelegt. Das zusätzliche Attribut PARAMETER vereinbart PI als Konstante (Unveränderliche). Bei der Deklaration von PI findet hier auch gleichzeitig eine Initialisierung
(Belegung mit einem Anfangswert) statt, indem man ihr mit Hilfe des Zeichens ’=’ den Wert 3.14159
zuweist. Im ausführbaren Teil wird nach einer Bildschirm–Meldung der Benutzer aufgefordert, den Radius des Keises einzugeben, der dann von der Tastatur eingelesen wird. Im Berechnungsteil erhält n
den Wert 2, danach wird die Kreis–Fläche πr2 berechnet und das Ergebnis den Variablen flaech und
wert zugewiesen. Im Ausgabeteil werden der Radius und die beiden Werte für die Fläche zusammen
mit einer Erläuterung auf dem Bildschirm ausgegeben. Schließlich wird das Programm beendet. Eine
Ergebnisausgabe könnte beispielsweise so aussehen:
10
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Radius = 1.000000
Flaeche = 3.141590
WERT
= 3
Die Erklärung für die unterschiedlichen Werte von flaech und wert wird im Abschnitt 4.2.5 Arithme”
tische Ausdrücke und Anweisungen“ gegeben.
4.2.2
Sprachelemente von Fortran95
Für ein korrektes FORTRAN–Programm müssen alle Anweisungen einer Reihe von eindeutig definierten
Regeln (Syntax) genügen. Zunächst beschreiben wir die Grundelemente der Sprache, aus denen einzelne
FORTRAN–Anweisungen zusammengesetzt werden können.
4.2.2.1
Zeichensatz
Anweisungen werden aus den Zeichen des FORTRAN–Zeichensatzes gebildet:
• Alphanumerische Zeichen (Buchstaben A bis Z und Ziffern 0 bis 9)
• Sonderzeichen + - : =
* / ( ) , . ’ ! " % & ; < > ? $ _ und das Leerzeichen
Zwischen Groß- und Kleinschreibung wird nicht unterschieden.
4.2.2.2
FORTRAN–Quelltextzeile
Eine FORTRAN–Zeile kann maximal 132 Zeichen lang sein. Innerhalb dieser 132 Zeichen dürfen Kommentare, Anweisungen und Leerzeichen an beliebiger Position stehen. Kommentare dienen nur zum
besseren Verständnis eines Programmes und haben keinen Einfluß auf das Programm. Ein Kommentar
wird mit dem Ausrufezeichen ! eingeleitet, wobei diese auch hinter den eigentlichen Anweisungen beginnen können. Leerzeichen können und sollten ebenso zur besseren Lesbarkeit eingefügt werden, haben
aber auch keinerlei Auswirkungen auf das Programm.
Bei mehr als einer Anweisung pro Zeile müssen diese durch ein Semikolon voneinander getrennt werden.
Aus Übersichtlichkeitgründen sollte man dieses aber auf kurze Anweisungen beschränken oder gleich ganz
vermeiden.
a = 1. ; b = 2.
! 2 Anweisungen
Anweisungen können auch in der nachfolgenden Zeile fortgesetzt werden. Dazu wird das &–Zeichen verwendet, welches man an das Ende der zu verlängernden Zeile anhängt. Dadurch wird die aktuelle und
die nachfolgende Zeile als eine Befehlszeile interpretiert. Eine Anweisung darf sich aber maximal über 40
Anweisungzeilen erstrecken.
kreisflaeche = radius * radius &
* 3.14159
Verwendet man das Fortsetzungszeichen auch am Anfang der Folgezeile, wird die Zeile erst ab dem zweiten
&–Zeichen fortgesetzt.
PRINT *, ’Hello&
& World’
! Liefert bei der Ausgabe:
! ’Hello World’
4.2. FORTRAN
11
Anweisungsmarken (Label) dürfen nur zu Beginn einer Anweisungszeile vor der eigentlichen Anweisung stehen. Anweisungsmarken sind ein- bis fünfstellige positive Zahlen und dienen dazu, um auf diese
Anweisung Bezug nehmen zu können, wie es beispielsweise für die FORMAT–Anweisung wichtig ist. Sie
können innerhalb einer Programmeinheit nur einmal vergeben werden, ihre numerische Reihenfolge ist
dabei beliebig und sie sind außerhalb dieser Programmeinheit unbekannt.
1430 FORMAT(A)
4.2.2.3
! Die FORMAT-Anweisung wird spaeter erklaert.
Symbolische Namen
Symbolische Namen für Programme, Unterprogramme, Variablen, Felder, Funktionen etc. bestehen
aus bis zu 31 Zeichen, wie Buchstaben, Ziffern und/oder dem Unterstrich ”_” , von denen das erste ein
Buchstabe sein muß. Bei der Wahl eines symbolischen Namens muß jedoch darauf geachtet werden, daß
es sich dabei nicht bereits um ein reserviertes Schlüsselwort handelt, wie zum Besipiel PROGRAM, END o.ä.
(Achtung! Verwechselungsgefahr: O und 0 (Buchstabe O und die Null), sowie I, l und 1 (Buchstaben
großes I“, kleines L“ und die Eins“)!)
”
”
”
4.2.2.4
Konstanten
Ein Datenelement mit festem Wert, das während der Programmausführung nicht geändert werden kann,
ist eine Kostante. Eine symbolische Konstante (Konstante mit Namen) wird mit Hilfe des PARAMETER–
Attributs (Abschnitt 4.2.3) definiert. Konstanten können vom Typ ganzzahlig (INTEGER), reell (REAL),
doppelt genau (DOUBLEPRECISION), komplex (COMPLEX), logisch (LOGICAL) oder Zeichenreihe (CHARACTER)
sein. Der Typ einer symbolischen Konstanten wird explizit, implizit oder per Konvention festgelegt, siehe
Abschnitt 4.2.3 Vereinbarungsanweisungen“.
”
Typen von Konstanten:
• ganzzahlig: eine Folge von Ziffern mit Vorzeichen, ohne Dezimalpunkt.
Beispiele:
4711 (auch: +4711)
-131072
• reell: eine Folge von Ziffern mit Dezimalpunkt und optional mit ganzzahligem Exponent zur Basis
10, gekennzeichnet durch E. Diese einfach genauen Werte werden in einem Speicherwort (Mikrocomputer: 4 Byte, 6 bis 7 signifikante Dezimalstellen genau) gespeichert.
Beispiele:
3.14159
-17.04
.5
12.E1
-.0094E+4
3.141E3
-256.E-2
Wert:
Wert:
Wert:
Wert:
12. ∗ 101 = 120.
−0.0094 ∗ 104 = −94.
3.141 ∗ 103 = 3141.
−256. ∗ 10−2 = −2.56
• doppelt genau: wie reell, der Exponent wird mit D gekennzeichnet. Doppelt genaue Werte werden
intern in zwei Speicherworten gespeichert (Mikrocomputer: 8 Byte, 13 bis 14 signifikante Dezimalstellen genau), wodurch sich eine höhere Genauigkeit ergibt.
Beispiele:
12.D1
-256.D-2
Wert: 12. ∗ 101 = 120.
Wert: −256. ∗ 10−2 = −2.56
12
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
• komplex: ein Paar reeller oder ganzzahliger Konstanten, durch Komma getrennt und in Klammern
eingeschlossen. Komplexe Werte werden intern in zwei aufeinanderfolgenden Speicherworten als
reelle Zahlenwerte abgespeichert.
Beispiele:
(3, 9.9)
(-6.4E-1, 2)
Wert: 3. + 9.9i (i2 = −1)
Wert: −0.64 + 2.0i
• logisch: es gibt die zwei Konstanten .TRUE. (wahr) und .FALSE. (falsch). Die Punkte sind zwingender Bestandteil.
Beispiele:
.TRUE.
.FALSE.
• Zeichenreihe: eine Reihe von Zeichen (mindestens eines), auch CHARACTER–String genannt, in
Apostrophe eingeschlossen. Leerzeichen sind signifikant. Die Länge der Zeichenreihe ist die Anzahl
der Zeichen zwischen den Apostrophen. Sie ist wichtig für Operationen mit der Zeichenreihe.
Beispiele:
4.2.2.5
’HFI’
’NULL8FUFF10’
’NOCH EIN BEISPIEL’
Länge: 3
Länge: 11
Länge: 17
Variablen
Eine Variable bezeichnet eine Größe, die im Lauf der Rechnung verschiedene Werte annehmen kann.
Die Variable wird durch ihren symbolischen Namen identifiziert, ihr wird vom Compiler ein Speicherplatz
im Arbeitsspeicher zugeordnet. Bei Benutzung der Variablen in einer Anweisung verfügt man über den
Inhalt ihres Speicherplatzes. Vor der ersten Verwendung muß die Variable definiert (initialisiert) werden.
Der Typ einer Variablen wird analog zum Typ von Konstanten explizit, implizit oder per Konvention
festgelegt, siehe Abschnitt 4.2.3 Vereinbarungsanweisungen“. Ansonsten gilt das bei Konstanten Gesagte.
”
4.2.2.6
Anordnungsreihenfolge von Anweisungen
Die zulässige Anordnung von FORTRAN–Anweisungen innerhalb eines Programms sei an dieser Stelle schon angeführt, Abb. 4.3, auch wenn die meisten FORTRAN–Anweisungen erst später erklärt werden. Ein FORTRAN–Programm besteht aus einem Hauptprogramm und einem oder mehreren (Funktions–) Unterprogrammen. Die PROGRAM–Anweisung darf nur als erste Anweisung in einem
Hauptprogramm stehen. Die erste Anweisung einer Subroutine oder einer Funktion ist entsprechend eine
SUBROUTINE– oder eine FUNCTION–Anweisung. Zwischen der CONTAINS– und der END–Anweisung können
für sich abgeschlossene interne Unterprogramme eingefügt werden. Fehlen interne Unterprogramme, wird
die CONTAINS–Anweisung selbst auch weggelassen. Die END-Anweisung ist die letzte Anweisung jeder
Programmeinheit. Weiteres hierzu im Abschnitt 4.2.15 Programmeinheiten und Anweisungen“.
”
Der Aufbau eines FORTRAN–Moduls ist in Abb.4.3 beispielhaft angegeben.
Die Reihenfolge von nebeneinanderstehenden Anweisungen ist beliebig. Durch horizontale Linien getrennte Anweisungen dürfen nicht miteinander gemischt werden.
4.2.3
Vereinbarungsanweisungen
Vereinbarungen gehören zu den nichtausführbaren Anweisungen und dienen dazu, die Eigenschaften der
im Programm verwendeten Namen (z.B. von Variablen, Unterprogrammen) festzulegen. Vereinbarungen
4.2. FORTRAN
13
PROGRAM–, SUBROUTINE–, FUNCTION–Anweisung
IMPLICIT NONE–Anweisung
IMPLICIT–Anweisungen
INCLUDE–
Zeilen
und
Compiler–
Anweisungen
Kommentar–
und
Leerzeile
FORMAT–
Anweisungen
PARAMETER–
Anweisungen
Typvereinbarungen, wie
REAL
INTEGER
CHARACTER
TYPE–Anweisungen
Formelfunktions–Definitionen
ausführbare Anweisungen, wie
Zuweisungen
READ
WRITE
OPEN
DO
IF () THEN, ...
CONTAINS–Anweisung
interne Unterprogramme
END–Anweisung
Abbildung 4.3: Anordnungsreihenfolge von FORTRAN–Anweisungen
14
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
SUBROUTINE low2UP (ZKette)
!======================================================================
!*Vers: 06.10.1989/WWB HFI-TUB
!---------------------------------------------------------------------!*Zweck: Diese Routine wandelt in der Zeichenkette ZKette enthaltene
!
Kleinbuchstaben in Grossbuchstaben um.
!---------------------------------------------------------------------!*Vars: In/Out: ZKette (Char) - Umzuwandelnde Zeichenkette
!
!
Lokal: LStr
(Int) - Laenge der Zeichenkette
!
NChar (Int) - Positionsnummer des Zeichens in der
!
ASCII-Tabelle
!---------------------------------------------------------------------!*Tip:
Zeichenweise Umwandlung gemaess ASCII-Tabelle (Kap. 5);
!
Kleinbuchstaben liegen zwischen 97 und 122, die entsprechenden
!
Grossbuchstaben liegen um 32 Positionen darunter.
!---------------------------------------------------------------------!*Mods: 30.10.90/WWB Version fuer Info-Tech-VL
!
18.12.01/TFS Umsetzung in Fortran 95
! ======================================================================
!*Vereinbarungsteil
!---------------------------------------------------------------------CHARACTER (LEN=*) :: ZKette
INTEGER
:: LStr, NChar, I
!======================================================================
!*Prozedurrumpf
!---------------------------------------------------------------------! --- Ermittlung der Laenge von ZKette
LStr = LEN (ZKette)
!---------------------------------------------------------------------! --- Zeichenweises Absuchen von ZKette nach Kleinbuchstaben
DO I=1,LStr
NChar = IChar( ZKette(I:I) )
IF (NChar .GE. 97 .AND. NChar .LE. 122) &
ZKette(I:I) = CHAR(NChar-32)
ENDDO
!======================================================================
END SUBROUTINE
Abbildung 4.4: Beispiel für den Aufbau eines FORTRAN–Moduls.
4.2. FORTRAN
15
müssen vor allen DATA–Anweisungen, Formelfunktionsdefinitionen und vor allen ausführbaren Anweisungen einer Programmeinheit stehen.
Es gibt folgende Vereinbarungsanweisungen:
• Typ (INTEGER, REAL, DOUBLE PRECISION, COMPLEX, LOGICAL, CHARACTER)
• IMPLICIT
• COMMON
• EXTERNAL
• INTRINSIC
• SAVE
• [ EQUIVALENCE ]
Hier wird zunächst nur die Typ– sowie eingeschränkt die IMPLICIT–Anweisung behandelt. Die anderen
Vereinbarungsanweisungen folgen später.
4.2.3.1
Typvereinbarungen
Typenvereinbarungen sind notwendig wegen der unterschiedlichen rechnerinternen Darstellungsform beispielsweise von ganzen und reellen Zahlen (binäre Darstellung im Arbeitsspeicher). Der Datentyp von
Variablen und Konstanten wird mit Typ- und IMPLICIT–Anweisungen bestimmt.
Wenn weder Typ– noch IMPLICIT–Anweisungen verwendet werden, findet eine Standard–Typenzuordnung
nach der Typenkonvention statt: tritt der Name nicht in einer expliziten oder impliziten Typanweisung
auf, dann ist die Variable vom ganzzahligen Typ (INTEGER), wenn der Name mit einem der Buchstaben
I, J, K, L, M, N beginnt, andernfalls ist sie von Typ reell (REAL). Alle anderen Typen müssen also
explizit per Vereinbarung definiert werden. Die explizite Vereinbarung übersteuert dabei die implizite,
die ihrerseits die Vereinbarung nach Konvention übersteuert.
Aus Gründen der Eindeutigkeit, Klarheit und Übersichtlichkeit und zum Schutz vor unliebsamen Fehlern
wird dringend empfohlen, alle Größen explizit per Typanweisung zu definieren. Für die Übungsaufgaben wird diese Empfehlung zur Pflicht. In diesem Zusammenhang findet sich auch die einzige
sinnvolle Verwendung der IMPLICIT–Anweisung: durch die Deklaration
IMPLICIT NONE
in jeder Programmeinheit wird die implizite Variablendeklaration untersagt, und alle Variablen müssen
explizit deklariert werden. Die IMPLICIT–Anweisung wird hier nicht weiter besprochen.
4.2.3.2
Explizite Typvereinbarungen
Bei der expliziten Typanweisung wird der Name in eine Typanweisung aufgenommen. Sie hat die
Form
typ [, attribut]... ::
v_1 , ..., v_n .
Dabei ist typ die Typbezeichnungen, und v_1, ..., v_n sind die Namen der Datenobjekte, deren Typ
festgelegt werden soll. Mit attribut kann man zusätzlich optionale Angaben zu deren Eigenschaften
machen, wie schon z.B. im Programm circle mit Parameter PI gezeigt.
Typen von Datenobjekten:
16
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
• ganzzahlig:
INTEGER
• reell:
REAL
• doppelt genau:
DOUBLE PRECISION
• komplex:
COMPLEX
Beispiele:
INTEGER :: freddy, bud, alf
REAL
:: ha, haha, hihi, komik
DOUBLE PRECISION :: viel, genauere, zahlen
COMPLEX :: z0, phase
Diese vier Typanweisungen dienen zur Deklaration von arithmetischen Datenobjekten. So legt z.B.
REAL fest, daß die Namen ha, haha, hihi und komik vom Typ reell sind, sich dahinter also reelle
Zahlen verbergen.
• logisch:
Beispiel:
LOGICAL
LOGICAL :: wahl, korrekt, ablauf
LOGICAL–Variablen können dagegen nur die Wahrheitswerte beiden .TRUE. und .FALSE. enthalten.
• Zeichenreihen:
Beispiele:
CHARACTER
CHARACTER (LEN=20) :: zeichenkette, dateiname
CHARACTER *25
:: buchstaben, text*15, zeile
Bei der Typ–Vereinbarung CHARACTER muß zusätzlich die Länge der Zeichenkette vereinbart werden. Dies geschieht seit der Fortran95–Norm mit der (LEN=..)–Angabe. Diese bezieht sich dann
auf alle in der Anweisung angegebenen Variablen und legt in unserem Beispiel für die Variablen
zeichenkette und dateiname eine Länge von jeweils 20 Zeichen fest. Eine ältere Form der Längen–
Angabe wird mittels *laenge vorgenommen. Dabei wird hier mit *15 hinter text dessen Länge mit
15 Zeichen vereinbart. Für alle anderen in der Anweisung aufgeführten Namen ( buchstaben und
zeile) gilt die Länge, die unmittelbar hinter dem Schlüsselwort CHARACTER steht (hier 25 Zeichen).
Prinzipiell sind beide Formen in der Fortran95–Norm enthalten. Die zweite soll jedoch in nachfolgenden Fortran–Normen durch die Angabe mit (LEN=..) abgelöst werden.
Macht man jedoch bzgl. der Länge gar keine Angaben, so wird vom Compiler die Länge 1 festgelegt!
Attribute von Datenobjekten:
• Konstanten:
PARAMETER
Mit der Vergabe des PARAMETER–Attributs kann man für eine Konstante einen symbolischen Namen vereinbaren ( symbolische Konstante“):
”
< typ >, PARAMETER :: p_1 = e_1, p_2 = e_2, ...
Darin sind pi symbolische Namen, und ei sind Konstanten oder Konstantenausdrücke, die ihrerseits nur Konstanten und bereits vorher definierte symbolische Konstanten (also insbesondere keine
Funktionsaufrufe) enthalten dürfen. Der Wert einer Konstanten kann im gesamten Programm nicht
mehr verändert werden.
4.2. FORTRAN
17
Beispiel:
INTEGER, PARAMETER :: G = 11, G2 = 2*G, G5 = 5*G+1
REAL :: FELD1 (G,G2), FELD2 (G5)
:
X1 = DELTAX/G
Man kann die Vereinbarung einer Konstante aber auch als eigenständige Anweisung schreiben:
PARAMETER (p_1 = e_1 , p_2 = e_2 , ...)
In diesem Falle darf diese aber erst nach der Vereinbarung der symbolischen Namen pi , – sonst aber
an beliebiger Stelle in den Vereinbarungs–Anweisungen stehen (Abb.4.3).
• Felder:
DIMENSION
Das DIMENSION–Attribut wird verwendet, um eine Variable als Feld (indizierte Variable, array) zu
vereinbaren. Ein Feld ist eine spezielle Datenstruktur, bei der alle Elemente der Struktur durch
einen einzigen symbolischen Namen gekennzeichnet, und diese Elemente alle vom gleichen Typ
sind. Neben dem DIMENSION–Attributes gibt es noch zwei weitere Möglichkeiten solch ein Feld
zu deklarieren. Die eine ist die, der direkten Angabe und eine weitere, die der Verwendung der
DIMENSION–Anweisung:
Die Beispiele
mit DIMENSION–Attribut:
INTEGER, DIMENSION (3)
REAL, DIMENSION (4:8)
:: a
:: xfeld
(eindimensional, 3 Elemente)
(eindimensional, 5 Elemente)
oder mit direkter Angabe:
REAL
:: werte (-10:10, 0:20)
COMPLEX :: s17u4 (100, -17:4, 3)
(zweidimensional, 21*21 Elemente)
(dreidimensional, 100*22*3 Elemente)
oder mittels DIMENSION–Anweisung:
INTEGER
:: n
DIMENSION :: n (16,2,10:15)
(dreidimensional, 16*2*6 Elemente)
zeigen die Syntax der Feldvereinbarungen. Die erste der beispielhaft gezeigten Feldvereinbarungen
richtet drei Speicherplätze für ganze Zahlen ein, die mit a(1), a(2) und a(3) angesprochen werden können. Die untere und die obere Grenze einer Dimension (eines Indexes) können beliebige
ganzzahlige Ausdrücke sein und werden durch einen Doppelpunkt getrennt. Fehlt die untere Grenze, wird der Wert 1 angenommen. Die obere Grenze muß größer gleich der unteren Grenze sein. Wir
werden später sehen (Abschnitt 4.2.19), daß in Unterprogrammen auch Felder vereinbart werden
können, deren Dimensionen durch Variablen gegeben sind oder diese auch direkt vom aufrufenden
Unterprogramm übernommen werden.
Es sind maximal sieben Dimensionen möglich. Die Länge eines Feldes ist gleich der Anzahl seiner
Elemente. Weitere Informationen über Felder, insbesondere zu Dimensionierung, Form der Indizes
und Speicherung, werden im Abschnitt 4.2.3.3 Felder“ gegeben.
”
• dynamische Felder:
ALLOCATABLE
Dynamische Felder sind Felder, deren Element–Anzahl erst im Laufe des Programmes festgelegt
werden können. Das ALLOCATABLE–Attribut kann also nur zusammen mit z.B. dem DIMENSION–
Attribut auftreten. Dabei muß die Anzahl der Dimensionen bereits bei der Deklaration vorgegeben
werden. Wir werden darauf genauer im Kapitel 4.2.3.5 eingehen.
18
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
• gesicherte Variablen: SAVE
Das Attribut SAVE ist nur bei der Verwendung von Unterprogrammen interessant. Der FORTRAN–
Standard besagt, daß beim Rücksprung in das rufende Programm alle lokalen Größen nicht mehr
definiert sind.
Das SAVE–Attribut sorgt dafür, daß der Wert der Variablen erhalten bleibt, wenn das Unterprogramm bei Ausführung der END–Anweisung wieder verlassen wird. Dieses sollte jedoch mit Vorsicht
behandelt werden!
• Zeiger:
POINTER
Mit dem POINTER–Attribut wird eine ganz besondere Art von Variablen deklariert. Diese dienen
nicht – wie Variablen normalerweise – dazu, in ihnen Daten für zum Beispiel Berechnungen abzulegen, sondern bieten dem Programmierer die Möglichkeit, Adressen von anderen Variablen zu
speichern. Mit dem
Beispiel:
INTEGER, POINTER :: zeigtauf
haben wir einen Zeiger zeigtauf deklariert, der die Adresse einer Integervariablen speichern kann.
Man sagt dabei: auf eine Integervariable zeigen kann. Genauer wird dies im Abschnitt 4.2.3.6
erläutert.
• Zeigerziel:
TARGET
Das TARGET–Attribut kann man einer Variablen geben, wenn man diese als Ziel eines Zeigers benutzen möchte. Auch dieses wird genauer im Abschnitt4.2.3.6 erläutert.
4.2.3.3
Felder
Ein Feld (indizierte Variable, Array) ist, wie in Abschnitt 4.2.3.2 bereits angesprochen, eine spezielle Datenstruktur, bei der alle Elemente der Struktur durch einen einzigen symbolischen Namen gekennzeichnet
sind. Ein einzelnes Feldelement wird durch Angabe dieses Namens und eines Indexes angesprochen. Alle
Feldelemente sind von gleichen Datentyp wie das Feld. Er wird wie bei Konstanten und Variablen über
den Namen bestimmt. Feldname und Felddimension müssen in einer Typ– oder COMMON–Anweisung
entweder direkt, mit Hilfe des DIMENSION–Attributes oder der DIMENSION–Anweisung vereinbart werden.
Jedes Feld darf nur einmal in jeder Programmeinheit vereinbart werden.
Die Dimensionierung von Feldern in einer expliziten Typvereinbarung hat die allgemeine Form
< typ >, DIMENSION(d1_1, d1_2, ...) :: array1
bzw.
< typ >:: array1(d1_1, d1_2, ...), array2(d1_2, d2_2, ...)
Darin ist arrayj der Name des j–ten Feldes und dji gibt die Grenzen der i–ten Dimension des j–ten Feldes
an, die in der Form li : ui geschrieben werden kann. Dabei ist ui die obere Grenze der i–ten Dimension
und li ist deren untere Grenze. Wird li weggelassen, gilt li = 1.
Neben den in 4.2.3.2 bespielhaft gezeigten Dimensionsvereinbarungen sind auch andere ganzzahlige konstante Ausdrücke1 für die Indexgrenzen erlaubt, z.B. (siehe auch Abschnitt 4.2.2):
INTEGER, PARAMETER :: N = 11, N2 = 2*N, N5P1 = 5*N + 1
REAL :: XFELD(N2, (N+1)/2), YFELD(N2+3:N5P1)
1 Ausdrücke
werden in Abschnitt 4.2.5 erläutert
4.2. FORTRAN
19
Zur flexiblen Verwendung von Unterprogrammen können übergebene Felder dort auch mit variablen
Dimensionen deklariert werden, sofern die Dimension als Variable übergeben wurde. Man achte dabei
darauf, daß Felder in allen Unterprogrammen stets gleich dimensioniert sind, da sonst insbesondere bei
mehrdimensionalen Feldern schwer zu findende Fehler durch Zugriffe auf falsche Speicherplätze auftreten
können (siehe Abschnitt 4.2.21 Kommunikation zwischen Programmeinheiten“).
”
4.2.3.4
Speicherung von Feldern
Bei der Verwendung von Feldern ist es wichtig, Kenntnis über die Speicherung zu besitzen. Ein Feld
wird in einem kontinuierlichen Block des Arbeitsspeichers abgespeichert. Das erste Feldelement kommt in
das erste Speicherwort. Die Feldelemente werden spaltenweise in aufsteigender Reihenfolge gespeichert,
d.h. der erste Indexwert wird am schnellsten erhöht und der letzte am langsamsten.
Beispiel: Das zweidimensionale Feld A(3,4) wird in der unten angegebenen Weise im Speicher abgelegt:
A(1,1); A(2,1); A(3,1); A(1,2); A(2,2); . . . ; A(2,4); A(3,4). Man kann auf das komplette Feld zugreifen
durch Angabe des Feldnamens; der Zugriff auf einzelne Feldelemente erfordert zusätzlich die Angabe des
Indexes (siehe obiges Beispiel).
?
A(1,1)
?
A(2,1)
?
A(1,2)
6
?
A(3,1)
?
A(2,2)
?
A(3,2)
?
A(1,3)
6
?
A(2,3)
?
A(1,4)
6
?
A(3,3)
?
A(2,4)
?
A(3,4)
?
Beim Zugriff auf Elemente kann der Indexausdruck Funktionsaufrufe und Zugriffe auf Feldelemente enthalten. Der Wert eines Indexausdrucks darf die aktuelle Größe der Dimension nicht überschreiten. Eine
Überprüfung auf Zugriffe außerhalb der Indexgrenzen findet in der Regel nicht statt, kann aber durch
Compileroptionen erzwungen werden. Feldzugriffe mit ungültigem Index bewirken nicht vorhersehbare
Reaktionen und Ergebnisse des Programms!
4.2.3.5
Dynamische Felder
Wie bereits unter 4.2.3.2 erwähnt, ist ein dynamisches Feld ein Feld, deren Feldweiten erst im Laufe
eines Programmes festgelegt zu werden brauchen. Das ALLOCATABLE–Attribut hat also nur Sinn bei einer
Variablen, die zugleich (z.B. mit dem DIMENSION–Attribut) die Eigenschaften eines Feldes erhält. Dabei
muß die Anzahl der Dimensionen bereits bei der Deklaration vorgegeben werden.
Eine Deklaration könnte z.B. wie folgt aussehen:
REAL, ALLOCATABLE, DIMENSION (:, :) :: dynfeld1, dynfeld2(:, :, :)
Für dynfeld1 ist durch das DIMENSION–Attribut also die Dimension 2 vorgegeben, wohingegen für das
dynamische Feld dynfeld2 3 Dimensionen mittels direkter Angabe festgelegt wurden. Die Zuweisung, wieviel Elemente die einzelnen Dimensionen besitzen sollen, braucht erst im Laufe des Programmes erfolgen.
Dazu wid die ALLOCATE–Anweisung verwendet:
20
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
ALLOCATE (feld1(dim,[dim]...), feld2(dim,[dim]...)[, STAT = statusvariable])
Der Parameter STAT dient dazu, um die korrekte Abbarbeitung der Anweisung zu überprüfen. Im Fehlerfall wird der angegebenen INTEGER–Variablen (hier: statusvariable) ein Wert ungleich Null zugewiesen; bei korrekter Ausführung dagegen gleich Null. Mit
ALLOCATE (dynfeld1(5,20), dynfeld2(-2:10,3,9), STAT = fehler)
zum Beispiel werden dynfeld1 5×20 Elemente zugewiesen und dynfeld2 erhält in seiner ersten Dimension
13 Elemente, wobei der unterste Index bei −2 beginnt, und in seiner zweiten und dritten je 3 und 9.
Möchte man ein dynamisches Feld erneut zuweisen, muß es zuvor wieder freigegeben werden. Dazu dient
die DEALLOCATE–Anweisung:
DEALLOCATE (feld1, feld2, ...[, STAT = statusvariable])
Um den Status eines dynamischen Feldes zu erfragen existiert die vordefinierte Funktion ALLOCATED(feld),
die TRUE als Rückgabewert liefert, falls das Feld definiert ist. Anderenfalls liefert sie den Wert FALSE.
4.2.3.6
Zeiger und Ziele
Zeiger - also, eine Variable, die mit dem POINTER–Attribut versehen wurde - sind, wie bereits erwähnt,
keine Variablen im eigentlichen Sinne. Sie dienen nicht zur Aufnahme von Rechendaten, sondern bieten
dem Programmierer die Möglichkeit, Adressen im Arbeitsspeicher anzusprechen. Man sagt, sie zeigen
darauf. Ist das Ziel eines Zeigers gesetzt worden, kann mit dem zugeordneten Speicherbereich gearbeitet
werden, wie bei allen anderen Variablen auch. Es können Werte abgelegt, verändert und ausgelesen
werden.
Unter Fortran95 gibt es für einen Zeiger zwei Möglichkeiten eine Speicheradresse festgelegt zu bekommen.
Zum einen kann ein Zeiger auf eine Adresse zeigen, die bereits einer anderen Variablen zugeordnet ist,
zum anderen kann ihm aber auch ein eigener (und somit zuvor freier) Speicherplatz zugewiesen werden.
Zeiger auf eine Variable
Ein Zeiger kann nur auf eine andere Variable zeigen, wenn für diese das TARGET–Attribut gesetzt, sie
also als Zeigerziel deklariert wurde. Die Zuweisung der Variable als Ziel erfolgt dann mit Hilfe einer
Zeigerzuweisungs–Anweisung ("=>").
Beispiel:
REAL, TARGET :: zahl
REAL, POINTER :: zeigaufzahl
! ein REAL-Variable, die Zeigerziel sein kann
! ein Zeiger auf eine Variable vom Typ REAL
zahl = 0.
zeigaufzahl => zahl
! ’zeigaufzahl’ soll ’zahl’ als Ziel haben
PRINT*, zeigaufzahl
zeigaufzahl = 7.3
PRINT*, zahl
! gibt
’0.’
auf dem Bildschirm aus
! gibt
’7.3’ auf dem Bildschirm aus
In unserem Beispiel haben wir eine REAL–Größe namens zahl deklariert, die auch das TARGET–Attribut
besitzt. Somit kann zahl auch als Ziel eines Zeigers benutzt werden. Der Zeiger zeigaufzahl ist auch
4.2. FORTRAN
21
vom Typ REAL, und kann somit als Zeiger auf REAL-Größen verwendet werden. Mit "=>" haben wir
zahl als Ziel von zeigaufzahl festgelegt. Somit zeigt zeigaufzahl auf zahl. Als wir den Wert von
zeigaufzahl auf 7.3 gesetzt haben, hat sich dadurch auch der Wert von zahl auf 7.3 geändert, da beide
symbolische Namen die gleiche Adresse im Speicher ansprechen.
Um den Zeiger von seinem Zeigerziel wieder zu trennen, kann die Anweisung
NULLIFY (zeiger1, zeiger2, ...)
verwendet werden. Jedoch würde jede Neuzuordnung eines Zeigers zu einem Ziel bereits implizieren, daß
die vorangegangene Zuordnung damit aufgehoben ist.
Zeiger auf eigenen Speicherplatz
Ähnlich wie bei dynamischen Feldern kann die Vergabe von Speicher an Zeiger gesehen werden. Daher ist
es auch nicht weiter verwunderlich, daß hierbei auch nahezu die gleichen Anweisungen verwendet werden.
Mit
ALLOCATE (zeiger1, zeiger2, ...[, STAT = statusvariable])
kann einem Zeiger (oder auch mehreren gleichzeitig) Speicherplatz zugewiesen werden. Dabei wird dem
Zeiger abhängig von seinem Typ Speicher zugeteilt und kann anschließend wie eine gewöhnliche Variable
dieses Typs behandelt werden. Die korrekte Abbarbeitung der Anweisung kann wie bei den dynamischen
Felder unter 4.2.3.5 abgefragt werden.
Um den Speicherplatz eines Zeigers wieder freizugeben verwendet man auch hier die Anweisung
DEALLOCATE (zeiger1, zeiger2, ...[, STAT = statusvariable]).
Um den Zuordungsstatus eines Zeigers zu erfragen existiert die vordefinierte Funktion ASSOCIATED(zeiger),
die TRUE als Rückgabewert liefert, falls dem Zeiger ein Ziel zugewiesen ist.
Allgemein sollte aber noch gesagt sein, daß niemals ein Zeiger verwendet werden sollte, der nicht bereits
ein Ziel zugewiesen bekommen hat. Um etwaige Fehler innerhalb eines Programmablaufs auszzuschließen
empfiehlt es sich, jeden Zeiger mit dem Rückgabewert der Funktion NULL() zu belegen
zeiger => NULL()
,um diesen als nicht–zugewiesen festzulegen!
4.2.4
Initialisierungen
Mit Initialisierung bezeichnet man die Vergabe eines Anfangswertes an ein Datenobjektes. Die Initialisierung von Variablen in einem Programm ist bei den meisten Programmiersprachen wichtig, da Variablen
oft nach der Deklaration keinen festgelegten Wert haben. Somit können Berechnungen, die von einem
Startwert abhängen, leicht falsche Ergebnisse liefern, ohne daß der Fehler im Programm ersichtlich ist.
In Fortran95 gibt es nun folgende Möglichkeiten der Initialisierung:
• Initialisierung bei der Deklaration
Die Initialisierung kann direkt hinter der Deklaration erfolgen.
22
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
INTEGER :: zaehler = 1, summe = 0
REAL :: feld1(100) = 1.5 , feld2(50) = / 1., 2., 3., 4., 5.,(i+5.,i=1,45)/
Den beiden INTEGER–Größen zaehler und summe werden hier die Anfangswerte 1 bzw. 0 zugewiesen. Bei dem REAL–Feld feld1 werden alle 100 Elemente auf einmal mit dem Wert 1.5 belegt.
Beim feld2 sollen die einzelnen Elemente mit verschiedenen Werten versehen werden. Dies kann
mit einer Werte–Liste erreicht werden, in der man in / .. / eingeschlossen alle Werte einzeln oder
mittels einer impliziten Zählschleife für mehrere Elemente angibt. Dabei muß jedoch nicht unbedingt mit einem konstanten Wert initialisiert werden, sondern dieser kann sich auch durch einen
arithmetischen Ausdruck ergeben.
• DATA–Anweisung
Die Initialisierung kann aber auch in einer seperaten Anweisung erfolgen, der DATA–Anweisung:
DATA nlist1 /wlist1 /, nlist2 /wlist2 /, . . .
Die DATA–Anweisung ist nicht ausführbar und muß nach dem Vereinbarungsteil auftreten (siehe Bild
4.3). nlisti ist eine Liste von Namen, denen Anfangswerte zugewiesen werden sollen, und wlisti ist
eine Liste von (symbolischen) Konstanten, die die eigentlichen Anfangswerte sind. Die nlisti kann
unterschiedlichste Elemente sowie implizite Schleifen enthalten. Die Liste der Anfangswerte wlisti
kann vor den Konstanten Wiederholungsfaktoren enthalten. Für jedes Element aus nlisti muß ein
Wert in wlisti vorhanden sein. Das erste Element der Konstantenliste entspricht dem ersten Namen
der Namenliste, das zweite der Konstantenliste dem zweiten der Namenliste usw.
Beispiel 1:
INTEGER :: array (200), feld (100), i
DATA (array (i), i = 1,111), feld /111*0, 60*1, 40*2/
Die ersten 111 Elmente des Feldes array werden über eine implizite Zählschleife mit Null initialisiert, die ersten 60 Elemente von feld werden mit Eins vorbesetzt und die restlichen 40 mit Zwei.
Man beachte, daß nicht alle Elemente von array initialisiert sind, und daß durch die Angabe des
Feldnamens feld ohne Indexausdruck alle Elemente dieses Feldes vorbesetzt werden müssen.
Beispiel 2:
REAL z(400)
DATA z(1:399:2), z(2:400:2) / 200*1., 200*-1./
Hier wird jedes Element von von Z abwechselnd mit dem Wert 1. und -1. belegt. Der Umgang mit
Feldern und Teilfeldern wird in einem späteren Abschnitt genauer besprochen.
Gleichgültig auf welche Art und Weise man ein Datenelement initialisiert, darf dieses nur ein einziges
Mal im gesamten Programm geschehen. Bezüglich weiterer Einschränkungen sei auf die weiterführende
FORTRAN–Literatur verwiesen.
4.2.5
Arithmetische Ausdrücke und Anweisungen
4.2.5.1
Arithmetische Ausdrücke
Ein arithmetischer Ausdruck besteht aus einer Folge von arithmetischen Operanden (Konstanten,
Variablen, Feldelementen und Funktionsaufrufen), die durch (arithmetische) Operatoren und Klammern
voneinander getrennt (oder miteinander verknüpft) sind.
4.2. FORTRAN
23
Zum Beispiel ist
-(3.-X)*F + H/D**4
ein gültiger arithmetischer Ausdruck.
Arithmetische Operanden stellen Werte vom Typ INTEGER, REAL, DOUBLEPRECISION und COMPLEX
dar. Arithmetische Operationen sind:
**
*
/
+
-
Exponentiation
Multiplikation
Division
Addition
Subtraktion
Enthält der Ausdruck mehrere Operatoren, so gilt für die Auswertung die Reihenfolge:
1. **
2. * und /
3. + und - .
Durch Klammern kann die Auswertungsreihenfolge beeinflußt werden.
Beispiele:
-Z**2
(-Z)**2
ist gleichbedeutend mit −(Z 2 )
ergibt
(−Z)2 = Z 2
Aufeinanderfolgende Operationen von gleichem Rang (* und / bzw. + und -) werden von links nach
rechts bearbeitet:
A/B*C
ist gleichbedeutend mit (A/B)*C
(und nicht A/(B*C)).
Aber: Aufeinanderfolgende Exponentiationen werden von rechts nach links zusammengefaßt:
2
2**3**2 ist gleichbedeutend mit 2**(3**2), also mit 2(3 ) .
Mehrere aufeinanderfolgende Operatoren wie A**-B oder C+-D sind verboten. Durch Verwendung von
Klammern wie A**(-B) oder C+(-D) werden diese Ausdrücke statthaft.
Überhaupt empfiehlt sich die fleißige Verwendung von Klammern auch an solchen Stellen, wo sie eigentlich
nicht zwingend notwendig sind. Man schafft dadurch Klarheit und Übersichtlichkeit in den Anweisungen
und beugt Zweifeln bei der Auswertungsreihenfolge vor.
Der Datentyp eines arithmetischen Ausdrucks ergibt sich aus dem Datentyp der Operanden nach der
folgenden Tabelle:
24
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
DOUBLE
INTEGER
REAL
COMPLEX
INTEGER
I
R
C
D
REAL
R
R
C
D
COMPLEX
C
C
C
—
D
D
—
D
DOUBLE
PRECISION
PRECISION
¾
Exponent bei **
6
Basis bei **
Tabelle 4.1. Ergebnistypen arithmetischer Ausdrücke.
(I = INTEGER, R = REAL, C = COMPLEX,
D = DOUBLEPRECISION, — = nicht erlaubt)
Bei der Verknüpfung von Operanden unterschiedlichen Typs wird der vom Ergebnistyp abweichende Operand zunächst in den Ergebnistyp umgewandelt. (Ausnahme: Exponentiation mit INTEGER–Exponent).
Der Ausdruck 2.*(2/3) hat beispielsweise den Wert 0.0, aber (2.*2)/3 hat den Wert 1.333333.... .
Bei der Division von INTEGER–Größen ist das Ergebnis wieder eine INTEGER–Größe, die durch Abschneiden
der Nachkommastellen im mathematischen Quotienten entsteht:
12/8*2 ergibt 2,
12*2/8 ergibt 3.
Aus dem selben Grund ist 8/3*3.0 gleich 6.0 und nicht 8.0.
Generell muß vor der Verwendung von sogenannten mixed mode–Ausdrücken (arithmetische Ausdrücke
mit Größen unterschiedlicher Typen) gewarnt werden, da sie eine beliebte Fehlerquelle darstellen (siehe
obige Beispiele). Für die Typ–Umwandlung gibt es in FORTRAN Standardfunktionen, z.B. INT, FLOAT,
usw..
Für die in arithmetischen Ausdrücken ebenfalls möglichen Funktionsaufrufe (z.B. Wurzel: SQRT, Sinus:
SIN usw.) gelten die gleichen Regeln wie oben. Funktionen werden später erläutert (Abschnitt 4.2.17,
4.2.18).
4.2.5.2
Arithmetische Anweisungen
Arithmetische Anweisungen gehören zu den ausführbaren Anweisungen und haben die allgemeine
Form
v = expr
Darin ist v eine (einfache oder indizierte) Variable und expr ein beliebiger arithmetischer Ausdruck.
Das Zeichen ’=’ muß dabei interpretiert werden als ergibt“ oder als Zuweisungszeichen: der Wert von
”
4.2. FORTRAN
25
expr wird der Variablen v zugewiesen, d.h. er wird auf dem der Variablen v zugeordneten Speicherplatz
gespeichert.
Arithmetische Anweisungen sind keine Gleichungen im mathematischen Sinn, wie das Beispiel
N = N + 1
zeigt: der Wert von N wird um 1 erhöht und das Ergebnis wird auf den Speicherplatz von N zurückgeschrieben.
Ist der Typ der Variablen vom Typ des arithmetischen Ausdrucks verschieden, so wird der Ausdruck
zuerst seinem Typ entsprechend berechnet; dann wird der Typ des Ergebnisses in den Typ der Variablen
umgewandelt.
Beispiel (LX und I3 sind vom Typ INTEGER):
I3 = LX/3.
Der Wert von LX wird in eine reele Größe umgewandelt und im reelen Modus durch 3. dividiert. Das
Ergebnis wird durch Abschneiden der Nachkommastellen in eine ganze Zahl umgewandelt und in I3
gespeichert.
Mit dem oben Gesagten erklären sich auch die unterschiedlichen Ergebnisse des dritten Programmbeispiels in Abschnitt 4.2.1 Einfache FORTRAN–Programmbeispiele“. Die Auswertung der Kreisflächen”
formel πr2 für r = 1 ergibt einen Wert von 3.14159. . . , der durch die Zuweisung an die REAL–Variable
flaech auch richtig ausgegeben wird. Bei der Zuweisung an die INTEGER–Variable wert findet jedoch eine
Umwandlung des Ergebnisses in eine ganze Zahl statt, die mit dem Abschneiden der Nachkommastellen
verbunden ist. Daher sind im FORTRAN–Sinne beide Ergebnisse richtig.
4.2.6
Logische Ausdrücke und Anweisungen
Die Ausführungsreihenfolge der Programmanweisungen kann abhängen von Größen, die sich während
der Rechnung ergeben. Die Steuerung solcher Verzweigungen geschieht über Steueranweisungen (siehe
Abschnitt 4.2.9 Steueranweisungen“), die logische Ausdrücke auswerten.
”
4.2.6.1
Logische Ausdrücke
Logische Berechnungen werden mit logischen Ausdrücken formuliert, bei deren Auswertung sich ein
Ergebnis vom logischen Typ mit dem Wert wahr oder f alsch ergibt. Jede logische Konstante, Variable,
jeder Vergleichsausdruck und jeder Aufruf einer logischen Funktion ist ein einfacher logischer Ausdruck.
Kompliziertere logische Ausdrücke werden gebildet durch Verknüpfung logischer Operanden mit logischen
Operatoren und Klammern.
Logische Operatoren sind
Operator
.NOT.
.AND.
.OR.
.EQV.
.NEQV.
mit der folgenden Wertetabelle:
Name
Negation
Konjunktion
Disjunktion
Äquivalenz
Antivalenz
Gebrauch
.NOT.X
X1.AND.X2
X1.OR.X2
X1.EQV.X2
X1.NEQV.X2
26
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
X1
.TRUE.
.TRUE.
.FALSE.
.FALSE.
X2
.TRUE.
.FALSE.
.TRUE.
.FALSE.
.NOT.X2
.FALSE.
.TRUE.
.FALSE.
.TRUE.
X1.AND.X2
.TRUE.
.FALSE.
.FALSE.
.FALSE.
X1.OR.X2
.TRUE.
.TRUE.
.TRUE.
.FALSE.
X1.EQV.X2
.TRUE.
.FALSE.
.FALSE.
.TRUE.
X1.NEQV.X2
.FALSE.
.TRUE.
.TRUE.
.FALSE.
In einem Vergleichsausdruck können die Werte zweier arithmetischer, zweier logischer oder zweier
Zeichenausdrücke verglichen werden, nicht jedoch z.B. der Wert eines arithmetischen mit dem eines
Zeichenausdrucks. Für Vergleichsoperationen mit REAL-Größen sei auf Abschnitt 4.4 hingewiesen. Vergleichsausdrücke treten nur in logischen Ausdrücken auf, ihr Wert ist wahr oder falsch.
Es gibt folgende Vergleichsoperatoren:
Operator
.LT.
<
.LE.
<=
.EQ.
==
.NE.
/=
.GT.
>
.GE.
>=
Name
less than
less or equal
equal
not equal
greater than
greater or equal
Gebrauch
X1.LT.X2
X1 < X2
X1.LE.X2
X1 <= X2
X1.EQ.X2
X1 == X2
X1.NE.X2
X1 /= X2
X1.GT.X2
X1 > X2
X1.GE.X2
X1 >= X2
Bedeutung
Ist X1 kleiner als X2?
Ist X1 kleiner oder gleich X2?
Ist X1 gleich X2?
Ist X1 ungleich X2?
Ist X1 größer als X2?
Ist X1 größer oder gleich X2?
Die Punkte bei den logischen und Vergleichsoperatoren sind zwingender Bestandteil. Die oben tabellierten
Vergleichsoperatoren gelten nur für numerische Typen. Logische Typen und Zeichenketten lassen sich
nur auf (Un-)Gleichheit überprüfen. Weitere, auf Zeichenketten beschränkte Vergleichsoperatoren sind
im Abschnitt 4.2.7 erwähnt. Die Reihenfolge der Auswertung von (hier numerischen) Ausdrücken wird
normalerweise durch Klammern vorgegeben. Generell gilt folgende Rangfolge:
0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Klammern
Funktionsaufruf
**
Vorzeichenoperationen
* und /
+ und Vergleichsoperationen
.NOT.
.AND.
.OR.
.EQV. und .NEQV.
Stehen gleichrangige Operationen nebeneinander, so wird von links nach rechts interpretiert (Ausnahme: Exponentiation!).
Danach ist der Ausdruck (mit REAL P,G und LOGICAL L,K)
-P**2.GE.G.AND.L.OR..NOT.K
gleichbedeutend mit
4.2. FORTRAN
27
(((-(P**2)).GE.G).AND.L).OR.(.NOT.K)
Es soll hier nochmals betont werden, daß die Verwendung von Klammern, auch wenn sie überflüssig sind,
die Verständlichkeit und Lesbarkeit von Ausdrücken wesentlich verbessert, siehe obiges Beispiel.
4.2.6.2
Logische Anweisungen
Logische Anweisungen haben die Form
v = expr
wobei v eine (einfache oder indizierte) logische Variable und expr ein logischer Ausdruck mit dem Wert
.TRUE. oder .FALSE. ist.
4.2.7
Zeichenausdrücke und -anweisungen
Ein Zeichenausdruck beschreibt eine Zeichenfolge (einen Text). Seine Auswertung liefert ein Ergebnis
vom Typ Zeichen. Zeichenausdrücke bestehen aus Zeichenkonstanten, Namen von Zeichenkonstanten,
Zeichenvariablen, Zeichenfeldelementen, Teilzeichenreihen und Aufrufe von Zeichenfunktionen, die durch
den Verkettungsoperator // (zwei Schrägstriche) und Klammern verbunden sein können.
Beispiel:
’AB’ // ’CDE’ ergibt die Zeichenfolge ’ABCDE’.
Zeichenanweisungen haben die Form
Zeich = expr
wobei Zeich der Name einer Zeichenvariablen, eines Zeichenfeldelementes oder einer Teilzeichenreihe ist,
und expr ist ein Zeichenausdruck. Keine der Zeichenpositionen von Zeich darf in expr auftreten (siehe
unten). Die Längen von Zeich und expr können unterschiedlich sein. Ist Zeich länger als expr, so wird
von rechts bis zur Länge von Zeich mit Leerzeichen aufgefüllt. Wenn Zeich kürzer ist als expr, wird expr
entsprechend der Länge von Zeich von rechts verkürzt.
Beispiel:
CHARACTER :: mo*3, da*3, date*12
date = da//mo//’1993’
Die Zeichenvariablen mo und da werden mit der Zeichenfolge 1993 zu einer 12 Zeichen langen Zeichenreihe
verkettet, die der Variablen date zugewiesen wird. date wird dabei rechts mit 2 Leerzeichen aufgefüllt.
Eine definierte und initialisierte Zeichenreihe (CHARACTER–String) erlaubt den Zugriff auf die komplette
Reihe oder auf Teile davon (Teilzeichenreihe, Substring). Syntax:
char( first : last )
Dabei ist char der Name der Zeichenreihe, first die Position des ersten Zeichens des Substrings und last
die des letzten Zeichens des Substrings. Fehlt first, wird der Wert 1 genommen, fehlt last, wird die Länge
len des Strings genommen, wobei allgemein gilt: 1 ≤ f irst ≤ last ≤ len.
Beispiel:
CHARACTER (LEN=6) :: chara1
chara1 = ’STRING’
28
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Hier ist die Länge des Strings len = 6. Mögliche Teilzeichenreihenzugriffe sind dann beispielsweise:
chara1(1:3)
Wert:
’STR’
chara1(3:4)
Wert:
’RI’
chara1(4: )
Wert:
’ING’
chara1( :4)
Wert:
’STRI’
chara1( : )
Wert:
’STRING’ (also die ganze Zeichenreihe)
Nach dem oben Gesagten ist eine Zuweisung
zeich(1:5) = zeich(3:7)
unzulässig, da die beiden Teilzeichenreihen sich in zeich(3:5) überlappen. Bei Substring–Zugriffen auf
Zeichenfelder wird erst das Feldelement (also dessen Index/Indices) und dann der Substring spezifiziert:
CHARACTER :: cfeld(6)*8, name*4
name = cfeld(2)(3:6)
Der Zugriff bezieht sich auf das 3. bis 6. Zeichen des 2. Elementes des Feldes cfeld.
Im Zusammenhang mit Zeichen soll noch auf die damit zusammenhängenden Standardfunktionen CHAR,
ICHAR, (Typumwandlung Ganzzahl in Zeichen bzw. Zeichen in Ganzzahl), LEN (Länge einer Zeichenreihe), INDEX (Ort der Zeichenreihe in einer anderen) sowie LGE, LGT, LLE, LLT (lexikalische Anordnung)
hingewiesen werden. Genaueres über die Standardfunktionen entnehme man der FORTRAN–Literatur
(z.B. [11] ).
4.2.8
Ausdrücke und Anweisungen mit Feldern und Teilfeldern
Alle der hier vorgestellten Operationen zwischen Datenobjekten gleichen Typs sind auch mit Feldern bzw.
Teilfeldern durchzuführen.
4.2.8.1
Ausdrücke mit Feldern und Teilfeldern
Wie man auf einzelne Elemente eines Feldes durch Angabe des jeweiligen Indexes zugreifen kann, ist
bereits im Abschnitt 4.2.3.2 erläutert worden. Es besteht allerdings auch die Möglichkeit, ausgewählte
Teile eines Feldes durch die Angabe eines Indextripels anzusprechen:
[ indexanfang ] : [ indexende ] [ : indexabstand ]
Dabei sind indexanfang und indexende ganzzahlige Indexausdrücke, die den ersten und den letzten
Wert der Indexfolge angeben. Fehlt indexanfang wird als erster Index die untere Indexgrenze des Feldes angenommen. Läßt man indexende weg, wird als obere Grenze der letzte Index der Dimension des
Ausgangsfeldes gewählt. Auch die Angabe von indexabstand ist optional. Sie dient dazu den Abstand
zwischen den Werten in der Indexfolge festzulegen. indexabstand kann auch negativ sein, wenn indexanfang größer ist als indexende. Fehlt die Angabe von indexabstand wird der Wert als 1 angenommen.
Beispiele:
4.2. FORTRAN
29
DIMENSION :: feld(25,30), i(5)
PARAMETER (i = (/ 3, 6, 9, 12, 15 /) )
feld(1,:)
spricht alle 30 Elemente der ersten Zeile von feld an.
feld(7,2:30:2)
liefert ein eindimensionales Teilfeld von feld, welches nur jedes
zweite Element der siebten Zeile von feld enthält.
feld(4:6,2:30:2)
kann als ein zweidimensionales Teilfeld mit 3*15 Elementen verstanden werden, welches jedes zweite Element der vierten, fünften
und sechsten Zeile von feld enthält.
feld(23,20:9:-4)
ist ein eindimensionales Teilfeld von feld, das die Elemente
(23,20),(23,16) und (23,12) von feld enthält.
feld(19,i)
entspricht einem eindimensionales Teilfeld, das die Elemente
(19,3), (19,6),
(19,9), (19,12) und (19,15) enthält.
4.2.8.2
Anweisungen mit Feldern und Teilfeldern
Anweisungen sehen bei Feldern bzw. Teilfeldern allgemein wie folgt aus:
f eld = expr
expr kann dabei, je nach Datentyp von f eld, ein beliebiger Ausdruck sein, wie wir es in den voranstehenden
Kapiteln gesehen haben. Bei der Rechnung mit Teilfeldern ist aber im Besonderen zu beachten, daß
beide Seiten der Zuweisung die gleichen Feldgrößen haben müssen, genauer die gleiche Anzahl
an Elementen. Taucht in der Berechnung von expr ein skalarer Wert auf, so wird er als Feld mit gleicher
Größe wie das eigentliche Teilfeld angenommen.
Beispiele:
REAL :: r1(7),r2(9),r3(6)
r1(1:3) = r2(3:9:3) - r3(2:6:2)
Es ergibt sich somit
r1(1) = r2(3) - r3(2),
r1(2) = r2(6) - r3(4) und
r1(3) = r2(9) - r3(6)
r1 = r1 + 3.
erhöht den Wert jedes Elementes von
r1 um 3. : r1(1) = r1(1) + 3.,
r1(2) = r1(2) + 3., usw...
CHARACTER (LEN=5) :: c1(3)*17,c2(3),c3(3)
c2 = (/’Anton’,’Berta’,’Chris’/)
c3 = (/’Alice’,’Bernd’,’Carla’/)
c1(1:3) = c2(1:3)//’ liebt ’//c3(3:1:-1)
LOGICAL
l2(1) =
l3(1) =
l4(1) =
liefert für
c1(1) : ’Anton liebt Carla’
c1(2) : ’Berta liebt Bernd’,
c1(3) : ’Chris liebt Alice’
:: l1(2),l2(2),l3(2),l4(2)
.TRUE. ; l2(2) = .TRUE.
.FALSE. ; l3(2) = .TRUE.
.FALSE. ; l4(2) = .FALSE.
l1 = (.NOT.(l2.AND.l3(2:1:-1))).EQV.l4
liefert für l1(1) : .TRUE.
und für l1(2) : .FALSE.
30
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Die Auswertung solcher Anweisungen erfolgt also offensichtlich elementweise, wobei das jeweils das erste
Element eines Teilfeldes von expr genommen wird um das erste Element von f eld zu bestimmen. Ebenso
geschieht dies mit dem zweiten, dritten, usw., bis das gesamte (Teil–)Feld f eld mit neuen Werten belegt
ist.
4.2.9
Steueranweisungen
Im Kapitel Programmiertechnik“ haben wir die Möglichkeiten kennengelernt, mit denen ein Programm
”
vom linearen Verlauf abweichen kann: Verzweigungen und Schleifen.
Mit Steueranweisungen wird die Abarbeitungsreihenfolge der Programmanweisungen beeinflußt, die
Ausführung des Programms kann unterbrochen oder beendet werden. Mögliche Steueranweisungen sind
unter anderem:
Logisches IF
Block IF
ELSEIF
ELSE
ENDIF
END
( DO )
( CALL )
[ Arithmetisches IF ]
[ STOP ]
[ RETURN ]
Die mit (...) versehenen Anweisungen werden in den folgenden Abschnitten behandelt: die DO–Anweisung
wird im Abschnitt 4.2.11 behandelt; CALL wird im Zusammenhang mit Unterprogrammen, Abschnitte
4.2.17, 4.2.18 und 4.2.19 beschrieben.
Die mit [....] versehenen Anweisungen sind nur der Vollständigkeit halber aufgeführt. Von ihrer Verwendung wird im Allgemeinen abgeraten, da sie entweder der strukturierten Programmierung widersprechen,
sie überholt sind, oder es bessere Ersatzkonstrukte in FORTRAN gibt.
4.2.10
Verzweigungen
In diesem Abschnitt beginnen wir, die in Kapitel Programmiertechnik“ angeführten Struktogramm–Ele”
mente in entsprechende FORTRAN–Anweisungen umzusetzen.
Verzweigungen stellen Fälle dar, in denen je nach Ergebnis der Auswertung von Bedingungen vom linearen Programmablauf abgewichen wird oder einer von mehreren alternativen Programmpfaden durchlaufen
wird.
Die bedingte Anweisung stellt den einfachsten Fall dar. Soll in Abhängigkeit von einer Bedingung
eine einzelne Anweisung ausgeführt werden oder nicht, so liegt eine einfache Verzweigung mit leerem
False–Pfad vor:
XXX
T
XXX
XXX
XX
stat
»
»»»
»
»
»»
expr
XXX
»»»
»
»
»
XXX »»
»
F
4.2. FORTRAN
31
Hierfür bietet FORTRAN das logische IF:
IF (expr) stat
Es ermöglicht die bedingte Ausführung der Anweisung stat, wenn der logische Ausdruck expr wahr ist.
Hat expr den Wert falsch, wird mit der dem IF folgenden Anweisung fortgesetzt. Die Anweisung stat darf
keine DO–, Block-IF oder logische IF–Anweisung sein.
Ein Beispiel: Berechnung der Wurzel aus einer Zahl X, sofern diese positiv ist,
XX
XXX
XXX
T
»»
XXX
X≥0
XXX
»»
»»»
»»
XXX
»
»»»
»»
»
»
F
Berechne Wurzel
wird umgesetzt zu
IF (x .GE. 0.0) wurz = SQRT(X)
4.2.10.1
Block–IF–Strukturen
Sollen in Abhängigkeit von einer Bedingung mehrere Anweisungen ausgeführt werden, so verwenden
wir das Block–IF.
XX
X
XX
XXX
T
»»»
»»
»
»
expr
»»
XX
»»
XXX
»
»
XXX
F
»»
X»»
Anweisungen
wenn expr den
Wert wahr hat
Eine Block–IF–Struktur beginnt mit einer Block–IF–Anweisung und endet mit einer ENDIF–Anweisung.
Dazwischen steht ein Block von ausführbaren Anweisungen:
IF (expr) THEN
Anweisungen wenn expr den W ert wahr hat
ENDIF
Darin ist expr ein logischer Ausdruck.
Ein Beispiel für die einfachste Form einer Block–IF–Struktur: wir erweitern das obige Beispiel der Wurzelberechnung um die Ausgabe des Wertes der Wurzel auf dem Bildschirm.
32
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
XX
»»»
»»
»
»
XXX
»
x≥0
XXX
»»»
»
»
F
XXX
»
X »»»
XX
XXX
T
Berechne Wurzel(x)
A(B): Wurzel(x)
Dies läßt sich wie folgt umsetzen:
IF (x .GE. 0.0) THEN
wurz = SQRT (x)
PRINT*, ’Die Wurzel ist:’, wurz
ENDIF
Wenn die Bedingung x ≥ 0 erfüllt ist, dann wird die Wurzel berechnet, und das Ergebnis wird zusammen
mit einem Kommentar ausgedruckt. Anschließend wird die auf die ENDIF–Anweisung folgende Anweisung
ausgeführt. Ist der logische Ausdruck falsch, also x < 0, dann wird sofort mit der auf das ENDIF folgenden
Anweisung fortgefahren.
Um zwei alternative Anweisungsblöcke programmieren zu können, verwendet man die ELSE–Anweisung in der Block–IF–Struktur.
XX
XXX
T
»»»
XX
XXX
»»»
»»»
Bedingung
»»»
»
XXX
»
X »»»
Anweisungen A1
Anweisungen A2
...
...
...
...
XXX
F
Die Umsetzung in FORTRAN lautet allgemein:
IF (Bedingung) THEN
Anweisungen A1 wenn Bedingung erf üllt ist
ELSE
Anweisungen A2 wenn Bedingung nicht erf üllt ist
ENDIF
Bedingung ist ein logischer Ausdruck. Ist sein Wert wahr, wird der Block mit den Anweisungen A1
ausgeführt, ist sein Wert f alsch, dann wird der Block mit den Anweisungen A2 ausgeführt.
Beispiel:
4.2. FORTRAN
33
XX
»»»
»»
»
»
XXX
»
x≥0
XXX
»»»
»
»
XXX
»
F
X »»»
XX
XXX
T
Berechne Wurzel(x)
A(B):
Fehlermeldung
A(B): Ergebnis
Das Struktogramm kann wie folgt umgesetzt werden:
IF (x .GE. 0.0) THEN
wurz = SQRT (x)
PRINT*, ’Die Wurzel ist:’, wurz
ELSE
PRINT*, ’Fehler: Der Radikand ist negativ!’
ENDIF
Je nachdem ob x ≥ 0 wahr oder falsch ist, wird entweder die Wurzel berechnet und ausgedruckt, oder es
wird eine Fehlermeldung ausgegeben.
Wird beim Abarbeiten eines Blockes ELSE oder ENDIF angetroffen, ist damit der betreffende Block abgeschlossen, und es wird mit der auf ENDIF folgenden Anweisung fortgesetzt.
Für den Fall der allgemeinen Auswahlanweisung lassen sich mehr als zwei alternative Anweisungsblöcke mit einer oder mehreren ELSEIF–Anweisungen realisieren.
PP
PP
PP
PP
PP
PP
PP
PP
Auswahlkriterium
PP
PP
B1
B2
B3
A1
A2
A3
Dieses Konstrukt wird wie folgt umgesetzt:
IF (Bedingung B1) THEN
Anweisungen A1 wenn B1 erf üllt ist
ELSEIF (Bedingung B2) THEN
Anweisungen A2 wenn B2 erf üllt ist
ELSEIF (Bedingung B3) THEN
Anweisungen A3 wenn B3 erf üllt ist
ELSEIF (Bedingung B4) THEN
PP
P
B4 PPP
¿
P¿
A4
¿
¿
¿
¿
¿ sonst
Asonst
¿
¿
¿
34
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Anweisungen A4 wenn B4 erf üllt ist
ELSE
Anweisungen Asonst wenn keine der anderen Bedingungen erf üllt sind
ENDIF
Von den hier fünf IF–Blöcken wird höchstens einer ausgeführt. Die logischen Ausdrücke Bedingung Bn
werden der Reihe nach ausgewertet, bis einer mit dem Wert wahr gefunden wird. Dann wird mit der
ersten Anweisung des zugehörigen IF–Blocks fortgefahren. Wenn dieser IF–Block abgearbeitet ist (oder
der zugehörige Block leer sein sollte), wird mit der dem ENDIF folgenden Anweisung weitergemacht.
Um alle möglichen Fälle in der Auswahlanweisung zu erfassen, kann man dies mit der zusätzlichen
ELSE–Anweisung in der Block–IF–Struktur erreichen. Sie muß dem letzten ELSEIF folgen. Ist keiner der
logischen Ausdrücke Bn wahr, wird mit dem auf ELSE folgenden Block fortgefahren.
Als Beispiel diene wieder die Wurzelberechnung von oben, diesmal mit noch mehr Ausgabekomfort. Als
Struktogramm könnte das so aussehen:
PP
P
PP
¡
PP
¡
PP
PP
x>0
WURZ=SQRT(X)
PP
¡
¡
PP
PP
¡
Auswahlkriterium
PP
PP
PP
PP
¡
¡
¡
PP
¡
PP
x<0
PP¡
¡
x=0
A(B):
A(B):
’Wurzel = 0’
’Radikand negativ’
sonst
A(B): ’Dieser
Fall kann gar
A(B): WURZ
nicht auftreten
Die Umsetzung in FORTRAN könnte dann so erfolgen:
IF (x .GT. 0.0) THEN
wurz = SQRT (x)
PRINT*, ’Die Wurzel ist’, wurz
ELSEIF (x .EQ. 0.0) THEN
PRINT*, ’Die Wurzel ist: 0.0’
ELSEIF (x .LT. 0.0) THEN
PRINT*, ’Fehler: Der Radikand ist negativ!’
ELSE
PRINT*, ’Dieser Fall kann gar nicht auftreten!’
ENDIF
Hier wird nacheinander überprüft, ob die Zahl größer, gleich, oder kleiner Null ist. Trifft einer dieser
Fälle zu, wird eine Meldung und gegebenenfalls das Ergebnis ausgegeben. In allen anderen Fällen (solche
kann es eigentlich gar nicht geben) wird eine Meldung mit Hinweis auf die Unmöglichkeit des Ereignisses
ausgegeben.
4.2. FORTRAN
4.2.10.2
35
CASE–Verzweigung
Die CASE–Verzweigung ist der Block–IF–Verzweigung sehr ähnlich und soll hier insbesondere aus Vollständigkeitsgründen aufgeführt werden. Im Gegensatz zu der Block–IF–Verzweigung wird hier der auszuführende
Block nur nach Inhalt einer Varialen bestimmt.
Die Konstruktion der CASE–Verzweigung sieht allgemein aus:
SELECT CASE (varbl)
CASE (selectlist1)
...
CASE (selectlist2)
...
CASE DEFAULT
...
END SELECT
varbl ist eine Variable eines beliebigen Datentyps, die bei der Abbarbeitung der CASE–Anweisung
ausgewertet werden soll. selectlist ist eine Liste von skalaren Werten gleichen Typs, wie varbl. Stimmt
der Inhalt der Variablen varbl mit einem Element der selectlist überein, wird der entsprechende Block
– und nur dieser Block – abgearbeitet. selectlist kann wie folgt zusammengesetzt sein:
w1, w2, w3, ..
w1:w2
w1:
:w2
Wert w1 oder Wert w2 oder ...
zwischen Wert w1 und Wert w2
größer als Wert w1
kleiner als Wert w2
Beispiel:
!
!
!
!
4.2.11
CHARACTER (LEN=5) :: Farbe
SELECT CASE (Farbe)
CASE (’rot’, ’ROT’)
.....
CASE (’gruen’, ’GRUEN’)
.....
CASE (’blau’, ’BLAU’)
.....
CASE DEFAULT
.....
END SELECT
! Farbe ist rot
! Farbe ist gruen
! Farbe ist blau
! Alle anderen Farben
Schleifen
Ein wichtiger Bestandteil von Programmen sind Schleifen. Das sind Gruppen von Anweisungen (Wiederholungsbereich), die im allgemeinen mehrfach ausgeführt werden. Wir haben im Kapitel Programmier”
technik“ Schleifen mit Eintritts– bzw. Austritts–Bedingung sowie Zählschleifen kennengelernt.
36
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
4.2.11.1
Zählschleifen
Zählschleifen sind Schleifen, bei denen zu Beginn der Ausführung festgestellt wird, wie oft die Schleife
durchlaufen wird; die Anzahl der Schleifendurchläufe steht also zu Beginn fest und wird während der
Ausführung nicht mehr verändert. Das Struktogrammsymbol für Zählschleifen lautet:
v = e1 , e2 [, e3 ]
Schleifenanweisungen
...
Für die Programmierung von Zählschleifen verwendet man unter Fortran95 die DO–Schleifen mit Laufvariable. Die Anweisung dann lautet
DO v = e1 , e2 [, e3 ]
Schleifenanweisungen
ENDDO
Die Laufvariable ist v, und der von ihr zu durchlaufende Wertebereich geht von e1 (Startwert) bis e2
(Endwert). Die Laufvariable wird in jedem Schleifendurchlauf um das Inkrement e3 erhöht. Wird das
(optionale) Inkrement weggelassen, wird e3 = 1 angenommen. Die Größen e1 , e2 und e3 sind nichtkomplexe arithmetische Ausdrücke, und v muß eine ganzzahlige Variable sein.
Zähl–Schleifen sind eigentlich spezielle Schleifen mit Eintrittsbedingung. Die Schleife wird je nach Vorzeichen des Inkrements e3 aufwärts (e3 > 0) oder abwärts (e3 < 0) durchlaufen bis v > e2 bzw. v < e2
ist. Die Schleife wird keinmal durchlaufen, wenn e1 > e2 bei e3 > 0 oder wenn e1 < e2 bei e3 < 0 ist. Die
einzelnen Schritte bei der Ausführung einer Zählschleife sind wie folgt:
1. Die Laufvariable erhält den Startwert.
2. Aus Startwert, Endwert und Inkrement wird die Zahl der erforderlichen Schleifendurchläufe ermittelt nach der Formel:
Durchlauf zahl = (Endwert − Startwert + Inkrement)/Inkrement
3. Ist die Durchlaufzahl kleiner oder gleich Null, wird die Schleife nicht ausgeführt (sie wird übersprungen).
4. Am Ende jedes Schleifendurchlaufs wird die Laufvariable um den Wert des Inkrements erhöht.
Beispiele: Das Programmstück
4.2. FORTRAN
37
SU M = 0
i = 1, 3
SU M = SU M + Ai Bi
...
j = 10, 0, −3
A(B): j
sollte in FORTRAN folgendermaßen aussehen (i und j sind ganzzahlig, sum, a und b sind reell):
! --- ZaehlSchleife mit Vorwaertszaehlung
! --sum = 0.0
DO i = 1, 3
sum = sum + a(i)*b(i)
ENDDO
:
:
! --- ZaehlSchleife mit Rueckwaertszaehlung
! --DO j = 10, 0, -3
PRINT*, j
ENDDO
Die letzte Schleife würde die Ausgabe
10, 7, 4, 1
erzeugen.
Zählschleifen können wie IF–Blöcke geschachtelt werden. Dabei muß die innere Schleife vollständig im
Schleifenbereich der äußeren enthalten sein. Zählschleifen können, wenn es sein muß, durch einen Sprung
aus dem Bereich verlassen werden. Sprünge in einen Schleifenbereich hinein sind verboten.
Beispiel für zulässige Schachtelung:
38
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
i = 1, 4
j = 1, 4
k = 1, 5
A(k, j, i) = 0
B(i, j) = 0
Die Umsetzung in FORTRAN sollte wie folgt aussehen:
INTEGER :: i, j, k
REAL
:: a(5,4,4), b(4,4)
DO i = 1, 4
DO j = 1, 4
DO k = 1, 5
a(k,j,i) = 0.0
ENDDO
b(j,i) = 0.0
ENDDO
ENDDO
In dieser Schleifenkonstruktion werden die beiden Felder a und b auf Null gesetzt.
Innerhalb des Wiederholungsbereichs einer Zählschleife dürfen weder die Laufvariable noch die Parameter
e1 , e2 und e3 geändert werden. Sie können aber sonst in beliebiger Weise, z.B. als Operanden in arithmetischen Ausdrücken, verwendet werden. Nach dem Ende der Schleife hat die Laufvariable den Wert
Endwert + Inkrement.
4.2.11.2
Schleifen mit Eintrittsbedingung
Eine Schleife mit Eintrittsbedingung (auch kopfgesteuerte oder abweisende Schleife) wird solange ausgeführt, wie die Eintrittsbedingung erfüllt ist. Ist diese Bedingung von Anfang an nicht erfüllt, wird die
Schleife nicht ausgeführt. Das Struktogrammsymbol
Eintrittsbedingung
Schleifenanweisungen
...
4.2. FORTRAN
39
wird in der folgenden Form umgesetzt:
DO WHILE (Eintrittsbedingung)
Schleifenanweisungen
ENDDO
Ein Beispiel: Finden des ersten von Null verschiedenen Elements eines Feldes mit N Elementen. Zur
Realisierung bietet sich eine Schleife mit Eintrittsbedingung an, da die notwendige Anzahl der Schleifendurchläufe nicht von vorneherein feststeht:
i=1
A(i) = 0 und i < N
i=i+1
Es kann wie folgt umgesetzt werden:
INTEGER :: i, N
PARAMETER (N=99)
REAL :: a(N)
...
i = 1
DO WHILE ((a(i).EQ.0.00) .AND. (i.LT.N))
i = i+1
ENDDO
4.2.11.3
Schleifen mit Austrittsbedingung
Bei Schleifen mit Austrittsbedingung (auch fußgesteuerte Schleife) werden die Anweisungen im Schleifenkörper so lange wiederholt, bis die Austritts– oder Abbruch–Bedingung erfüllt ist. Die Überprüfung
dieser Bedingung erfolgt immer am Ende eines Schleifendurchlaufs. Da es in Fortran95 leider keine direkte Umsetzung der Schleife mit Austrittsbedingung gibt, behelfen wir uns mit einer Endlos–Schleife (DO
ohne Fortsetzungsbedingung) und der EXIT–Anweisung, mit deren Hilfe wir eine Schleife wieder verlassen
können. Die Umsetzung des Struktogrammsymbols
Schleifenanweisungen
...
Abbruchbedingung
40
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
erfolgt dann in der Form:
DO
Schleifenanweisungen
IF (Austrittsbedingung) EXIT
ENDDO
Für die korrekte Umsetzung des Struktogrammsymbols sei noch erwähnt, daß sich zwischen der EXIT–
und ENDDO–Anweisung keine weitere Anweisung, gleich welcher Art, befinden darf. Anderenfalls handelt
es sich nicht mehr um eine strukturierte Programmierung!
Ein kleines Beispiel für eine Schleife mit Austrittsbedingung: Positive Meßwerte von Tastatur einlesen;
Abbruch durch Eingabe einer negativen Zahl.
Drucke Eingabeauf forderung
Lies Meßwert ein
Meßwert < 0
Die Umsetzung des Struktogramms kann beispielsweise so aussehen:
DO
PRINT *, ’Bitte Messwert eingeben’
READ *, MessW
IF (MessW .LT. 0) EXIT
ENDDO
4.2.12
CONTAINS– und END–Anweisung
Der allgemeine Aufbau eines Programmmoduls sieht wie folgt aus:
PROGRAM, SUBROUTINE bzw. FUNCTION upname
:
CONTAINS
:
END [PROGRAM, SUBROUTINE bzw. FUNCTION]
Die CONTAINS–Anweisung ist eine nicht–ausführbare Anweisung und wird nur benötigt, wenn innerhalb
eines Programmmoduls interne Funktionen oder Unterroutinen definiert werden sollen. Diese interne
Funktionen oder Unterroutinen dürfen nur zwischen CONTAINS und END stehen und außer diesen Definitionen dürfen dort auch keine weiteren (selbständigen) Anweisungen untergebracht werden. Fehlt die
Definition interner Unterprogramme, so muß auch die CONTAINS–Anweisung weggelassen werden.
Die END–Anweisung zeigt dem Compiler das Ende der Programmeinheit an. Daher muß jede Programmeinheit mit einer END–Anweisung enden. Die END–Anweisung kann auch optional mit der Angabe der hier
zu beendenen Programmeinheit erfolgen. Handelt es sich jedoch um den Abschluß einer internen Funktion
4.2. FORTRAN
41
oder Unterroutine, muß die END–Anweisung um die Angabe FUNCTION bzw. SUBROUTINE erweitert werden.
Das Antreffen einer END–Anweisung bedeutet auch das logische Ende der betreffenden Programmeinheit
und bewirkt bei Unterprogrammen den Rücksprung ins rufende Programm, bei Hauptprogrammen die
Beendigung der Programmausführung.
4.2.13
Ein-/Ausgabe (Standard-Ein-/Ausgabe)
Bei der Eingabe von Daten, auch Lesen“ genannt, werden Daten an das Programm übergeben. Bei
”
der Ausgabe, auch Schreiben“ genannt, werden Daten vom Programm an eine Datei übergeben. Bei
”
den hier zunächst behandelten Standard-Ein-/Ausgabemöglichkeiten bedeutet Datei“ die Standard–
”
Ein-/Ausgabeeinheit. Das meint im Allgemeinen die Tastatur und den Bildschirm. Die erweiterten
Ein-/Ausgabemöglichkeiten werden im Abschnitt 4.2.14 Erweiterte Ein-/Ausgabe“ behandelt.
”
Die einfachste Ein-/Ausgabe ist das listengesteuerte Lesen bzw. Schreiben. Dabei werden codierte (d.h.
lesbare) Datensätze ohne Angaben zur Formatierung verarbeitet. Jeder Datensatz besteht aus einer Liste
von Werten in freier Form; man spricht deshalb auch von freiem Format“ (nicht zu verwechseln mit
”
formatfrei“).
”
Die Ein-/Ausgabeanweisungen lauten in diesem einfachsten Fall
READ *, iolist
(Lesen, Eingabe von der Tastatur)
PRINT *, iolist
(Schreiben, Ausgabe auf dem Bildschirm)
Die Ein-/Ausgabeliste iolist kann Variablennamen, Feldnamen, Namen von Feldelementen, Namen von
Teilzeichenketten und Listen mit impliziter Schleife enthalten. Die Elemente der Liste werden durch
Komma getrennt. Ein Feldname ohne Indexangabe bezeichnet das gesamte Feld in der der internen
Speicherbelegung entsprechenden Reihenfolge; es wird also das ganze Feld gelesen oder geschrieben.
Beim Lesen werden Daten von der Eingabeeinheit nach Umwandlung in die rechnerinterne Darstellung
(siehe Kapitel 5) an die in der Eingabeliste iolist symbolisch aufgeführten Speicherplätze (i.a. Variablen)
übertragen.
Beispiel:
READ *, zahl1, zahl2, zahl3
Die einzelnen Eingabedaten der Eingabeeinheit (hier: Tastatur) müssen dabei durch Trennzeichen (Komma oder Leerzeichen) voneinander getrennt werden. Korrekte Eingaben für die obige Leseanweisung sind
beispielsweise:
3.14159,-17.400,4.567890
oder
2.9,
9.8765
-7.182e-4
Jede READ–Anweisung liest einen neuen Datensatz (d.h. eine neue Zeile). Die Eingabeliste darf keine
Konstanten, Ausdrücke oder Zeichenketten enthalten.
Beim Schreiben werden Daten von den in der Ausgabeliste iolist symbolisch aufgeführten Speicherplätzen nach Umwandlung von der rechnerinternen Darstellung in die externe Darstellung (siehe Kapitel
5) an die Ausgabeeinheit übertragen.
Beispiel:
PRINT *, ’DIE WURZEL IST:’, wurzel
42
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Das Beispiel zeigt, daß in der Ausgabeliste neben symbolischen Namen auch Zeichenausdrücke vorkommen
können. Außerdem sind auch Konstanten sowie arithmetische und logische Ausdrücke zugelassen.
Eine implizite Schleife in einer Ein-/Ausgabeliste hat die Form
(dlist, v = e1 , e2 , e3 )
Die Elemente v, e1 , e2 , e3 haben dieselbe Bedeutung wie bei DO–Schleifen, und dlist ist eine Ein-/Ausgabeliste.
Beispiel:
READ *, max, (number(i), i = 1, max)
Einzelne Elemente von dlist können ihrerseits wieder implizite Schleifen enthalten.
Beispiel:
PRINT *, ((hfi(i,j), i = 1,4), j = 1,5)
Die Elemente werden dabei in der folgenden Reihenfolge ausgegeben (der erste Index läuft als erster):
hfi(1,1), hfi(2,1), hfi(3,1), hfi(4,1), hfi(1,2), hfi(2,2),
hfi(3,2), hfi(4,2), hfi(1,3), hfi(2,3), ...
Jede READ– oder PRINT–Anweisung beginnt einen neuen Datensatz. Die Reihenfolge der zu übertragenden Daten wird durch die Liste bestimmt. Daher bezeichnet man diese Art der Ein-/Ausgabe auch als
listengesteuert“. Die Anzahl der eingelesenen Daten wird nur durch die Liste bestimmt, nicht durch
”
die tatsächlich vorhandenen Daten. Enthält ein Datensatz mehr Daten als Elemente in der Liste sind,
werden die überzähligen Daten ignoriert. Sind in der Liste mehr Elemente angegeben als ein Datensatz
enthält, so werden mehrere Datensätze gelesen. Erfolgen die Eingaben nicht Typengerecht, so wird das
Programm beendet. Möglichkeiten, dies zu verhindern, werden in Abschnitt 4.2.14 beschrieben.
4.2.14
Erweiterte Ein-/Ausgabe
Die Ein-/Ausgabe, wie wir sie bisher kennengelernt haben war listengesteuert im sogenannten freien
Format. Für die allgemeine Ein- und Ausgabe von Daten gibt es verschiedene Möglichkeiten. Diese
unterscheiden sich hinsichtlich der Art des Zugriffs auf die Daten, der Art der beteiligten Speichermedien,
der Art der externen Darstellung der Daten und deren Art der Beschreibung. So soll beispielsweise die
Eingabe nicht nur von Tastatur sondern auch aus einer Datei erfolgen, und die Ausgabe sollte auch in eine
Datei auf Diskette oder Platte oder auf den Drucker gemacht werden können. Neben den Anweisungen
für die Art der Ein-/Ausgabe (READ, PRINT, WRITE) gibt es Anweisungen, die den Zustand einer Datei
abfragen oder verändern (OPEN, INQUIRE, CLOSE) sowie Anweisungen zur Positionierung einer Datei
(REWIND, BACKSPACE, ENDFILE). Wir werden nicht auf alle Anweisungen detailliert eingehen, da dies
den Rahmen einer Einführung sprengt.
4.2.14.1
Formatierte Ein-/Ausgabe
Neben der bisher vorgestellten Ein-/Ausgabe im freien Format (listengesteuert) gibt es die formatierte
oder formatgesteuerte Ein-/Ausgabe. Dabei wird bei Abarbeitung der Ein-/Ausgabeliste jedem Element der Liste die zugehörige Datenfeldbeschreibung aus der Formatangabe (z.B. FORMAT–Anweisung,
s.u.) zugeordnet und das Element entsprechend ausgegeben.
Beispiel (seien i, j, k ganzzahlig):
4.2. FORTRAN
43
PRINT 999, i, j, k
999 FORMAT (I4, I2, I5)
Die Integer–Variablen i, j und k werden entsprechend der Formatangabe mit der Marke 999 als 4–,
2– bzw. 5–stellige ganze Zahlen hintereinander in einer Zeile ausgegeben. Dabei ist zu beachten, daß
die Formatangaben und die auszugebenden Daten vom Typ her stets zusammenpassen (im obigen Fall:
ganzzahlig).
Die Formatbeschreibung wird bis zum Ende der Ein-/Ausgabeliste abgearbeitet. Reicht die Formatbeschreibung nicht aus, wird das Format wiederholt, und dabei wird eine neue Zeile begonnen.
Bei der formatierten Ein-/Ausgabe werden die Daten entsprechend den Formatangaben gelesen und in
die interne binäre Darstellung (rechnerabhängig) konvertiert bzw. aus dieser internen Darstellung in
Zeichenfolgen umgewandelt. Formatangaben sind in runde Klammern eingeschlossene Zeichenfolgen, die
alternativ unter anderem angegeben werden können durch
1. die Anweisungsmarke einer FORMAT–Anweisung (siehe obiges Beispiel)
2. den Namen eines Zeichenfeldes
3. einen beliebigen Zeichenausdruck.
Wir werden hier nur auf die erste Möglichkeit eingehen. Es können sich mehrere Ein-/Ausgabeanweisungen auf die gleiche Formatangabe beziehen.
Die Format–Anweisung
labl FORMAT( f list )
ist eine nichtausführbare Anweisung, die die Konvertierung und Formatierung der zu übertragenden
Daten festlegt. Dabei ist labl eine Anweisungsmarke und f list eine Liste von durch Komma getrennten
Elementen der Form
r ed, ned, r(f list)
ed und ned sind wiederholbare bzw. nichtwiederholbare Datenfeldbeschreibungen und die Wiederholungszahl r ist eine ganzzahlige Konstante > 0. Fehlt r, wird der Wert 1 angenommen.
In den Tabellen 4.2 und 4.3 geben Großbuchstaben die Konvertierungsart an, und kleine Buchstaben
bezeichnen vom Programmierer hinzuzufügende Informationen:
w Von Null verschiedene, vorzeichenlose, ganzzahlige Konstante, die die Datenfeldweite der externen
Darstellung, d.h. die Anzahl der Zeichen einschließlich führender Leerzeichen, Vorzeichen, Dezimalpunkt und Exponenten angibt.
d Vorzeichenlose, ganzzahlige Konstante, die die Anzahl der Ziffern rechts vom Dezimalpunkt angibt.
Bei der Ausgabe werden Zahlen gegebenenfalls gerundet.
e Von Null verschiedene, vorzeichenlose, ganzzahlige Konstante, die die Anzahl der Ziffern im Exponenten angibt.
m Vorzeichenlose, ganzzahlige Konstante, die angibt, wieviele Ziffern mindestens ausgegeben werden
sollen.
k Skalierfaktor (genauer: Exponent der Zehnerpotenz), ganzzahlige Konstante.
n Positive ganzzahlige Konstante ungleich Null.
44
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Form
A
Aw
Dw.d
Ew.d
Ew.dEe
Typ
Zeichen
Zeichen
Numerisch
Numerisch
Numerisch
Fw.d
Gw.d
Gw.dEe
Numerisch
Numerisch
Numerisch
Iw
Iw.m
Lw
Numerisch
Numerisch
Logisch
Bedeutung
Zeichen mit datenabhängiger Länge
Zeichen mit vereinbarter Länge
Doppelt genaue Gleitpunktzahl mit Exponent
Einfach genaue Gleitpunktzahl mit Exponent
Einfach genaue Gleitpunktzahl mit explizit vereinbarter Länge des Exponenten
Einfach genaue Gleitpunktzahl ohne Exponent
Einfach genaue Gleitpunktzahl mit oder ohne Exponent
Einfach genaue Gleitpunktzahl ohne Exponent oder mit Exponent (mit
explizit vereinbarter Länge des möglichen Exponenten)
Ganze Dezimalzahl
Ganze Dezimalzahl mit mindestens m Ziffern
Logischer Wert
Tabelle 4.1: Wiederholbare Datenfeldbeschreibungen
Form
BN
BZ
kP
nX
SP
SS
S
Tn
TRn
TLn
:
/
$
Typ
Steuerung der
numerischen Eingabe
Skalierfaktor
Steuerung der
numerischen
Ausgabe
E/A-Tabulator
Formatkontrolle
Ende des Datensatzes
Bedeutung
Leerzeichen werden ignoriert
Leerzeichen werden als Nullen aufgefaßt
Skalierung für numerische Konvertierung
Setze um n Positionen vor
Plus–Zeichen werden vorangestellt
Plus–Zeichen werden unterdrückt
Plus–Zeichen werden unterdrückt
Setze auf Position n
Setze um n Positionen vor
Setze um n Positionen zurück
Beendet die Formatabarbeitung
Zeigt das Ende des gegenwärtigen
Ein- oder Ausgabedatensatzes an
Verhindert den Zeilenumbruch
Tabelle 4.2: Nichtwiederholbare Datenfeldbeschreibungen
Das Ausgabefeld wird bei allen Umwandlungen rechtsbündig aufgefüllt.
Es folgt eine kurze Beschreibung der wichtigsten Datenfeldbeschreibungen (auch: Formatspezifikationen).
Das A-Format dient zur Ein-/Ausgabe von Zeichengrößen und hat die Form
A oder Aw
Ist w kleiner als die Länge der Zeichengröße, dann werden die ersten w Zeichen ausgegeben bzw. die
am weitesten rechtsstehenden Zeichen eingelesen, und der Rest wird jeweils ignoriert. Falls w größer
als die Länge der Zeichengröße ist, dann wird linksbündig in den Speicher eingelesen bzw. rechtsbündig
ausgegeben, und der Rest wird mit Leerzeichen aufgefüllt. Wird keine Datenfeldweite angegeben, wird
die Länge der Zeichengröße benutzt.
Innerhalb der Formatangabe auszugebende Zeichenfolgen werden im Apostroph–Format angegeben,
d.h. in Apostrophe eingeschlossen.
Beispiel:
4.2. FORTRAN
45
CHARACTER :: what*5
what = ’ALLES’
PRINT 120, what
120 FORMAT (’ Ich will’, A7)
ergibt die Bildschirm–Ausgabe:
Spalte: 1234567890123456
Zeichen: Ich will ALLES
Zur Ein-/Ausgabe von reellen Zahlen gibt es u.a. das F–Format und das E–Format. Die einfachste
Form dieser Formate ist
Fw.d
bzw.
Ew.d
Bei der Ausgabe im E–Format wird ein Exponent gedruckt; die Verwendung des F–Formats führt zur
Ausgabe einer reellen Zahl ohne Exponent.
Bei der Eingabe werden genau w Zeichen eingelesen. Es können auch Zahlen im jeweils anderen Format
eingelesen werden, z.B. kann man mit dem F–Format auch Zahlen lesen, die im E–Format auf dem
Datensatz stehen. Reelle Zahlen sollten im Eingabebereich stets mit Dezimalpunkt geschrieben werden.
Bei der Ausgabe im E–Format in der oben angegebenen Form hat der Exponent stets zwei Stellen
plus ein Vorzeichen, d.h. zur Darstellung des Exponenten werden von der gesamten Feldweite w vier
Stellen verwendet. Die Mantisse ist stets: 0.1 ≤ Mantisse < 1.0 (Ausnahme: Die Zahl Null), und der
Exponent wird entsprechend angeglichen. Allgemein muß die Feldweite so groß sein, daß alle Zeichen
auch ausgegeben werden können. Dies gilt insbesondere für das F-Format. Falls w zu klein gewählt
wird, werden w Sterne gedruckt. Falls w zu groß ist, wird rechtsbündig ausgegeben und mit Leerzeichen
aufgefüllt.
Beispiel:
REAL :: alfa
alfa = -3.98E+03
PRINT 910 ,(alfa, i = 1,5)
910 FORMAT (’ ’, F8.2, F5.0, E10.3, E8.1, E6.1)
erzeugt die Ausgabe (auf einer Datei):
Spalten:
Zeichen:
12345678901234567890123456789012345678
-3980.00***** -.398E+04 -.4E+04******
Das erste Zeichen der Ausgabezeile ist ein sogenanntes Vorschubsteuerzeichen für den Drucker. Wir werden darauf später eingehen. Die beiden ersten Formatspezifikationen geben alfa ohne Exponent aus, die
anderen drei bewirken eine Ausgabe mit Exponent. Die Feldweiten des 2. und 5. Formats sind zu klein
um die notwendigen Zeichen aufzunehmen, daher werden Sterne gedruckt. Das vierte Format verwendet
nur eine Stelle für die Mantisse, die daher gerundet wird.
Die Ausgabe ganzzahliger Variabler mit E– oder F–Format produziert nicht vorhersehbare Ergebnisse,
da ganze und reelle Zahlen intern unterschiedlich dargestellt werden (siehe Kapitel 5).
Zur Ein-/Ausgabe ganzer Zahlen dient das I–Format:
Iw
46
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Bei der Eingabe werden genau w Zeichen eingelesen. Die Zahl sollte rechtsbündig geschrieben werden,
da abhängig von der Voreinstellung des Compilers Leerzeichen als Nullen interpretiert werden können.
Standard ist, daß Leerzeichen ignoriert werden. Ein vollständig leeres Feld wird jedoch als Null interpretiert. Dezimalpunkte sind verboten. Die Ausgabe erfolgt ohne Dezimalpunkt. Für negative Zahlen wird
das Vorzeichen unmittelbar vor die erste Ziffer gedruckt.
Beispiel:
INTEGER :: izahl1, izahl2
izahl1 = -123
izahl2 = +45678
PRINT 117, izahl1, izahl2
117 FORMAT (’ ’, 2I4)
erzeugt die Ausgabe
Spalten:
Zeichen:
1234567890
-123****
Die zweite Datenfeldbeschreibung ist zu klein, es werden vier Sterne ausgegeben.
Das L–Format wird benutzt, um logische Daten ein- oder auszugeben:
Lw
Bei der Eingabe wird dem logischen Listenelement der Wert .TRUE. oder .FALSE. zugeordnet, wenn die
ersten von Leerzeichen verschiedenen Zeichen T oder .T bzw. F oder .F sind. Bei der Ausgabe werden
.TRUE. und .FALSE. rechtsbündig mit w − 1 führenden Leerzeichen als T bzw. F ausgegeben.
Beispiel:
LOGICAL :: ok, ko
ok = .TRUE.
ko = .FALSE.
PRINT 135, ok, ko
135 FORMAT (’ ’, 2L3)
erzeugt die Ausgabe:
Spalten:
Zeichen:
1234567890
T F
Der Schrägstrich / ( Slash“) innerhalb einer Formatangabe zeigt das Ende des letzten und den Beginn
”
eines neuen Datensatzes an, es wird also eine neue Zeile begonnen.
Das X–Format wird verwendet, um Zeichen zu überspringen. Seine Form ist
nX
und n ist eine ganze Zahl > 0, die die Anzahl der zu überspringenden Zeichenpositionen angibt, ausgehend von der augenblicklichen Position. Bei der Ausgabe werden übersprungene noch nicht gefüllte
Positionen des Datensatzes mit Leerzeichen gefüllt. Das X–Format sollte besser durch das allgemeinere
Tabulatorformat T, insbesondere den Rechtstabulator TRn ersetzt werden.
4.2. FORTRAN
47
Einzelne Formatspezifikationen oder ganze Gruppen können durch Angabe eines Wiederholungsfaktors wiederholt werden. Gruppen werden dabei in Klammern eingeschlossen.
Beispiel:
102 FORMAT (3I4, 2(1X, 2F6.1))
entspricht:
102 FORMAT (I4, I4, I4, 1X, F6.1, F6.1, 1X, F6.1, F6.1)
Das erste Zeichen eines jeden Datensatzes wird bei der Ausgabe auf manchen Druckern nicht ausgedruckt
sondern zur Verschubsteuerung benutzt. Bei anderen Ausgabemedien gehört es zu den Daten. Es gibt
drei Zeichen, die besondere Aktionen des Druckers bewirken können:
0
1
+
eine Leerzeile vor dem Drucken
Vorschub auf den Anfang einer neuen Seite
kein Vorschub, in der gleichen Zeile weiterdrucken.
Um einer eventuellen Fehlinterpretation der Daten in der ersten Spalte jedes Datensatzes beim Fortlassen
der Vorschubzeichen aus dem Weg zu gehen,sollte bei der Ausgabe das erste Zeichen jeder Zeile ein Leerzeichen sein. Dieses Leerzeichen sollte explizit angegeben werden und nicht in einer Formatspezifikation
versteckt“ werden. Also:
”
100 FORMAT (1X, I4, 2F5.2)
und nicht
100 FORMAT (I5, 2F5.2)
4.2.14.2
Externe Ein-/Ausgabe
Wir haben bisher die Ein-/Ausgabe über die spezielle Standard– Datei“ Konsole (also Tastatur und
”
Bildschirm) kennengelernt. Die allgemeine Ein-/Ausgabe erfolgt über sogenannte externe Dateien.
Das sind Dateien, die auf einem beliebigen Speichermedium (Platte, Diskette, Band etc.) vorliegen. Diese
Dateien haben einen Namen; er dient der Identifikation dieser Datei durch das FORTRAN–Programm
einerseits und das Betriebssystem andererseits. Vor dem ersten Zugriff auf eine (neue oder bereits existierende) Datei muß die Datei geöffnet werden, wobei sie für die Ein-/Ausgabe eingerichtet und mit
gewissen Dateiattributen versehen wird. Wir werden hier nur sequentielle Dateien behandeln.
Sequentielle Dateien kann man sich vorstellen als Dateien mit Zeilenstruktur. Jede Zeile enthält Daten
in Form eines Datensatzes (record). Records können formatiert oder unformatiert sein. Auf die Records
kann nur sequentiell (d.h. der Reihe nach) zugegriffen werden: auf eine bestimmte Zeile kann nur zugegriffen werden, wenn alle davorliegenden Zeilen bereits gelesen oder übersprungen wurden.
Bei jeder Anweisung zum Zugriff auf Dateien kann abgefragt werden, ob ein Fehler aufgetreten ist, z.B.
durch Fehlen der Datei oder Erreichen des Dateiendes. Werden diese Abfragen nicht durchgeführt, bricht
das Programm im Fehlerfall ab.
Auf Dateien wird in E/A–Anweisungen über die E/A–Einheit (auch: E/A–Kanal) Bezug genommen. Zum
Beispiel spezifiziert die Anweisung
READ (2, 100) X, Y
48
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
die E/A–Einheit 2. Die Leseoperation wird auf der Datei durchgeführt, die der Einheit 2 zugeordnet ist.
Die Zuordnung von Einheiten zu Dateien geschieht mit dem UNIT–Parameter in der OPEN–Anweisung
(siehe unten).
In den Ein-/Ausgabeanweisungen können Parameter verwendet werden. Die wichtigsten E/A–Parameter werden im folgenden kurz erläutert.
UNIT=u spezifiziert die E/A–Einheit für den Zugriff auf eine externe Datei oder spezifiziert die interne
Datei (siehe interne Ein-/Ausgabe“). Für u gibt es folgende Möglichkeiten:
”
- ein * bedeutet die Standardeinheit (in der Regel die Konsole),
- ein ganzzahliger Ausdruck mit 0 ≤ u ≤ 999,
- der Name einer Zeichengröße, die eine interne Datei definiert.
UNIT=“ kann entfallen, wenn u der erste Parameter ist.
”
Standard–E/A–Einheiten sind meist UNIT=5 für die Eingabe, UNIT=6 für die Ausgabe,
d.h. diese Angaben wirken wie UNIT=*.
FMT=fn Formatangabe für formatierte Ein-/Ausgabe. Für fn gibt es unter anderem folgende Möglichkeiten:
- die Anweisungsmarke einer FORMAT–Anweisung in der selben Programmeinheit,
- eine Zeichengröße, deren Wert die Formatangabe enthält,
- ein Stern * für listengesteuerte E/A.
Wenn UNIT=“ fehlt, darf auch FMT=“ fehlen, und die Formatangabe muß an zweiter Stelle
”
”
in der Liste stehen.
IOSTAT=ios Mit dem optionalen IOSTAT–Parameter spezifiziert man eine ganzzahlige Variable ios, der,
je nach Korrektheit der Abbarbeitung der Anweisung, einer der folgenden Werte zugewiesen
wird:
ios = 0 : die Abbarbeitung verlief fehlerfrei!
ios > 0 : eine Fehlerbedingung ist aufgetreten!
ios < 0 : das Dateiende wurde (beim Lesen) erreicht!
END=labl Marke einer ausführbaren Anweisung, zu der bei Erreichen des Dateiendes beim Lesen verzweigt wird. (Optionaler Parameter)
ERR=labl Marke einer ausführbaren Anweisung, zu der im Fehlerfall verzweigt wird. (Optionaler Parameter)
iolist E/A-Liste.
Weitere Parameter können in einem FORTRAN–Handbuch nachgelesen werden.
Bei formatgebundener Ein-/Ausgabe muß eine Formatangabe spezifiziert werden. Die E/A-Liste
darf fehlen. Jede formatgebundene E/A–Anweisung überträgt einen oder mehrere Datensätze. Formatgebundenes Lesen erfolgt mit der Anweisung
READ (UNIT = u, FMT = f n, IOSTAT = ios, END = labl, ERR = labl) iolist
oder kürzer
READ (u, f n, IOSTAT = ios, END = labl, ERR = labl) iolist
Formatgebundenes Schreiben erfolgt mit der Anweisung
4.2. FORTRAN
49
WRITE (UNIT = u, FMT = f n, IOSTAT = ios, ERR = labl) iolist
oder kürzer
WRITE (u, f n, IOSTAT = ios, ERR = labl) iolist
oder auf die Standardausgabeeinheit mit
PRINT f n, iolist
Diese Anweisung wirkt wie WRITE(*,f n) iolist . Es gelten die bei den E/A- Parametern gemachten
Bemerkungen.
Beispiel (die OPEN–Anweisung wird unten erklärt):
! --- Beispiel fuer Ein-/Ausgabe
PROGRAM IOTest
IMPLICIT NONE
REAL :: a, b
INTEGER :: io1, io2, io3
! --- vorhandene Datei oeffnen (Zuordnung Kanal 2):
OPEN (2, FILE=’Datei.2’, IOSTAT=io1, STATUS=’OLD’)
IF (io1 == 0) THEN
! --- Formatiert von Kanal 2 einlesen:
READ (2, 100, IOSTAT = io2) a, b
IF (io2 == 0) THEN
! --- Formatiert auf Console ausgeben:
WRITE (*, 110, IOSTAT = io3) a, b
! --- Ausgabe bei Schreibfehler:
IF (io3 /= 0) PRINT *, ’ E/A-Fehler!’
ELSEIF (io2 < 0) THEN
! --- Ausgabe bei Dateiende:
PRINT *, ’ Dateiende!’
ELSE
! --- Ausgabe bei Lesefehler:
WRITE (*,*) ’ E/A-Fehler!’
ENDIF
ELSE
! --- Ausgabe falls die Datei nicht existiert:
PRINT *, ’ Fehler beim Oeffnen!’
ENDIF
! --- Formatangaben:
100 FORMAT (2F10.4)
110 FORMAT (1X, 2(F10.4, 2X) )
END
Falls während des Öffnens der Datei ein Fehler auftritt, weil die Datei nicht existiert, wird der INTEGER–
Variablen io1 ein Wert größer als Null zugewiesen. Der IF-Block wählt nun den Pfad für die zugehörige
Fehlerausgabe. Sonst werden entsprechend der FORMAT–Anweisung 100 zwei Werte gelesen. Beim Lesen
50
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
oder dem anschließenden Schreiben gemäß FORMAT–Anweisung 110 verzweigt das Programm wieder
in Abhängikeit davon, ob io2 und io3 größer, kleiner oder gleich Null sind, und gibt im Fehlerfall eine
passende Meldung auf dem Bildschirm aus.
Die Möglichkeit der formatfreien (unformatierten) Ein-/Ausgabe soll hier auch noch erwähnt werden. Dabei werden die Daten nicht konvertiert, sondern in binärer Form übertragen. Diese E/A-Methode
wird benutzt, um Daten schnell zwischenzuspeichern, um sie z.B. in einem nachfolgenden Programmlauf
weiterzuverarbeiten. Näheres entnehme man der FORTAN–Literatur.
Die Anweisungen für listengesteuerte Ein-/Ausgabe entsprechen formal denen für formatierte E/A, mit
der Einschränkung, daß für die Formatangabe ein Stern gesetzt wird: FMT=*. Wir haben den Spezialfall
der listengesteuerten E/A über Standard-E/A-Einheiten schon in Abschnitt 4.2.13 kennengelernt.
Mit Ein-/Ausgabe–Status–Anweisungen werden Eigenschaften einer Datei festgelegt, geprüft oder
geändert. Hier sollen nur die wichtigsten Parameter der Anweisungen angegeben werden.
Mit der OPEN–Anweisung
OPEN (UNIT = u, FILE = f il, STATUS = stat, IOSTAT = ios, ERR = labl)
wird die dem FORTRAN–Programm bekannte E/A-Einheit u der Datei mit dem Namen f il (auf Betriebssystemebene) zugeordnet. f il ist dabei ein Zeichenausdruck. Das Programm nimmt also auf die
Datei f il Bezug durch die E/A-Einheit mit der Nummer u. stat ist ebenfalls ein Zeichenausdruck, der
den erwarteten Status der zu öffnenden Datei angibt. Dabei kann stat folgende Werte entahlten
’OLD’
’NEW’
:
:
’UNKNOWN’
:
die Datei soll bereits existieren
die Datei darf noch nicht existieren, sondern wird erst mit dieser
Anweisung angelegt
das ist die Standardeinstellung; existiert sie noch nicht, wird sie
angelegt, ansonsten wird die vorhandene Datei geöffnet
Im Fehlerfall wird der Variablen ios ein Wert ungleich Null zugewiesen und/oder zur Anweisung mit der
Marke labl verzweigt. Die Angabe der E/A-Einheit u ist unbedingt erforderlich, die anderen Parameter
können gegebenenfalls fehlen.
Beispiel:
OPEN (6, FILE = ’OUTPUT’)
Neben der Zuordnung lassen sich mit der OPEN–Anweisung auch Details des Zugriffs auf die Datei
(Dateiattribute) festlegen. Wir werden hierauf nicht weiter eingehen und verweisen auf weiterführende
FORTRAN–Literatur.
Mit der CLOSE–Anweisung
CLOSE (UNIT = u)
wird eine Datei von der E/A-Einheit u getrennt. Danach kann z.B. die E/A-Einheit u mit einer anderen
Datei verbunden werden. Die abgetrennte Datei kann z.B. zurückgegeben werden. Bei Programmende
(END, STOP) findet ein selbständiges CLOSE statt.
Zur Abfrage des Zustands einer E/A-Einheit oder einer Datei dient die INQUIRE–Anweisung. Sie soll
hier nicht weiter besprochen werden; hierfür wird auf die FORTRAN–Literatur (z.B. [11] ) verwiesen.
Zur Positionierung von sequentiellen Dateien gibt es drei Anweisungen.
Die REWIND–Anweisung
REWIND (UNIT = u, ERR = labl)
4.2. FORTRAN
51
setzt eine Datei auf den Anfang zurück, so daß der nächste in einer E/A–Anweisung angesprochene
Datensatz der erste Datensatz dieser Datei ist.
Die BACKSPACE–Anweisung
BACKSPACE (UNIT = u, ERR = labl)
setzt die E/A-Einheit u um einen Datensatz (d.h. um eine Zeile) zurück.
Die ENDFILE–Anweisung
ENDFILE (UNIT = u, ERR = labl)
schreibt einen speziellen Datensatz, den sogenannten Dateiendesatz auf die E/A-Einheit u. Diese Dateiendesätze können beim Lesen z.B. mit dem E/A–Parameter END=“ erkannt werden. Das Dateiende
”
wird aber auch erkannt wenn kein Dateiendesatz vorhanden ist.
In allen drei Positionierungsanweisungen ist die Angabe von u erforderlich; das Schlüsselwort UNIT=“
”
kann entfallen, wenn u der erste Parameter ist.
4.2.14.3
Interne Ein-/Ausgabe
Die interne Ein-/Ausgabe ermöglicht den Datentransport von einem Bereich des Arbeitsspeichers zu
einem anderen, ohne Benutzung von E/A-Geräten oder externer Speichermedien und erlaubt gleichzeitig
die Änderung der Darstellung, z.B. Umwandlung numerischer Daten in Zeichendaten und umgekehrt.
Interne Ein-/Ausgabe wird nur mit formatgebundenen READ- und WRITE–Anweisungen vorgenommen und
verwendet Variablen vom Typ CHARACTER als interne Dateien“. Diese enthalten einen einzigen Datensatz;
”
Positionierungs- und Statusanweisungen sind daher für interne Dateien sinnlos. Bei der Ausgabe werden
die Daten der Formatangabe entsprechend in Zeichen umgewandelt (wie auch bei externen formatierten
Dateien) und in der Zeichengröße gespeichert. Bei der Eingabe werden die Zeichen der internen Datei der
Formatangabe entsprechend konvertiert und an die in der Eingabeliste stehenden Größen übergeben.
Beispiel:
CHARACTER (LEN=20) :: IntDat
INTEGER :: NR, Neu
IntDat = ’Dies sind 20 Zeichen’
READ (IntDat, 100) NR
WRITE (*,100) NR
100 FORMAT (10X, I2)
Neu = 99
WRITE (IntDat(11:12), 102) Neu
102 FORMAT (I2)
PRINT*, IntDat
erzeugt die Ausgabe
Spalten: 12345678901234567890
20
Dies sind 99 Zeichen
4.2.15
Programmeinheiten und Anweisungen
Zur Lösung eines komplexen Problems ist es sinnvoll, das Gesamtproblem in mehrere Teilprobleme zu
zerlegen. Durch diese Unterteilung lassen sich Anschaulichkeit und Übersichtlichkeit bewahren. FORT-
52
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
RAN unterstützt dies durch Unterprogramme, die für sich selbständige Teilprogramme darstellen. Die
Verwendung von Unterprogrammen hat einige Vorteile:
• modularer Programmaufbau; Unterprogramme können einzeln programmiert und ausgetestet werden. Das erleichtert die Fehlersuche, da die Übersichtlichkeit gewahrt bleibt.
• wiederholt zu durchlaufende Programmteile brauchen nur einmal programmiert werden und werden
als Unterprogramm nur immer wieder aufgerufen.
• Unterprogramme zur Lösung von Teilproblemen können in anderen Programmen verwendet werden,
siehe insbesondere numerische Standardverfahren, z.B. für Interpolation, Lösung von Gleichungssystemen etc.
Außerdem existieren für etliche Problemstellungen (Berechnung mathematischer Funktionen) bereits fertige Unterprogrammbibliotheken, die durch entsprechende Aufrufe in beliebigen Programmeinheiten verwendet werden können.
Ein fertiges Programm besteht somit mindestens aus einem Hauptprogramm, das mit einer PROGRAM–
Anweisung beginnt, und gegebenenfalls einem oder mehreren Unterprogrammen. Jedes dieser Unterprogramme stellt eine Programmeinheit dar, die aus einer Reihe von FORTRAN–Anweisungen mit wahlweise
eingestreuten Kommentaren besteht, und die mit einer END–Anweisung abgeschlossen werden muß, siehe
auch Abschnitt 4.2.2. Unterprogramme können nur zusammen mit einem Hauptprogramm ausgeführt
werden.
Ein Vereinbarungsunterprogramm enthält keine ausführbaren Anweisungen und wird daher als nichtausführbares Programm bezeichnet. Es beginnt mit einer BLOCKDATA–Anweisung, und in ihm kann man
alle möglichen Vereinbarungen treffen (Typdefinitionen, COMMON–Blöcke, Feldvereinbarungen) und z.B.
Größen in benannten gemeinsamen Speicherbereichen (COMMON–Blöcke) Anfangswerte zuweisen.
Ausführbare Unterprogramme sind Subroutinen und (benutzerdefinierte) Funktionen, die mit einer
SUBROUTINE– bzw. FUNCTION–Anweisung beginnen und mit einer END–Anweisung enden. Ein Unterprogramm kann Größen von der rufenden Programmeinheit über eine Parameterliste und/oder über gemeinsame Speicherbereiche übernehmen, verarbeiten und auch an die rufende Programmeinheit zurückgeben.
Eine Subroutine stellt die Lösung einer abgeschlossenen Teilaufgabe dar. Ihr Aufruf erfolgt über eine
besondere Anweisung.
Eine Funktion wird ausschließlich in (arithmetischen, logischen oder Zeichen–) Ausdrücken verwendet;
sie liefert über ihren Namen einen Funktionswert als Operanden für den Ausdruck. Die vier Arten von
Funktionen sind benutzereigene interne und externe Funktionsunterprogramme (FUNCTION), Formelfunktionen (Statement function), sowie vordefinierte Funktionen, die von FORTRAN fix und
fertig zur Verfügung gestellt werden. Prinzipiell ist Fortran95 zur Rekursion, also der Möglichkeit eines
Unterprogrammes sich selbst direkt oder indirekt aufzurufen, fähig. Es wird jedoch kein Bestandteil dieses
Skriptes sein und sollte bei Bedarf in weiterführender Literatur nachgeschlagen werden. Erwähnt werden
soll nur noch, daß sie mit Vorsicht genossen werden sollte, da die Gefahr der Endlos–Rekursion besteht!
4.2.16
Parameterlisten
Bei den Parameterlisten unterscheidet man Aktual- und Formalparameter. Aktualparameter erscheinen
in der Parameterliste in der aufrufenden Programmeinheit. Die aufrufende Programmeinheit übergibt diese Aktualparameter über die Liste an das aufgerufene Unterprogramm. Das Unterprogramm übernimmt
die Werte der entsprechenden Aktualparameter und liefert die gegebenenfalls neu berechneten Werte über
die Liste wieder an die aufrufende Programmeinheit zurück. Aktualparameter können neben Konstanten
auch symbolische Konstanten, Namen von Variablen, Feldern und Funktionen sein.
4.2. FORTRAN
53
Formalparameter erscheinen in der Parameterliste des aufgerufenen Unterprogramms. Bei der Ausführung des Unterprogramms werden die Werte (genauer: die Speicheradressen dieser Werte, call by
”
reference“ ) der Aktualparameter den entsprechenden Formalparametern des Unterprogramms zugeordnet
und ersetzen diese. Die Formalparameter sind notwendig, um Typ, Art und Anzahl der Aktualparameter
festlegen zu können. Die Aktualparameter müssen hinsichtlich Anzahl, Typ und Reihenfolge mit den
Formalparametern übereinstimmen.
Nach ihrer Funktion unterscheidet man drei Arten von Parametern:
• Eingangs- oder Eingabe–Parameter,
• Ausgangs- oder Resultat–Parameter und
• Durchgangs–Parameter.
Eingabe–Parameter (auch IN–Parameter) werden von rufenden Programm an das Unterprogramm
übergeben, ihre Werte werden dort ohne Änderung benutzt.
Ausgabe–Parameter (auch OUT–Parameter) sind Ergebnisse von Unterprogrammen, deren Werte an
das rufende Programm zurückgeben werden.
Durchgangs–Parameter (auch IN/OUT–Parameter) sind Größen, die vom rufenden Programm an das
Unterprogramm übergeben werden, dort meist mit anderen Werten versehen werden, und dann an das
rufende Programm zurückgeliefert werden.
In Fortran95 kann man die Funktion eines Formalparameters mit dem INTENT–Attribut festlegen. Die
drei Parameter–Arten sind dabei mittels Angabe von INTENT(IN), INTENT(OUT) oder INTENT(INOUT) zu
bestimmen.
4.2.17
Vordefinierte Standardfunktionen
Eine Reihe von Funktionen wie sin, cos, tan, log, Wurzel usw. werden in Anwendungen häufig benötigt.
Solche und andere Funktionen, z.B. zur Typkonvertierung werden von FORTRAN fertig zur Verfügung
gestellt. Sie sind im Compiler bereits definiert und liefern jeweils einen einzelnen Wert. Diesen erhält
man durch Aufruf der Funktionen unter ihrem Namen mit dem gewünschten Argument, also durch einen
Funktionsaufruf in einem Ausdruck.
Zum Beispiel liefert
LOG(x)
den natürlichen Logarithmus des Arguments x. Der Typ des Arguments x kann reell, doppelt genau
oder komplex sein. Der Typ des Ergebnisses eines Funktionsaufrufs ist mit Ausnahme z.B. der Typenumwandlungen gleich dem Typ des Arguments. Das Beispiel zeigt die Verwendung einer generischen,
d.h. für Parameter verschiedenen Typs anwendbaren Funktion. Andere interne Funktionen sind spezifisch, d.h. nur für genau einen Parametertyp anwendbar. So berechnen die spezifischen Funktionen
ALOG, DLOG und CLOG ebenfalls den natürlichen Logarithmus für ein reelles, doppelt genaues bzw. komplexes Argument und liefern ein reelles, doppelt genaues bzw. komplexes Ergebnis. Allgemein sollten die
generischen Funktionsnamen verwendet werden. Soll jedoch der Name einer internen Funktion als Aktualparameter an ein Unterprogramm übergeben werden, muß ein geeigneter spezifischer Name verwendet
werden.
54
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
4.2.18
Benutzerdefinierte Funktionen
Zu den benutzereigenen Unterprogrammen mit Funktionscharakter gehören die in diesem Abschnitt zu
besprechenden Formelfunktionen, die internen und die externen Funktionen.
Eine Formelfunktion (statement function) wird definiert durch eine einzige, vom Programmierer in der
aufrufenden Programmeinheit anzugebende nichtausführbare Anweisung. Sie ähnelt einer Zuweisung. Die
Definition einer Formelfunktion muß vor der ersten ausführbaren Anweisung und hinter den Vereinbarungsanweisungen vorgenommen werden. Die allgemeine Definitionsanweisung lautet:
Stfun (p1 , p2 , p3 ,. . . ) = expr
Der Name der Funktion ist Stfun und pi sind Formalparameter (Variablennamen). Der Wert des Ausdrucks expr ist der Wert der Formelfunktion nach deren Ausführung. Die Formelfunktion wird über ihren
Namen aufgerufen:
Stfun (a1 , a2 , a3 , . . . )
Die Aktualparameter ai sind Ausdrücke. Wird der Name der Formelfunktion in einem Ausdruck angetroffen, wird die Formelfunktion ausgewertet. Dazu werden die Werte der Aktualparameter berechnet und
anstelle der Formalparameter bei der Auswertung des Ausdrucks expr verwendet. Eine Formelfunktion
kann nur in der sie definierenden Programmeinheit aufgerufen werden. Ihr Name ist lokal, d.h. dieser
Programmeinheit zugeordnet und darf nicht mit irgendeinem anderen Namen dieser Programmeinheit
übereinstimmen.
Beispiel für Definition und Verwendung einer Formelfunktion:
PROGRAM demo
! --- Polynomauswertung nach HORNER-Schema
IMPLICIT NONE
REAL
:: XAnf, XEnd, XI, DX, FXI, A0, A1, A2, A3
REAL
:: A, B, C, D, X
! --- Die Formelfunktion HORNY muss selbst auch deklariert werden
REAL
:: HORNY
INTEGER :: I, NX
! --- Definition des Hornerschemas als Statementfunction
HORNY (A, B, C, D, X) = D + X*(C + X*(B + A*X))
! --- Eingabe PolynomKoeffizienten und Wertebereich
READ*, A0, A1, A2, A3
READ*, XAnf, XEnd, NX
! --- Berechnung und Ausgabe Funktionstabelle
DX = (XEnd - XAnf) / (NX - 1)
PRINT 100, ’WerteTabelle’
PRINT 100, ’X’,’F(X)’
DO I = 1, NX
XI = Xanf + (I - 1)*DX
FXI = HORNY (A0, A1, A2, A3, XI)
PRINT 110, XI, FXI
ENDDO
100 FORMAT(2A15)
110 FORMAT(2F15.5)
END
Die Verwendung von Formelfunktionen ist allerdings nicht sehr zukunftsweisend, da sie in nachfolgenden
FORTRAN-Versionen nicht mehr unterstützt werden wird. Da die Formelfunktion ohnehin nur lokal, d.h.
in einem Unterprogramm, Gültigkeit besitzt, ist sie durch die interne Funktion abgelöst worden.
4.2. FORTRAN
55
Die interne ist wie die externe Funktion ein benutzereigenes Unterprogramm, das eine Reihe von Anweisungen ausführt und einen Wert über seinen Namen zurückliefert. Sie werden über die FUNCTION–
Anweisung
typ FUNCTION fun (d1 , d2 , . . . )
definiert. typ ist der Typ der Funktion, welcher nicht nur jeder beliebige skalare Datentyp sein kann,
sondern auch ein Feld beliebigen Typs, und f un ist ihr Funktionsname. (Die Zuweisung eines Typs zum
Funktionnamen darf sogar auch erst innerhalb der Funktion selbst erfolgen.) Die Formalparameter di
können Variablennamen, Feldnamen oder Namen anderer Unterprogramme sein. Der Name der Funktion
ist innerhalb der Funktion selbst ein Variablenname, der nur auf der linken Seite einer Zuweisung stehen
darf. Diese Variable muß während der Ausführung der Funktion definiert d.h. mit einem Wert versehen
werden. Der Wert der Funktion beim Rücksprung (Antreffen von END FUNCTION) ins rufende Programm
ist dann der Wert dieser Variablen.
Prinzipielle Struktur einer Funktion:
typ FUNCTION name (d1 , d2 , ...)
:
name = wert
:
END FUNCTION
Der Aufruf einer internen oder externen Funktion geschieht über ihren Funktionsnamen. Die Funktion
wird ausgeführt, wenn ihr Name als Operand innerhalb eines Ausdrucks erscheint. Die Funktion übernimmt Werte von der aufrufenden Programmeinheit über eine Parameterliste oder über gemeinsame
Speicherbereiche; sie liefert einen Wert über ihren Namen zurück (oder auch mehrere Werte über die
Parameterliste oder gemeinsame Speicherbereiche).
Der Typ des Ergebnisses ist indirekt durch die Deklaration des Funktionsnamen festgelegt. Die Aktualparameter müssen hinsichtlich Anzahl, Typ und Reihenfolge mit den Formalparametern übereinstimmen,
ihre Namen können unterschiedlich sein.
Zwischen internen oder externen Funktion gibt es nun folgende Unterschiede:
Die interne Funktion
Die interne Funktion wird innerhalb einer Programmeinheit definiert, welche das Hauptprogramm, eine
Unterroutine, aber auch eine andere Funktion sein kann. Die Definition der Funktion findet zwischen der
CONTAINS– Anweisung und der END–Anweisung der umgebenen Programmeinheit statt:
PROGRAM main
:
CONTAINS
typ FUNCTION name (d1 , d2 , ...)
:
name = wert
:
END FUNCTION
56
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
END
Die interne Funktion ist nur lokal in der umgebenen Programmeinheit bekannt, muß aber bei expliziter Deklaration noch mittels Deklaration des Funktionsnamens innerhalb der Einheit bekannt gemacht
werden. Außerdem kann eine interne Funktion, ebenso wie Formelfunktionen, auf alle Variablen des umgebenen Unterprogramms zurückgreifen, und das auch ohne Kommunikation über die Parameterliste.
Die externe Funktion
Externe Funktionen sind gegenüber den internen global verfügbar. Ihr Name ist überall im Programm
bekannt und darf daher im gesamten Programm nur ein einziges Mal auftauchen. Ihre Definition findet äuquivalent zum Hauptprogramm als eigenständiger Programmteil (also außerhalb jedes anderen
Unterprogramms) statt und sieht wie folgt aus:
typ FUNCTION name (d1 , d2 , ...)
:
name = wert
:
END FUNCTION
Da die externe Funktion eigenständig definiert wird, sind in ihr nur die Werte bekannt, die ihr mittels
Parameterliste, COMMON-Block, o.ä. zugänglich gemacht werden. Der Vorteil einer externen Funktion
liegt offensichtlich in ihrer globalen Natur, da sie innerhalb des gesamten Programms aufgerufen werden
kann. Im Prinzip hängt die Entscheidung für einen der Funktionsarten natürlich immer vom jeweiligen
Verwendungszweck ab.
Beispiel:
PROGRAM vektor
IMPLICIT NONE
INTEGER :: i
REAL :: a(3), b(3), sp, SKALP
READ*, (a(i), b(i), i = 1,3)
sp = SKALP (a, b, 3)
PRINT*, ’Das Skalarprodukt ist:’, sp
PRINT*, ’Aus der Vektoraddition ergibt sich: ’,VEKTADD(3)
CONTAINS
! --- Funktion zur Addition zweier Vektoren
FUNCTION VEKTADD (dimen)
IMPLICIT NONE
INTEGER :: k, dimen
REAL, DIMENSION(dimen) :: VEKTADD
DO k=1, dimen
! --- a() und b() sind aus dem umgebenen Programm bekannt
VEKTADD (k) = a(k) + b(k)
ENDDO
END FUNCTION
END
! --- Funktion zur Berechnung des Skalarprodukts zweier Vektoren
REAL FUNCTION SKALP (v1, v2, n)
4.2. FORTRAN
57
IMPLICIT NONE
INTEGER :: n, ik
REAL :: v1(n), v2(n)
SKALP = 0.0
DO ik = 1, n
SKALP = SKALP + v1(ik)*v2(ik)
ENDDO
END FUNCTION
Im Zusammenhang mit Funktionen soll noch kurz auf den Unterschied zwischen lokalen und globalen
Größen eingegangen werden. Alle in Programmeinheiten auftretenden Namen und Anweisungsnummern
haben lokale Bedeutung, d.h. sie sind nur in der jeweiligen Programmeinheit bekannt und dürfen außerhalb, gegebenenfalls mit anderer Bedeutung, wieder verwendet werden. Der Name einer externen Funktion
ist dagegen eine globale Größe; er darf weder mit anderen globalen Namen (z.B. Name der rufenden
Programmeinheit) noch mit lokalen Namen der betreffenden Programmeinheit identisch sein (Ausnahme: gleichnamige Variable in der FUNCTION selbst). Globale Größen sind für das gesamte Programm
von Wichtigkeit. Weitere globale Größen sind z.B. die Namen gemeinsamer Speicherbereiche (benannte
COMMON–Blöcke) oder benutzerdefinierte Typen (TYPE).
4.2.19
Benutzerdefinierte Unterprogramme – Subroutinen
Während Funktionsunterprogramme bei jedem Aufruf einen einzigen Wert — den Funktionswert —
berechnen, können in FORTRAN mit Subroutinen–Unterprogrammen mehrere Werte bei einem Aufruf
berechnet und der rufenden Programmeinheit zur Verfügung gestellt werden. Eine Subroutine beginnt
stets mit der SUBROUTINE–Anweisung
SUBROUTINE sname (d1 , d2 , ...)
Der Name sname dient ausschließlich zur Identifikation der Subroutine. Er besitzt keinen Datentyp
und kann nicht zur Werteübergabe an das rufende Programm benutzt werden. Die Parameterübergabe
zwischen rufendem Programm und Subroutine findet über die Parameterliste statt, wobei di die formalen
Parameter sind. Daneben ist noch eine Parameterübergabe über gemeinsame Speicherbereiche (siehe
unten) möglich. Jede Subroutine muß mit einer END SUBROUTINE–Anweisung abgeschlossen werden und
kann eine RETURN–Anweisung enthalten, die den vorzeitigen Rücksprung ins rufende Programm bewirkt.
Es gibt auch bei Subroutinen interne und externe Subroutinen. Ihre Unterscheidung bezüglich Gültigkeit und Definition entsprechen denen der internen und externen Funktionen, und sollen deshalb hier
nicht erneut aufgeführt werden.
Subroutinen stellen selbständige Programmeinheiten dar, weshalb auch alle erforderlichen Spezifikationen (z.B. Typdeklarationen) angegeben werden müssen. Der Aufruf einer Subroutine geschieht mit der
CALL–Anweisung
CALL sname (a1 , a2 , ...)
Der Name der gerufenen Subroutine ist sname und ai sind die Aktualparameter. Diese müssen, wie auch
bei Funktionen, bezüglich Anzahl, Typ und Reihenfolge mit den Formalparametern übereinstimmen. Die
Parameter der Liste können Eingabe- oder Ausgabeparameter sein oder auch beide Funktionen gleichzeitig
erfüllen. Aus Gründen der Übersichtlichkeit sollte man jedoch für Eingabe-, Ausgabe- und Durchgangs–
Parameter jeweils unterschiedliche Parameter verwenden.
Beispiel:
PROGRAM Extrem
58
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
IMPLICIT NONE
INTEGER :: i
INTEGER, PARAMETER :: NDIM = 10
REAL :: x(NDIM), xmin, xmax
PRINT*, ’Bitte die 10 Feldwerte eingeben:’
READ*, (x(i), i = 1,10)
CALL MinMax (x, NDIM, xmin, xmax)
PRINT*, ’Das Minimum betraegt:’, xmin
PRINT*, ’Das Maximum betraegt:’, xmax
END
! --- Subroutine zur Berechnung von Minimum und Maximum eines Feldes
SUBROUTINE MinMax (f, n, fmi, fma)
IMPLICIT NONE
INTEGER :: n, i
REAL :: f(n), fmI, fmA
fmI = f(1)
fmA = f(1)
DO i = 1, n
fmi = MIN (fmi, f(i))
fma = MAX (fma, f(i))
ENDDO
END SUBROUTINE
Im Hauptprogramm werden 10 Werte über die Tastatur eingegeben. Zur Ermittlung der Extremwerte
wird das Feld x mit den 10 Werten an das Unterprogramm übergeben. Ein weiterer Eingabeparameter
ist die aktuelle Dimension NDIM des Feldes. Die beiden letzten Parameter der Liste xmin und xmax sind
Ausgabeparameter, die gesuchten Extremwerte.
Das Unterprogramm MinMax benutzt variable Dimensionierung, d.h. es ist für allgemeine, beliebig
große Felder geschrieben. Die jeweilige aktuelle Dimensionierung (hier: n) muß dann als Parameter vom
rufenden Programm übergeben werden. Beim Aufruf des Unterprogramms werden die Werte der aktuellen
Parameter (im rufenden Programm) den formalen Parametern (im Unterprogramm) zugeordnet, und zwar
in der Reihenfolge ihres Auftretens in der Parameterliste: x→f, NDIM→n, xmin→fmi und xmax→fma.
Der Algorithmus zur Extremwertbestimmung ist wie folgt: Das Unterprogramm setzt zunächst Minimum
und Maximum auf den Wert des ersten Feldelements. In der Schleife werden dann durch Vergleich der
bisher gefundenen Extremwerte mit dem laufenden Feldelement die globalen Extremwerte ermittelt.
Der FORTRAN–Standard besagt, daß beim Rücksprung in das rufende Programm alle lokalen Größen
nicht mehr definiert sind. Möchte man dem entgegenwirken muß für die betreffenden Variablen das
SAVE–Attribut gesetzt werden.
4.2.20
Speicherplatz–Zuordnung (COMMON) und Einbettung von Programmzeilen (INLUDE)
Bei der Übersetzung (Compilierung) bzw. beim Laden eines Programms werden den vorkommenden
Größen Speicherplätze (Adressen) zugeordnet. Diese Zuordnung kann durch die im folgenden besprochenen Anweisungen beeinflußt werden.
Die COMMON–Anweisung ordnet Variablen verschiedener Programmeinheiten gleiche Plätze im Arbeitsspeicher zu und erlaubt daher, neben der Parameterliste, die Übertragung von Werten zwischen Programmeinheiten.
Formalparametern eines Unterprogramms, also allen Parametern der Parameterliste, werden keine Spei-
4.2. FORTRAN
59
cherplätze zugewiesen. Vielmehr werden beim Aufruf dem gerufenen Unterprogramm die Speicheradressen
der aktuellen Parameter im rufenden Programm übermittelt.
Daraus ergibt sich die folgende Regel:
Formalparameter von Unterprogrammen dürfen nicht in einer COMMON–, SAVE–, DATA– oder EQUIVALENCE2 –Anweisung stehen. Umgekehrt dürfen aber Größen, die in einer solchen Anweisung auftreten
als Aktualparameter in Unterprogrammaufrufen verwendet werden.
Normalerweise werden Programme und Daten für jede Programmeinheit getrennt gespeichert, so daß
beispielsweise eine Größe, die in einem Unterprogramm gespeichert ist und nicht in der Parameterliste oder
einem Unterprogrammaufruf steht, von keiner anderen Programmeinheit aus erreicht werden kann (lokale
Größe). Demgegenüber gibt es noch den allen Programmeinheiten gemeinsamen Speicherbereich
(COMMON–Bereich), der über die COMMON–Anweisung definiert wird
COMMON /name1 / liste1 , /name2 / liste2 , ...
namei ist der Name eines gemeinsamen Speicherbereichs, der einen benannten COMMON–Bereich
identifiziert und dessen Elemente in listei aufgeführt sind. Die Elemente von listei können Namen von einfachen und indizierten Variablen (ggfs. einschließlich der Dimensionen) sein, die durch Kommata getrennt
werden. Die Verwendung von COMMON–Bereichen wird im Abschnitt 4.2.21 Kommunikation zwischen Pro”
grammeinheiten“ beispielhaft erläutert.
Wird der Name des COMMON–Blocks weggelassen, so ist der gemeinsame Speicherbereich ein unbenannter
COMMON–Block.
Die Zuordnung der Namen von Komponenten eines COMMON–Blocks zu den zugehörigen Speicherplätzen
(Adressen) ergibt sich unabhängig von der Programmeinheit allein aus der Stellung der Komponenten
innerhalb der Liste listei . Innerhalb einer Programmeinheit darf ein symbolischer Name nur einmal
in einer COMMON–Anweisung auftreten, aber gemeinsame Speicherbereiche dürfen mehrmals vereinbart
werden. Dabei werden durch listei , die dem Namen des COMMON–Blocks folgt, dem COMMON–Block weitere
Elemente angehängt.
Beispiel:
COMMON /ALL/ MARS, VENUS
COMMON /ALL/ ERDE, MOND
ist gleichbedeutend mit:
COMMON /ALL/ MARS, VENUS, ERDE, MOND
Die Länge eines COMMON–Blocks ist die Gesamtzahl der Speicherplätze, die die Elemente des COMMON–
Blocks belegen. Die Länge eines benannten COMMON–Blocks muß in allen Programmeinheiten gleich sein,
der unbenannte COMMON–Block kann dagegen in verschiedenen Programmeinheiten verschiedene Längen
haben.
Das Auftreten einer Zeichengröße in einem COMMON–Block bedeutet, daß alle anderen Größen in diesem
Block ebenfalls vom Typ Zeichen sein müssen.
Elemente in benannten COMMON–Blöcken können mittels DATA–Anweisungen in Blockdaten–Unterprogrammen (BLOCKDATA, hier nicht weiter besprochen) mit Anfangswerten versehen (initialisiert) werden. Elemente im unbenannten COMMON–Block können nicht auf diese Weise initialisiert werden.
Vor der Verwendung von COMMON–Blöcken sei aber gewarnt, denn sie erfordert äußerste Sorgfalt beim
Programmieren wegen der möglichen Speicherplatzverschiebung bei unkorrekten Listen. Eine mögliche
Lösung dieses Problems könnte dier Verwendung des INCLUDE–Befehls sein.
2 EQUIVALENCE
sollte nicht verwendet werden und wird daher hier nicht weiter besprochen
60
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Die INCLUDE–Anweisung erlaubt es, Quellcodeteile, die sich in einer seperaten (externen) Datei befinden,
in den eigentlichen Quelltext einzufügen.
INCLUDE "f ilename"
Dabei wird die INCLUDE–Anweisung durch den kompletten Inhalt der angegebenen Datei f ilename ersetzt. Um sich dieses nun bei der Verwendung der COMMON–Blöcke zunutze zu machen, lagert man die
Definition des COMMON–Blockes mitsamt den Deklarationen der darin enthaltenen Variablen in ein Datei beliebigen Namens aus und "included " sie in allen Unterprogrammen, in denen der COMMON–Block
verwendet werden soll:
PROGRAM Main
INCLUDE "comm.inc"
:
CALL Subb (...)
:
END
SUBROUTINE Subb (...)
INCLUDE "comm.inc"
:
END
comm.inc:
INTEGER :: I, J
REAL :: X(100)
COMMON / COM / I, J, X
4.2.21
Kommunikation zwischen Programmeinheiten
Der Informationsaustausch zwischen Programmeinheiten kann über gemeinsame Speicherbereiche
(COMMON–Blöcke) und/oder über Parameterlisten stattfinden. COMMON–Blöcke können jedoch nicht zur
Übertragung von Werten an interne und Formel-Funktionen benutzt werden; dies geht nur über Parameterlisten. Namen von Unterprogrammen können ebenfalls nur über Parameterlisten übergeben werden.
Die Parameterübergabe über Parameterlisten ist bereits in Kapitel 4.2.20 Speicherplatzzuordnung und
”
Einbettung von Programmzeilen“ beschrieben worden. Zur Übergabe von Feldern durch Angabe des
Feldnamens ist noch zu bemerken, daß dabei lediglich die Speicheradresse des ersten Feldelements übergeben wird. Die Informationen für Zugriffe auf beliebige Feldelemente werden durch die Feldvereinbarung
(Typ–, COMMON– oder DIMENSION–Anweisung) geliefert. Felder als Formalparameter in Unterprogrammen
können mit variablen Dimensionen vereinbart werden, wenn die Elementanzahl einfach über die Parameterliste übergeben wird und man die Deklaration im Unterprogramm in Abhängigkeit dieser Größe
durchführt:
SUBROUTINE Subb (X, N)
INTEGER :: N
REAL :: X(N)
:
END
Allgemein wird die Speicheradresse eines Feldelements anhand des Indexes dieses Elements relativ
zum ersten Feldelement ermittelt, im allgemeinen ohne daß Feldgrenzenüberprüfung stattfindet. Dies
4.2. FORTRAN
61
wird gerne für trickreiche Programmierung verwendet. Außerdem birgt dies die schon in Abschnitt 4.2.3.3
Felder“ erwähnte Gefahr des Zugriffs auf falsche Speicherplätze bei der Übergabe mehrdimensionaler
”
Felder, die nicht genau wie im rufenden Programm definiert sind. In 4.2.3.4 wurde schon beispielhaft die
Speicherung eines Feldes A(3,4) erläutert. Die Reihenfolge im Speicher ist dann
A(1,1); A(2,1); A(3,1); A(1,2); A(2,2); A(3,2)...A(2,4); A(3,4),
und das Element A(2,2) befindet sich auf dem 5. Speicherplatz. Wird das Feld im gerufenen Unterprogramm mit A(4,3) dimensioniert, so ist die dort lokal wirksame Speicherplatzzuordnung
A(1,1); A(2,1); A(3,1); A(4,1); A(1,2); A(2,2)...A(3,3); A(4,3),
und für das Element A(2,2) wird jetzt auf den 6. Speicherplatz zugegriffen, der aber das Element A(3,2)
des übergebenen Feldes enthält. Felder sollten daher stets genau wie im rufenden Programm dimensioniert
werden.
Neben Variablen können auch Namen von Unterprogrammen als Parameter über die Parameterliste übergeben werden. Dazu muß in der rufenden Programmeinheit der Name einer externen (benutzereigenen) Subroutine oder Funktion mit der EXTERNAL–Anweisung
EXTERNAL f unnam
und der Name einer internen (eingebauten) Funktion mit der INTRINSIC–Anweisung
INTRINSIC f unnam
deklariert werden, damit die Subroutine oder Funktion f unnam als Aktualparameter übergeben werden
kann. Von den eingebauten Funktionen dürfen nicht als Aktualparameter verwendet werden: die Funktionen für Typkonvertierung und für lexikalische Vergleiche sowie die Minimum-/Maximumfunktionen.
Von den übrigen dürfen nur die spezifischen, nicht die generischen Namen verwendet werden (z.B. ALOG
statt LOG für natürlichen Logarithmus von reellen Zahlen).
Beispiel:
SUBROUTINE What
REAL :: Wert1, Wert2, Wert3, Wert4
REAL :: Ergeb1, Ergeb2, Ergeb3, Ergeb4
INTRINSIC ALOG, SQRT
EXTERNAL Quad, Halb
:
CALL Who (ALOG, Wert1, Ergeb1)
CALL Who (Quad, Wert2, Ergeb2)
CALL Who (SQRT, Wert3, Ergeb3)
CALL Who (Halb, Wert4, Ergeb4)
:
END SUBROUTINE
SUBROUTINE Who (Func, Argum, Result)
REAL :: Argum, Result
:
Result = Func (Argum)
:
END SUBROUTINE
REAL FUNCTION Quad (X)
REAL :: X
Quad = X*X
62
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
END FUNCTION
REAL FUNCTION Halb (X)
REAL :: X
Halb = X / 2.
END FUNCTION
Die EXTERNAL– und INTRINSIC–Anweisungen können aber auch verwendet werden, wenn man eine benutzerdefienierte Funktion oder Unterroutine schreiben möchte, die den gleichen Namen besitzt, wie eine vom
Compiler vordefinierte. In diesem Fall kann der Compiler nicht entscheiden, ob das eigene (EXTERNAL)
oder das vorgegebene (INTRINSIC) Unterprogramm benutzt werden soll. Dieses muß in dem aufrufendem
Modul explizit angegeben werden:
SUBROUTINE vordefiniert
INTRINSIC SQRT
:
zahl1 = SQRT(wert1)
:
END SUBROUTINE
SUBROUTINE eigene
EXTERNAL SQRT
:
zahl2 = SQRT(wert2)
:
END SUBROUTINE
REAL FUNCTION sqrt(wert)
:
END FUNCTION
! Hier wird die Compilerfkt. verwendet!!
! Hier wird die eigene Fkt. verwendet!!
! Definition der eigenen Funktion
! zur Wurzel-Berechnung
Die Ausführung einer END– oder RETURN–Anweisung bewirkt normalerweise, daß alle lokalen Datenelemente eines Unterprogramms undefiniert werden. Ausgenommen sind die Fälle, in denen Datenelemente
• mit SAVE–Attribut deklariert werden,
• im unbenannten COMMON–Block stehen,
• (mittels DATA–Anweisung) initialisiert und nicht verändert worden sind,
• in benannten COMMON–Blöcken stehen, welche sowohl im Unterprogramm als auch in einer direkt
oder indirekt übergeordneten Programmeinheit auftreten. Insbesondere bleiben die Werte aller im
Hauptprogramm aufgeführten COMMON–Blöcke definiert.
4.3
Weitere Sprachmittel
In diesem Abschnitt sollen lediglich aus Gründen einer besseren Vollständigkeit noch einige Elemente
des Fortran95–Sprachstandards erwähnt werden. Zum einen sollen dem Interessierten kurze Anregungen
gegeben werden, zum anderen soll die Möglichkeit geschaffen werden, bereits geschriebene Programme
besser zu verstehen.
4.3. WEITERE SPRACHMITTEL
63
• Spezifizierung der Genauigkeit:
Fortran95 ermöglicht mit dem Attribut KIND den Wertebereich und die Genauigkeit von REAL- und
INTEGER–Variablen ein, z.B.
INTEGER, PARAMETER :: GENAU=SELECTED REAL KIND(10,100)
REAL(KIND=GENAU), DIMENSION(30) :: X
Hiermit ist X als Feld mit 30 Elementen, einer Genauigkeit von mindestens 10 Dezimalstellen und
einem Exponentenbereich von mindestens 100 vereinbart. Zur Laufzeit des Programms hat man
über Funktionen Zugriff auf Typcharakteristika.
• Datentypen
Fortran95 bietet neben der expliziten Genauigkeitsangabe bei numerischen Typen auch Ganzzahlkonstanten zu verschiedenen Basen (z.B. binär, oktal, hexadezimal), Verbunde (records), sowie die
Möglichkeit, zusätzliche, sogenannte abgeleitete Datentypen zu definieren, z.B.
TYPE FAHRER
CHARACTER (LEN=20) :: VORNAME, FAMILIENNAME
INTEGER :: SCHICHT, BUS
END TYPE
Die Struktur beschreibt einen Busfahrer durch seinen Vor- und Familiennamen, die Nummer der
Schicht, der er angehört, und durch die Nummer des Busses, den er fährt. Die Betriebsabteilung
bestehe aus 20 Fahrern, so daß eine weitere Vereinbarung folgen kann:
TYPE (FAHRER), DIMENSION (20) :: FUHRBETRIEB
Ein Busfahrer kann mithilfe des Prozentsymbols durch
FUHRBETRIEB (I) % FAMILIENNAME
beschrieben werden. Daneben können Operatoren und Funktionen auf diesen Datenobjekten definiert werden. Auf benutzerdefinierten Datentypen können sogar vordefinierte Operatoren umdefiniert (überladen) werden.
• Die STOP–Anweisung
STOP n
(mit n kann eine Bildschirmausgabe bewirkt werden) bildet das logische Ende des Programms und
bewirkt das Beenden der Ausführung des Programms.
Beispiel:
STOP ’Programmlauf OK’
Ein Programm kann mehrere STOP–Anweisungen enthalten.
• Der Rücksprung aus einem Unterprogramm ins rufende Programm wird normalerweise durch die
END–Anweisung veranlaßt. Als Alternative dazu kann der Rücksprung auch durch eine RETURN–
Anweisung erfolgen:
RETURN
Die RETURN–Anweisung bildet damit analog zur STOP–Anweisung das logische Ende eines Unterprogramms.
Zur Beendigung der Abarbeitung einer Subroutine gibt es neben dem einfachen Rücksprung auch
die Möglichkeit des mehrfachen Rücksprungs (mehrere RETURN–Anweisungen) und des alternativen Rücksprungs. Wir gehen darauf ebensowenig ein wie auf die Möglichkeit der mehrfachen
Eingangspunkte (ENTRY–Anweisung) und verweisen hier auf die FORTRAN–Literatur.
64
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
• Bei der impliziten Typvereinbarung wird der Typ einer Grösse durch ihren Anfangsbuchstaben
festgelegt. Hierfür gibt es die IMPLICIT–Anweisung. so wird zum Beispiel mit
IMPLICIT INTEGER (I-K,M,N), REAL (A-B,D-H,O-Z), LOGICAL(L)
festgelegt, daß alle Variablen, deren Namen mit einem der Buchstaben I bis K, M oder N beginnt vom
Typ INTEGER sind, solche mit Anfangsbuchstaben A, B, D bis H, oder O bis Z sind vom Typ REAL
und Größen mit Anfangsbuchstaben L sind logische Größen. Von der impliziten Typvereinbarung
wird jedoch abgeraten, alle Größen sollen explizit vereinbart werden. Auf die einzig sinnvolle
Anwendung der impliziten Typvereinbarung ist bereits in Abschnitt 4.2.3 hingewiesen worden.
4.4
Tips zum (nicht nur FORTRAN–) Programmieren
Hier sollen die in Abschnitt Programmierstil — wie schreibt man ein gutes Programm?“ gebrachten
”
sprachunabhängigen Hinweise nochmals in Kurzfassung angegeben und um weitere, mehr FORTRAN–
spezifische Tips ergänzt werden. Im anschließenden Abschnitt wird ergänzend ein kurzgefaßter FORT”
RAN–Knigge“ gebracht.
Ziel ist ein klar strukturiertes und gut lesbares Programm. Struktur erreicht man durch Gliederung
und Zerlegung in nicht zu große Module mit definierten Schnittstellen. Die einzelnen Module sollen
ebenfalls im formalen Aufbau und im Ablauf strukturiert sein. Dabei unterstützt uns die Verwendung
von Konstrukten zur strukturierten Programmierung. Lesbarkeit und Selbst-Dokumentation erreicht man durch Gliederung in Programmkopf (Kommentarteil und Vereinbarungsteil) und Prozedurrumpf. Der Kommentarteil umfaßt die Zweckangabe und die Parameterbeschreibung und enthält
weitere Hinweise sowie eine Modifikationsgeschichte. Der Vereinbarungsteil umfaßt Definitionen,
Vereinbarungen und Initialisierungen. Für die Programmanweisungen im Prozedurrumpf sollen
aussagekräftige Namen verwendet werden. Übersichtlichkeit erreicht man durch optische Trennung
mit Leerzeichen und -Zeilen. Zusammengehörige Blöcke im Wiederholungsbereich von Schleifen und in
IF–Blöcken werden eingerückt. Kleinere Anweisungsblöcke werden mit beschreibenden Kommentaren
versehen, ebenso wie die Eingabeanforderungen und Ausgaben des Programms.
Die folgenden Tips sind zum Teil ebenfalls schon an anderer Stelle im Kapitel 4.2 näher besprochen
worden.
Geschachtelte Schleifen:
• Optimierende Compiler (z.B. auf Vektorrechnern) können (zumeist) nur die innerste Schleife optimieren/vektorisieren. Bei kurzen inneren Schleifen mit fester Durchlaufzahl überprüfe man, ob
diese Schleife nicht abgerollt werden kann (Unrolling).
Beispiel:
DO i = 1, 64
DO j = 1, 4
A(i) = B(i,j)*C(i,j)
ENDDO
ENDDO
läßt sich abrollen zu
DO i = 1, 64
A(i) = B(i,1)*C(i,1) + B(i,2)*C(i,2) + B(i,3)*C(i,3) + B(i,4)*C(i,4)
ENDDO
4.4. TIPS ZUM (NICHT NUR FORTRAN–) PROGRAMMIEREN
65
Die untere Codeversion bewirkt auch auf nicht-vektorisierenden Rechnern eine schnellere Ausführung: der
Overhead für die innere Schleife entfällt, und der Rechner kann Zwischenresultate statt im Arbeitsspeicher
in den schnelleren Registern halten. Überdies kann jetzt die größere Schleife vektorisiert werden.
Vergleiche mit REAL-Größen sollten wegen der endlichen internen Darstellungsgenauigkeit nicht auf
gleich“ durchgeführt werden, sondern stets auf einen Toleranzbereich (ein Epsilon ε);
”
also nicht
IF (A-B .EQ. 0.00) THEN
sondern
IF (ABS(A-B) .LT. 1.E-6) THEN
Bei Iterationsdifferenzen (Unterschied zweier aufeinanderfolgender iterierter Werte) sollte eine Abfrage
auf relative Genauigkeit statt auf absolute gemacht werden, da nur dann gewährleistet ist, daß die
Rechnung auf eine bestimmte Anzahl von signifikanten Stellen genau ist.
Also nicht absolut:
IF (ABS(X(i) - X(i-1)) .LE. Eps) THEN
sondern relativ:
IF (ABS(X(i) - X(i-1)) .LE. Eps*ABS(X(i)) THEN
oder äquivalent dazu und gleichzeitig effizienter:
IF (ABS(X(i-1)/X(i)-1.) .LE. Eps) THEN
Für den Fall Eps=0.01 ergibt sich dann für ∆X=X(i)-X(i-1) und jeweils absolute und relative Genauigkeitsabfrage bei
X(i) = 100.2
X(i-1) = 100.1
:
∆X = 1.E-1,
X(i) = 0.1002
X(i-1) = 0.1001
:
∆X = 1.E-4,
X(i) = 0.009
X(i-1) = 0.001
:
∆X = 8.E-3,
absolut nicht erfüllt
relativ erfüllt
absolut erfüllt
relativ erfüllt
absolut erfüllt
relativ nicht erfüllt
Obwohl in den ersten beiden Fällen beide Werte in drei signifikanten Stellen übereinstimmen, ist die
absolute Genauigkeitsabfrage einmal erfüllt, einmal nicht. Im letzten Fall ist die absolute Abfrage erfüllt,
obwohl beide Werte in keiner signifikanten Stelle übereinstimmen.
Die Verwendung von Klammern (auch redundanten) verbessert die Lesbarkeit von Anweisungen erheblich und schließt Zweifel bei der Interpretation und Auswertungsreihenfolge von komplizierten Ausdrücken
aus.
Vorschubsteuerzeichen für den Drucker sollten zur Vermeidung unerwünschter Effekte nicht in anderen
FORMAT–Spezifikationen versteckt werden, sondern explizit angegeben werden. Also nicht:
100 FORMAT (I5, F6.2)
sondern
100 FORMAT (’ ’, I4, F6.2)
Alle Größen im Programm sollten grundsätzlich initialisiert, d.h. mit Anfangswerten versehen werden.
Man überlasse diese Arbeit nicht dem rechner- und installationsabhängigen Betriebssystem/Compiler.
66
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Schon oft haben Programme mit vom Betriebssystem zu Null initialisierten Größen gerechnet und dabei
vernünftig aussehende, jedoch falsche Ergebnisse produziert.
Alle Größen im Programm sollten in expliziten Typ–Anweisungen definiert werden, d.h. man sollte
auf Typzuweisung nach Konvention durch den Anfangsbuchstaben der Variablen und auf die DIMENSION–
Anweisung (ohne eine damit verbundene IMPLICIT–Anweisung) verzichten.
Parameterübergabe an Unterprogramme sollte im Hinblick auf klare Schnittstellen durch Parameterlisten erfolgen. Die Verwendung von COMMON–Blöcken läßt zwar effizientere Programme zu, birgt aber auch
einige Risiken in sich (siehe 4.2.20 Speicherplatz-Zuordnung [...]“).
”
Generell läßt sich der Widerspruch zwischen effizientem und stilvollem Programmieren nicht
auflösen. Effizienz und Stil schließen sich meist gegenseitig aus. Aus Gründen des Selbstschutzes sollte
man dem Motto Stil immer da, wo möglich — Effizienz dort, wo unbedingt nötig“ folgen.
”
Zum Testen von Programmen, die im Entstehen oder in der Veränderung begriffen sind, noch einige
Hinweise.
Zunächst sollen die wichtigsten Fehlerarten erläutert werden, die während der Programmentwicklung
auftreten können: Syntax-, Compilations-, Link-, logische und Laufzeitfehler. Die Schwere der Fehler
wird oftmals noch unterschieden nach: Hinweis (Comment), Vorsicht (Caution), Warnung (Warning) und
Fatal. Fatale Fehler müssen auf jeden Fall beseitigt werden, da sie so schwerwiegend sind, daß sie ein
lauffähiges Programm verhindern. Warnungen deuten meist darauf hin, daß trotz formal korrektem
Programm ein Fehler wahrscheinlich ist. Generell sollten alle Meldungen beachtet werden, auch wenn es
sich dabei nur“ um Hinweise handelt.
”
Syntaxfehler sind Verletzungen der FORTRAN–Syntax auf der Ebene einzelner Wörter (lexikalisch)
und einzelner Anweisungen (syntaktische Struktur). Beispiele hierfür sind
• falsch geschriebene Schlüsselwörter ( FUNKTION“ statt FUNCTION“)
”
”
• unzulässige Variablennamen
• unzulässige Reihenfolge von Anweisungen, z.B. eine Vereinbarungsanweisung nach der ersten ausführbaren Anweisung
• nicht paarweise auftretende Klammern
• REAL-Zahlen mit Komma statt Dezimalpunkt
• Zeichensetzungsfehler, z.B. falsche Trennzeichen (Punkt oder Semikolon statt eines Komma), fehlendes Komma in einer Liste, Zeichenkette nicht durch Hochkomma abgeschlossen
• Verwendung von Feldnamen mit mehr oder weniger Indices als Dimensionen vereinbart wurden
• falsch geschachtelte IF–Blöcke oder DO–Schleifen
• fehlendes ENDIF
• mehrfach definierte Labels
• mehrfach verwendetet Namen (z.B. Variable und Unterprogramm haben den selben Namen)
• fehlendes END im Haupt- oder Unterprogramm; der dann folgende Vereinbarungsteil wird oft recht
seltsam interpretiert
• Unzulässige Mischung von numerischen und nicht–numerischen Operanden in Ausdrücken
4.4. TIPS ZUM (NICHT NUR FORTRAN–) PROGRAMMIEREN
67
Diese Fehler werden im ersten Übersetzungsdurchgang vom Compiler erkannt und als Syntaxfehler ausgeworfen. Von Programmen mit Syntaxfehlern werden keine Objektdateien erzeugt, es kann somit kein
lauffähiges Programm erhalten werden.
Compilationsfehler treten auf, wenn das Programm zwar syntaktisch richtig ist, aber dennoch kein
lauffähiger Objektcode erzeugt werden kann. Meist werden compilerinterne Grenzen (heap, stack, table
sizes) oder Systemgrenzen (segment size) überschritten. Neben diesen sind Beispiele für Compilationsfehler
• Zu groß dimensionierte Felder
• Auswertung von Konstantenausdrücken zur Übersetzungszeit führt auf Überschreiten des darstellbaren Wertebereichs (engl. overflow)
Linkfehler treten in der letzten Phase der Erzeugung eines lauffähigen Programms auf. Neben den
Fehlern aufgrund der Überschreitung linkerinterner Grenzen machen sich sich beispielsweise folgende
Fehler bemerkbar:
• Aufrufe von nicht vorhandenen Unterprogrammen; meist hervorgerufen durch Tippfehler beim Aufruf eines Unterprogramms oder durch Verwendung von Elementen nicht–dimensionierter Felder in
Ausdrücken (der Compiler sucht dann eine Funktion gleichen Namens).
• Programm ist zu groß (braucht zuviel Speicher); tritt häufig bei DOS–Programmen auf, selten unter
UNIX. Ursache sind meist zu groß deklarierte Felder.
Laufzeitfehler ergeben sich erst nach dem Start eines ausführbaren Programms, das fehlerfrei übersetzt
und gelinkt werden konnte. Häufig vorkommende Fehler und Ursachen sind:
• Zahlenbereichsüberschreitung in arithmetischen Ausdrücken durch Verwendung von betragsmäßig
zu großen oder zu kleinen Zahlen (Overflow, Underflow); Ursache ist oft Division durch Null.
• Zugriff auf Feldelemente, die außerhalb des deklarierten Indexbereichs liegen.
• Verwendung ungültiger Operanden; z.B. bei Funktionsaufrufen (Logarithmus einer negativen Zahl)
• Ein-/Ausgabe–Fehler, z.B. Dateiende erreicht; Fehlerhafte Eingabedaten (REAL-Zahl als INTEGER
einlesen); Falsche Formatspezifikation (REAL als INTEGER ausgeben).
• Inkorrekte Anzahl oder Typenkonflikt von Parametern beim Aufruf von Unterprogrammen.
Logische Fehler entstehen bei ungenügend durchdachter Umsetzung einer Aufgabenstellung in einen
Programmentwurf. Strikte Verwendung von Struktogrammen vor der Codierung sollte solche Fehler
weitestgehend vermeiden helfen.
• Falsche Anzahl von Schleifendurchläufen (oft einer zuviel oder zuwenig)
• Endlosschleifen. Schleifenabbruchbedingungen werden nicht überprüft oder sind falsch formuliert.
• Falsch formulierte Bedingungen in Verzweigungen.
Ein Programm sollte nach jeder Änderung getestet werden; niemals mehrere Veränderungen gleichzeitig durchführen. Tests mit vorher festgelegten Testdaten durchführen und neben den eigentlichen
Ergebnissen auch Zwischenergebnisse ausdrucken lassen; so lassen sich oft logische Fehler frühzeitig erkennen. Bei der Fehlersuche (Laufzeitfehler, logische Fehler) läßt man sich verdächtige“ Größen durch
”
ins Programm gestreute WRITE– oder PRINT–Anweisungen ausdrucken. Verdächtige Programmbereiche
68
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
lassen sich so sukzessive eingrenzen. Für spätere Fehlersuchzwecke kann man diese Anweisungen statt sie
zu entfernen einfach Kommentarisieren, d.h. ein ! davor setzen. Die Technik des Kommentarisierens
empfiehlt sich auch bei Änderungen im Programmcode, wenn Anweisungsblöcke durch andere ersetzt werden. Dies kann allerdings ungewollte logische Fehler zur Folge haben: nicht alle unnötigen Anweisungen
sind kommentarisiert oder notwendige Anweisungen sind aus Versehen noch kommentarisiert.
Bei der Fehlersuche macht sich auch modularisierte Programmierung bezahlt. Compilations- und Laufzeitfehler lassen sich besser lokalisieren und damit schneller beseitigen. Korrekte Programmeinheiten
brauchen nicht jedesmal neu übersetzt werden.
Laufzeitfehler sind oft auch auf falsche Variablennamen aufgrund von Tippfehlern zurückzuführen. Solche
lassen sich leicht lokalisieren, wenn man im Programm mit
IMPLICIT NONE
die implizite Typvereinbarung nach der Namenskonvention unwirksam macht. Damit müssen alle Variablen explizit deklariert werden, was ohnehin dringend angeraten wird.
4.5
FORTRAN–Knigge
Dieser Abschnitt basiert noch auf den FORTRAN77 Coding Guidelines“ von David Levine. Diese sind
”
in großen Teilen auch auf Fortran95 übertragbar und sollen hier einen Eindruck vermitteln, was einen
guten Programmierstil ausmacht.
4.5.1
Vorbemerkung
Viele der hier checklistenartig zusammengestellten Regeln sind bereits im Abschnitt 4.4 oder an anderer Stelle erwähnt worden. Die Regeln sollen dazu ermuntern, einen einheitlichen, programmierer- und
projektübergreifenden Programmierstil zu pflegen. Viele tiefgreifende Entscheidungen werden in der Codierphase getroffen. Die meisten davon haben zwar keinen Einfluß auf den Maschinencode, sie beeinflussen
aber das Erscheinungsbild des Programms. Konsistente Vorgehensweisen verbessern die Produktivität, die
Transparenz und die Wiederverwendbarkeit. Projektbezogene Erfordernisse haben, sofern sie zutreffen,
Vorrang.
Die Ziele der Programmierregeln sind mit abnehmender Bedeutung:
1. Verständlichkeit — vermittelt dem Leser den Zweck der Berechnungen.
2. Portabilität — zwischen Compilern auf unterschiedlichen modernen Betriebssystemen.
3. Wartungsfähigkeit — wird sofort verbessert.
4. Effizienz — Ausführungsgeschwindigkeit.
4.5.2
Allgemeines
1. Der Standardzeichensatz von Fortran95 umfaßt + - : =
_ [0--9] [A--Z] sowie [a-z] und das Leerzeichen.
* / ( ) , . ’ ! " % & ; < > ? $
4.5. FORTRAN–KNIGGE
4.5.3
69
Projektorganisation
1. Verwende einen einheitlich gestalteten Kommentarblock am Anfang jedes Unterprogramms (s. Beispiel in Abschnitt 4.2.2).
2. Fasse Unterprogramme, die zum selben Problembereich gehören, in einem Modul zusammen. Dieses
Modul soll mit einem eindeutigen Namen versehen und in einer oder mehreren Dateien in einem
eigenen Unterverzeichnis gespeichert werden.
3. Innerhalb eines Moduls sollen die Namen der Unterprogramme so gewählt werden, daß die ersten
zwei Buchstaben mit dem Modulnamen zusammenhängen, die restlichen vier Buchstaben bezeichnen das Unterprogramm selbst.
4.5.4
Programme und Unterprogramme
1. Beginne jedes Haupt–Programm mit der PROGRAM–Anweisung
2. Wenn an ein Unterprogramm mehr als ein Parameter übergeben wird, ordne die Parameter in folgender Reihenfolge an: Eingangsparameter, Resultatparameter, Steuerparameter, externe Namen.
3. Eine FUNCTION darf keine Seiteneffekte haben.
4. Verwende in einem Unterprogramm nie mehrere RETURN–Anweisungen. Jedes Unterprogramm soll
nur einen Eingang und einen Ausgang besitzen. Am besten benutze kein RETURN.
4.5.5
Format des Quelltextes
1. Trenne keine Bezeichner am Zeilenende.
2. Verwende nie mehr als eine Anweisung pro Zeile.
4.5.6
Labels
1. Verwende Labels nur wenn es nötig ist.
2. Verwende Labels in aufsteigender Folge.
3. Verwende für die FORMAT–Anweisungen, die am Ende eines Programms oder Unterprogramms zusammengefaßt sind, eine andere Label–Serie.
4. Schreibe Labels rechtsbündig.
4.5.7
Groß- und Kleinschreibung
1. Schreibe alle FORTRAN–Schlüsselworte mit großen Buchstaben.
2. Schreibe alle symbolischen Namen von Konstanten mit kleinen Buchstaben.
3. Schreibe alle anderen Bezeichner mit großem Anfangsbuchstaben und sonst mit kleinen Buchstaben
(wenn es die Lesbarkeit erleichtert, verwende auch Großbuchstaben innerhalb des Namens).
4. Behalte innerhalb eines Programms eine einheitliche Schreibweise bei.
70
4.5.8
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Verwendung von Leerzeichen
1. Verwende keine Tabulatoren (Tabs).
2. Schreibe FORTRAN–Schlüsselworte immer zusammen (ENDIF statt END IF), setze davor und dahinter ein Leerzeichen.
3. Schreibe kein Leerzeichen zwischen den Namen eines Feldes und seinen Index (z. B. Feld(I)).
4. Setze ein Leerzeichen zwischen den Namen eines Unterprogramms und seine Parameterliste (z. B.
Uprog (Par1, Par2)).
5. Setze ein Leerzeichen hinter jede öffnende und vor jede schließende Klammer, Ausnahmen: Parameter- und Indexlisten.
6. Setze hinter jedes Komma ein Leerzeichen.
7. Verwende Leerzeichen in arithmetischen Ausdrücken, um die Operatoren hervorzuheben und den
Vorrang der Operationen zu verdeutlichen.
8. Verwende Einrückungen, um die Programmstruktur zu verdeutlichen. Jede Einrückungsstufe sollte
drei Leerzeichen tief sein.
9. Verwende Leerzeilen, um die Lesbarkeit zu verbessern.
4.5.9
Wahl von Bezeichnern
1. Beschränke die Länge von Namen auf 31 Zeichen, verwende die ersten 6 zu Unterscheidung.
2. Verwende sprechende“ Namen.
”
3. Erkläre die Bedeutung jeder Variable und jedes Feldes in einem Kommentar.
4. Kürze .TRUE. und .FALSE. nicht ab.
5. Schließe Zeichenketten in einfache Anführungszeichen (’) ein.
4.5.10
Datentypen
1. Deklariere alle Variablen. Verwende die Anweisung IMPLICIT NONE, um nicht deklarierte Variablen
aufzuspüren.
2. Ordne die Bezeichner bei der Variablendeklaration in einer sinnvollen Reihenfolge an, die durch
die Größen gegeben ist, die die Variablen repräsentieren. Bei Unterprogrammen können die Deklarationen auch in der Reihenfolge der Parameterliste erfolgen. Wenn es keine sich anbietende
Ordnungsfolge gibt, ordne alphabetisch.
3. Verwende bei Funktionsaufrufen immer den generischen Namen (z. B. SQRT statt DSQRT). Ausnahme:
Der Funktionswert wird als Argument an ein anderes Unterprogramm übergeben.
4. Schreibe Zahlen, die nicht ausdrücklich Integer sein sollen mit Dezimalpunkt (z.B. 7. statt 7).
4.5. FORTRAN–KNIGGE
4.5.11
71
Logische Operatoren
1. Verwende niemals .EQ. (==) oder .NE. (/ =), um Fließkommazahlen zu vergleichen.
2. Verwende .GE. (>=) oder .LE. (<=) anstelle von .EQ. (==), wenn überprüft werden soll, ob ein
bestimmter Schwellenwert erreicht wurde.
3. Verwende nur die logischen Operatoren .AND., .OR., .EQV., .NEQV. und .NOT. und wende sie nur
auf LOGICAL–Operanden an.
4. Vermeide .NE. (/ =) in einer IF–Konstruktion, die einen ELSE-Zweig besitzt. Verwende stattdessen .EQ. (==) und vertausche die beiden Zweige, so daß der ELSE-Zweig die logische Negation
behandelt.
4.5.12
Arithmetische Ausdrücke
1. Setze vor und hinter einen Operator mit niedriger Priorität ein Leerzeichen.
2. Erstreckt sich ein arithmetischer Ausdruck über mehr als eine Zeile, so trenne ihn hinter einem
Operator.
3. Rücke Fortsetzungszeilen ein.
4. Beachte die Typen der Operanden und ihre Auswirkung auf das Resultat einer Rechnung (z.B. 8
/ 3 * 3.0 ergibt 6.0 und nicht 8.0).
5. Vorsicht, wenn einer INTEGER–Variablen ein vermeintlich exaktes Rechenergebnis einer Fließkommarechnung zugewiesen wird. Beispielsweise kann die Zuweisung von 30.0 / 0.1 an eine INTEGER–
Variable den Wert 299 zum Ergebnis haben, da nicht unbedingt gerundet wird.
4.5.13
Felder
1. Lege die Dimension eines Feldes in der Variablendeklaration fest, vermeide eine extra DIMENSION–
Anweisung.
2. Verwende beim Zugriff auf einzelne Feldelemente nur Indizes vom Typ INTEGER.
3. Versuche die Bearbeitung von mehrdimensionalen Feldern so durchzuführen, daß der erste Feldindex
derjenige ist, der sich am schnellsten ändert, und der letzte der, der sich am langsamsten ändert.
(s. Abschnitt 4.2.3.4)
4. Beim Zugriff auf Feldelemente müssen immer alle Indizes angegeben werden.
5. Überschreite nie die vorgegebenen Feldgrenzen.
4.5.14
Kontrollstrukturen
1. Springe nie in einen Schleifenkörper oder einen Zweig einer IF–Konstruktion.
2. Vermeide es, aus Schleifen herauszuspringen. Eine Schleife soll nur durch ihr eigenes Abbruchkriterium verlassen werden können.
3. Benutze die STOP–Anweisung nur, wenn besondere Umstände (z. B. Fehler) einen Programmabbruch erfordern und eine Fortsetzung unmöglich oder sinnlos ist. Gib mit der STOP–Anweisung eine
Meldung aus, die den Grund des Abbruchs erklärt.
72
4.5.15
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Parameter
1. Die Argumente eines Unterprogramms sollten beim Aufruf sowohl in der Anzahl als auch im Datentyp mit der Deklaration übereinstimmen.
2. Benutze keinen Parameter in einem Aufruf doppelt.
3. Wenn eine intrinsic function mehrere Argumente erfordert, müssen diese alle den selben Datentyp
besitzten (z. B. bei MIN oder MAX).
4. Übergib nie eine Konstante als Parameter an ein Unterprogramm, es sei denn es ist sicher, daß es
sich nur um einen Eingangsparameter handelt.
4.5.16
COMMON–Blöcke
1. Benutze COMMON–Blöcke nur wo es notwendig ist, und fasse dann auch nur die Variablen dort zusammen, für die es erforderlich ist.
2. Schreibe die Definition eines COMMON–Blocks in eine eigene Datei und lade diese beim Schreiben
des Programms an den entsprechenden Stellen zum Quelltext hinzu (INCLUDE–Anweisung). So
werden Fehler durch falsches Abtippen vermieden.
3. Beachte in einem COMMON–Block die Reihenfolge der Datentypen. Aus Geschwindigkeitsgründen
kann es sinnvoll sein, die Variablen in einem COMMON–Block nach ihrem Datentyp zu sortieren, wobei folgende Reihenfolge eingehalten werden sollte: DOUBLEPRECISION, COMPLEX, REAL, INTEGER,
LOGICAL, CHARACTER.
4. Übergib an ein Unterprogramm nie eine Variable als Parameter, die gleichzeitig in einem COMMON–
Block verwendet wird.
5. Initialisiere COMMON–Blöcke nur mit der BLOCKDATA–Anweisung.
4.5.17
Ein- und Ausgabe
1. Verwende die Möglichkeiten zur Fehlerbehandlung mit END=, ERR= und IOSTAT=, und behandle alle
möglichen Fälle sorgfältig.
2. Wenn eine formatierte Ein- oder Ausgabe erfolgt, schreibe die dazugehörige FORMAT–Anweisung in
die darauffolgende Zeile. Ausgenommen hiervon sind FORMAT-Anweisungen, die von mehreren Ein/Ausgabe–Anweisungen verwendet werden, diese werden an das Ende der jeweiligen Programmeinheit gestellt.
3. Aus Geschwindigkeitsgründen sollten implizite Schleifen gegenüber expliziten DO–Schleifen bevorzugt werden.
4. Gib beim Öffnen von Dateien mit der OPEN–Anweisung, wenn nichts anderes gefordert ist, immer
STATUS = ’UNKNOWN’ an.
4.5.18
Abschlußbemerkung
Es sei noch bemerkt, daß ein Programmier–Knigge von Natur aus recht subjektiv ist, obwohl die meisten der Regeln weithin akzeptiert und für gut befunden sind. Mit den Regeln soll hauptsächlich die
Aufmerksamkeit auf einige Kompliziertheiten bei der Programmierung gelenkt werden, mit dem Ziel, zur
Konsistenz zu ermutigen.
4.6. ZUSAMMENFASSUNG
4.6
73
Zusammenfassung
Dieses Kapitel informiert über Programmiersprachen allgemein und FORTRAN im Besonderen. Programmiersprachen gestatten die Codierung eines Programms so, daß der Rechner es ausführen kann. Maschinennahe Programmiersprachen erlauben die gezielte Ausnutzung von speziellen Rechnereigenschaften,
sind aber hardwareabhängig. Höhere Programmiersprachen sind problemnahe Sprachen und meist rechnerunabhängig. Sie ermöglichen einfachere, schnellere und weniger fehleranfällige Programmierung, die
Programme sind portabel. Die Übersetzung in Maschinencode geschieht mit einem Compiler. Für nahezu
alle Zwecke gibt es abhängig von den jeweiligen Sprachelementen Sprachen, die mehr oder weniger spezialisiert sind. Von den prozeduralen Sprachen sind im Ingenieurbereich vor allem algorithmische Sprachen
wie FORTRAN, C, Pascal oder BASIC verbreitet. Daneben gibt es nicht–prozedurale Sprachen für u.a.
künstliche Intelligenz und Datenbanken.
In diesem Kapitel wurde die älteste und am weitesten verbreitete Programmiersprache FORTRAN in
der Standard–Version Fortran95 besprochen. FORTRAN eignet sich besonders für die numerische Behandlung naturwissenschaftlicher und technischer Probleme in herkömmlicher Formelsprache. FORTRAN
verfügt neben den üblichen Datentypen wie INTEGER, REAL und LOGICAL auch über Zeichenketten und
insbesondere komplexe Größen. Die Mehrheit der Sprachelemente dient der Arithmetik und der Ablaufsteuerung. Strukturiertes, modularisiertes Programmieren wird unterstützt, genauso wie die Verarbeitung
von Zeichenketten und die Verwaltung von Dateien. Hierfür bietet FORTRAN eine große Zahl von vordefinierten Standardfunktionen. Es wurde gezeigt, wie die grundlegenden Struktogrammelemente mit
Fortran umgesetzt werden können.
74
4.7
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Fragen und Übungen zu Kapitel 4
F 4.1 Welche Vorteile hat die Verwendung einer höheren Programmiersprache wie z.B. Fortran gegenüber
der Maschinensprache?
Woraus
bestimmt
F 4.2 (Fortgeschritten!)
(Fortran–)Programmes (Lademodul)?
sich
die
Arbeitsspeicheranforderung
eines
F 4.3 Welche Möglichkeiten gibt es, Arbeitsspeicher zu sparen?
F 4.4 Was ist eine Formelfunktion (statement function)?
F 4.5 Wo stehen Vereinbarungsanweisungen in einer Programmeinheit?
F 4.6 Welche der folgenden Zeichen gehören nicht zum Fortran Zeichensatz?
u U ( Ä : ? ;
F 4.7 Wieviel Elemente enthält ein mit
INTEGER, DIMENSION (10, -15:5, 2:3) :: S
dimensioniertes Feld?
F 4.8 Wozu verwendet man die PARAMETER–Anweisung?
F 4.9 Welchen Wert haben die Fortran–Ausdrücke
3**2+12/8
3**(2+12/8)
(3**2+12)/8
F 4.10 Welchen Output erzeugt das folgende Programmstück?
IMPLICIT LOGICAL (L)
DATA LA, LB, LC, LD, LE
&
.FALSE., .FALSE., .TRUE., .FALSE., .FALSE./
LZ= LA .AND. .not.LB .OR. .not.LC .AND. LD .EQV. LE
LY= .not.LA .AND. .not.LB .OR. LC .AND. .not.LD .NEQV. .not.LZ
LX= .not.LA .AND. LB .OR. LC .AND. LD .NEQV. .not.LY
LW= LA .or. LB .OR. LC .AND. .not.LD .EQV. LX
Print*,la, lb, lc, ld, le, LZ, LY, LX, LW
F 4.11 (Fortgeschritten!) Beim Zeichnen von Achsenkreuzen mit beschrifteten Achsen ergibt sich oft das
Problem, daß der Text zentriert ausgegeben werden soll. Zur Lösung benötigt man ein Unterprogramm, das die Länge eines Zeichenstrings ermittelt. Dieses Unterprogramm ist zu schreiben. Das
UP bekommt den String übergeben und soll die Anzahl der führenden Leerstellen und die Länge
des Strings ermitteln. Unter Länge soll die Anzahl der Zeichen verstanden werden, die zwischen
dem ersten und dem letzten vom Leerzeichen verschiedenen Zeichen liegen. Leerzeichen am Anfang
und am Ende des Strings sollen bei der Längenermittlung ignoriert werden.
Hinweis: Man suche den String zeichenweise nach Leerzeichen ab.
Beispiel:
12345678901234567890
Test auf Laenge
sollte eine Länge von 15 und 3 führende Leerzeichen ergeben.
F 4.12 Was versteht man unter geschachtelten DO–Schleifen und welche Sprünge sind bei ihnen erlaubt?
4.7. FRAGEN UND ÜBUNGEN ZU KAPITEL 4
75
F 4.13 Wie können zwei Unterprogramme (SUBROUTINEn) Daten austauschen?
F 4.14 Welchen Unterschied im Datenaustausch gibt es zwischen SUBROUTINE und FUNCTION ?
F 4.15 Welche Möglichkeiten gibt es, das Lesen von einer Datei variabler Länge zu organisieren?
F 4.16 (Fortgeschritten!) Wie setzt man einen Bildschirmpunkt in Fortran?
F 4.17 Setze Frage F2.5 zuerst in ein Struktogramm um, erstelle danach ein FORTRAN–Programm für
die Aufgabe.
F 4.18 Welchen Output erzeugt folgendes Programm?
PROGRAM test
INTEGER, DIMENSION (2,2) :: k
DO i=1,2
DO j=1,2
k(i,j)=i*2+j
ENDDO
ENDDO
CALL sub1(k)
END
SUBROUTINE sub1(kfeld)
INTEGER,DIMENSION(4) :: kfeld
PRINT*,(kfeld(i),i=1,4)
END SUBROUTINE
F 4.19 Welchen Output erzeugt folgendes Programm auf dem Bildschirm ?
print*,’12345678901234567890’
print 10,10,10,10
10 format(i4,i2)
end
F 4.20 Die Datei TEST.DAT enthalte folgende Daten:
10.3
-7.373
1.03E+01
Schreibe ein Programm, das diese Daten einliest und den Mittelwert ausgibt.
76
4.8
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
Antworten zu den Fragen und Übungen zu Kapitel 4
A 4.1 bessere Dokumentationsmöglichkeiten ; problemorientiert ; maschinenunabhängig, daher portabler
Code ; leichtere Programmierung
A 4.2 Aus der Größe des Objektcodes (Codesegment) plus der Größe des Datensegmentes. In das Codesegment gehen sowohl die übersetzten Anweisungen des selbstgeschriebenen Programms als auch
der Code der Routinen aus der Laufzeitbibliothek (z.B. für Ein-/Ausgabe) ein. Vergrößerungen des
Datenbereiches, z.B. durch größere Dimensionen von Feldern, gehen nicht notwendig linear in die
Größe des Lademoduls ein.
A 4.3 Mehrfachbenutzung von Variablen (-namen). Mehrfachnutzung von COMMON–Bereichen. Mehrfachnutzung von Speicherbereichen mit EQUIVALENCE. Datenauslagerung auf Massenspeicher (Platte)
statt Halten im Arbeitsspeicher. Sinnvolle, problemangepasste Dimensionierung von Feldern.
A 4.4 Eine statement function ist eine besondere Art einer Funktion. Sie besteht aus einer einzigen Anweisung und wird in einer Programmeinheit vor den ausführbaren Anweisungen vereinbart. Sie ist
nur lokal gültig (wie die interne Funktion und im Gegensatz zur externen Funktion).
A 4.5 Vor allen DATA–Anweisungen, Formelfunktionsdefinitionen und vor allen ausführbaren Anweisungen.
A 4.6 Ä ?
A 4.7 Es enthält 10 · 21 · 2 = 420 Elemente
A 4.8 Zum Besetzen von Konstanten, wobei der Compiler überwacht,daß sie nicht verändert werden, sowie
zum eleganten Dimensionieren von Feldern.
A 4.9 10, 27, 2
A 4.10 F F F T F F T T F F
A 4.11 Die Standardfunktion LEN alleine reicht nicht aus, da sie nur die in der CHARACTER–Anweisung
definierte Länge des Strings ermittelt. Eine mögliche Lösung lautet:
SUBROUTINE StrLen (String, NCLead, NCStr)
!---- -------------------------------------!* Zweck: Subroutine zur Ermittlung der Anzahl fuehrender Leer !
zeichen und der Laenge einer Zeichenkette
!* Vars: String --- zu untersuchende Zeichenkette (IN)
!
NCLead --- Anzahl fuehrender Leerzeichen (OUT)
!
NCStr
--- Laenge der Zeichenkette ohne fuehrende und
!
nachfolgende Leerzeichen (OUT)
!
LenStr --- Laenge des uebergebenen Strings (LOC)
!
i
--- Laufvariable (LOC)
!* Mods: Version 7.11.90/WWB f"ur InfoTech VL
!*
Version 7.03.02/TFS f"ur InfoTech VL
!---- -------------------------------------!* Deklarationen und Initialisierungen
CHARACTER :: String*(*)
INTEGER :: LenStr, NCLead, NCStr
NCLead = 0
NCStr = 0
!* Ausfuehrbarer Teil des Unterprogramms
!---- Uebergegebene Laenge ermitteln
LenStr = LEN (String)
4.8. ANTWORTEN ZU DEN FRAGEN UND ÜBUNGEN ZU KAPITEL 4
77
!---- Fuehrende Leerstellen ermitteln
i = 1
DO WHILE ((i<=LenStr) .AND. (String (i:i) /= ’ ’))
NCLead = i
i = i + 1
ENDDO
!---- Echte Stringlaenge ermitteln durch Absuchen des Strings
!---- nach Leerstellen vom Ende her
NCStr = LenStr - NCLead
i = LenStr
DO WHILE (i <= NCLead +1 .AND. (String (i:i) .NE. ’ ’))
NCStr = i - NCLead - 1
i = i - 1
ENDDO
!------------------------------------------END SUBROUTINE
A 4.12 DO–Schleifen sind geschachtelt, wenn sich im Wiederholungsbereich einer Schleife eine weitere DO–
Schleife befindet. Sprünge sind nur von der inneren in die äußere erlaubt. Einzige Ausnahme ist ein
Sprung aus der äußeren an den Beginn der inneren Schleife. Jedoch sollten Sprünge vollkommen
vermieden werden!!
A 4.13 Direkt über Parameterliste
Direkt und indirekt über COMMON–Blöcke Indirekt über externe Dateien
A 4.14 Die FUNCTION gibt ihr Ergebnis über den Function–Namen zurück, die SUBROUTINE über die
Parameterliste.
A 4.15
- Man schreibt die Anzahl der records (Zeilen) an den Anfang der Datei.
- Man vereinbart ein spezielles Dateiendezeichen.
- Man benutzt den END–Parameter der READ–Anweisung.
A 4.16 In Standard-FORTRAN gibt es diese Möglichkeit nicht. Falls es mit einem bestimmten Compiler
möglich ist, handelt es sich um eine nichtstandardisierte Spracherweiterung.
A 4.17 Das FORTRAN–Programm könnte so aussehen:
i=0
j=0
k=0
DO
IF (j > k) THEN
k=k+2
ELSE
k=k+1
j=j+k
ENDIF
IF (j > 10) EXIT
ENDDO
DO i = 1, 10
j=j+i
ENDDO
PRINT*, i, j, k
END
78
KAPITEL 4. PROGRAMMIERSPRACHEN, FORTRAN
A 4.18 3 5 4 6
A 4.19 12345678901234567890
1010
10
A 4.20 Das FORTRAN–Programm könnte so aussehen:
OPEN (1,file=’TEST.DAT’)
i=0
xm=0.
DO
READ (1,*, IOSTAT=ios) x
IF (ios = 0) THEN
i=i+1
xm=xm+x
ENDIF
IF (ios /= 0) EXIT
ENDDO
PRINT *,’ Der Mittelwert ist ’,xm/i
END
Der Mittelwert ist 4.409
Literaturverzeichnis
[1] Byte. McGraw–Hill, New York, monatlich erscheinende Computerzeitschrift
[2] c’t — Computerfachzeitschrift, Heise–Verlag, Hannover
[3] Duden ’Informatik’. Dudenverlag Mannheim/Wien/Zürich, 1988
[4] Buchdruck auf dem PC, S. Demmig, Juli 1989
[5] Textverarbeitung von Rang — acht prominente Programme, A. Fourier, S. Pfeiffer, Mai 1990
[6] Informatik für Ingenieure, Prof. Dr. Dr. E. Hering, Dipl.–Phys. Dr. U. Dyllong, Dipl.–Ing. J. Gutekunst, VDI–Verlag GmbH, Düsseldorf 1995
[7] The TEXbook, D. E. Knuth, Addison–Wesley, 1986
[8] LATEX— Eine Einführung, H. Kopka, Addison–Wesley, 1994
[9] LATEX— A Document Preparation System, L. Lamport, Addison–Wesley, 1988
[10] Mikroprozessoren und Mikrorechner, W. Meiling und Ruprecht Fülle, Akademie–Verlag Berlin, 1988
[11] Fortran95 Sprachumfang, Regionales Rechenzentrun für Niedersachsen (RRZN), 2. Auflage, RRZN
Hannover, 1997 (erhältlich im Sekretariat der ZRZ, TUB)
[12] Beiträge zur Rechnerarithmetik RRZN-Bericht 38, 2. rev. Auflage, Regionales Rechenzentrum für
Niedersachsen, 1988.
[13] Computer Graphics: System and Concepts, R. Salmon und M. Slater, Addison–Wesley, Massachusetts, 1987
[14] Einführung in TEX, Schwarz, Addison–Wesley, 1988
[15] Computer Networks, A.S. Tanenbaum, Prentice–Hall, 1996
[16] VDI–Nachrichten,Wochenzeitung des VDI, VDI–Verlag, Düsseldorf
[17] PostScript Language, Reference Manual (1986); Language Tutorial and Cook Book (1986); Language
Design (1988); alle Bände: Addison–Wesley, Reading, Massachusetts.
[18] http://www.mssi.com/s1672pic.htm
[19] http://www.intel.com
[20] http://www.intel.com/pressroom/archive/releases/dp010897.htm
Herunterladen