Dieses Feldermodell ist ein Zugeständnis an die Anschaulichkeit. Die Variablen bzw. Adressen sind hier Namen für Felder (Töpfe, Speicherplätze), die Daten enthalten. Datenstrukturen sind hier somit Felderstrukturen, was die Operationen etwas umständlicher macht.
Daten, Typen
Die Daten der "Computer"-Welt sind in Klassen eingeteilt, in die sogenannten
(Daten-)Typen. So gibt es:
Ganze Zahlen (integer), die wir mit Namen wie 1,2,3,4,5,..
belegen,
Buchstaben (character), die wir mit Namen wie a,b,c,d,e,..
belegen,
Worte (string), denen wir Namen wie 'Hello world'
geben,
Adressen (pointer), für die wir keine eigenen
Namen haben,
Programme (procedure), für die es auch keine
konstanten Namen gibt,
etc.
Felder, Variable
Für jeden Typ gibt es Speicherplätze, die wir hier anschaulich
"Felder" nennen wollen und in die Daten dieses Typs gelegt werden
können. Diese Felder bekommen Namen, die wir Variable nennen, und
die wir mit Buchstaben oder Worten schreiben. Den Inhalt eines Feldes x
liefert uns die Funktion inh().
Statements
Die einfachsten Statements legen Variable an stellen Felder mit Namen
bereit legen Daten in die Felder und vergleichen Inhalte von Feldern.
| Statements: | Erklärung: | Ergebnis: |
| var x,y:integer | Zwei Felder vom Typ "integer", der Größe 2 Byte, werden bereitgestellt und mit "x" bzw. "y" benannt. Die Namen "x" und "y" bezeichnen wir als Integervariable, weil sie für Felder stehen, in die "integer" gelegt werden. | |
| x := 4 | lege in den Feld (mit Namen) x den Integer 4 | inh(x) = 4 |
| x := y | lege in den Feld x eine Kopie des Inhalts von Feld y. | inh(x) = inh(y) |
| x := x+y | addiere die Inhalte von Feld x und Feld y und lege die Summe in Feld x | inh(x) = inh(x)+inh(y) |
| if x=4 | wenn der Inhalt von Feld x der Integer 4 ist | wenn inh(x) = 4 |
| if x=y | wenn die Inhalte der Felder x und y gleich sind. | wenn inh(x) = inh(y) |
Man beachte dabei, daß das Zeichen : = nur wenig mit dem Gleichheitszeichen
der Mathematik zutun hat. Grundsätzlich gilt:
| := | links von := steht immer der Namen eines Feldes, während rechts davon immer ein Datum gemeint ist, das in dieses Feld paßt, also seinen Typ hat. |
| = | links und rechts von dem Zeichen = sind immer Daten (Feldinhalte) vom selben Typ gemeint. |
Datenstrukturen
Man kann mehrere Daten zu einer Datenstruktur zusammenfassen. Eine
Datenstruktur besteht aus Anordnungen von Feldern zusammen mit einer
oder mehreren Operationen, die den Zugriff auf die Daten in den Feldern
steuern.
Die Datenstruktur "Array".
Wir fassen n Integer-Felder zusammen zu einer Einheit, einem n-Tupel.
Ist (T1, .., T50) ein solches 50-Tupel von Integer-Feldern, so liefert
uns die Funktion pr(10,(T1, .., T50)) = T10 gerade das 10-te Feld daraus.
Die Datenstruktur "Integer-Array" der Länge 50 besteht also
aus den 50-Tupeln von Integer-Feldern, den Integern selbst und einer Selektionsfunktion
[], die einem Array und einer Position (Adresse) das entsprechende Feld
liefert.
| Statements | Ergebnis: |
| var A:
array[1..100]
of integer |
legt ein Feld an, in dem ein 100-Tupel von Feldern liegt, die nebeneinander angeordnet und mit 1 bis 100 durchnummeriert sind |
|
A[9]:=4 x:=A[9] A[i]:=4 |
inh(A[9]) = inh(pr(9, inh(A))
=
4
inh(x) = inh(A[9])=inh(pr(9, inh(A)) = 4 inh(A[i]) = inh(pr(inh(i), inh(A)) = 4 |
Man beachte, daß im letzten Statement ein Integer-Feld in dem Array-Feld
A durch Angabe des Integer-Feldes i adressiert wurde und nicht durch Angabe
eines Integers. Diese Art der Adressierung, bei der nicht die Adresse selbst,
sondern ein Feld angegeben wird, in dem die Adresse liegt, nennt man indirekte
Adressierung. Die Adressierung muß nicht durch Integer erfolgen, es kann
auch jeder andere ordinale Datentyp sein.
Der Datenstruktur "Record"
Im Unterschied zu den Arrays geschieht hier die Adressierung direkt
durch konstante Namen für die Komponenten. Die Datenstruktur "Record"
besteht aus den (ungeordneten) Mengen von Feldern mit ihren Namen und einer
Selektionsfunktion "." , die einer Feldmenge und einem Komponentennamen
das entsprechende (so benannte) Feld zuordnet.
| Statement | Ergebnis: |
| type karte=
record
name: string; ort: string; tel: integer; end; var person: karte;
person.name:=meier;
|
ein Tripel "Karte" von Feldern verschiedenen Typs wird
definiert. Die Felder haben die Namen (Adressen) "name", "ort" und "tel",
die aber jetzt von verschiedenem Typ sein dürfen.
Ein Feld mit Namen "person" für solche Feld-Tripel wird bereitgestellt. inh(person.name) = inh(pr(name, inh(person)) = meier
|
Datenstruktur Pointer
Will man große Datenstrukturen definieren, so benötigt man viele Felder und viele Namen. Bei den Arrays haben wir einfach die Integer (oder andere ordinale Datentypen) als Namen benützt. Dabei könnten wir es belassen. Die Integer aber suggerieren eine Ordnung, die wir nicht immer einhalten wollen. So wollen wir vielleicht zwischen Feld 4 und Feld 5 in einem Array von 100 Feldern ein neues Feld einschieben. Da es keinen Integer zwischen 4 und 5 gibt, müssen wir uns einen anderen freien noch nicht gebrauchten Integer nehmen, und das neue Feld damit benennen. Dann ist allerdings die Ordnung der Felder nicht mehr die der Namen. So würden Integer als Namen eher verwirren und zu unerwünschten Operationen wie Addition oder Multiplikation verführen, die auf Integern ja erlaubt sind.
Mit jedem anderen uns bekannten Datentyp ist es ähnlich. Alle haben sie Eigenschaften, die uns nicht interessieren, ja sogar stören. Also definieren wir einen neuen Datentyp "Namen", den wir "Adressen" nennen wollen, um ihn von den Variablen zu unterscheiden, die ja auch Namen sind. Von den Adressen wissen wir jetzt noch gar nichts, außer daß wir genügend davon haben und daß wir sie in Feldern speichern können. Allerdings haben wir für die Adressen im Gegensatz zu den uns bisher bekannten Datentypen keine konstanten Namen, wir können sie nicht direkt in den Programmtext schreiben. Um überhaupt an Adressen zu kommen, gibt es eine Funktion new, die in ein Feld vom Typ Adresse, eine uns weiter nicht bekannte Adresse legt, die aber noch in keinem anderen Feld liegt, die also noch unbenutzt ist. Zu dieser Adresse gehört ein Feld, in das wir jetzt etwas legen können. Um dieses Feld anzusprechen, können wir aber nicht die Adresse nennen - die können wir ja nicht schreiben, ja wir kennen sie nicht einmal - aber wir können das Feld angeben, in dem die Adresse liegt, d.h. ganz wie bei den Arrays adressieren wir das Feld indirekt.
Adressen sind wie Variable Namen, d.h. sie bezeichnen Felder von einem bestimmten Typ. Also werden auch die Adressen, wie die Variablen typisiert: wir geben an, was für Datentypen in die von ihnen benannten Felder gelegt werden können. So gibt es Integeradressen, wie es Integervariablen gibt, d.h Adressen vom Typ Integer, etc. Und es gibt Variablen vom Typ Integeradresse, also Integeradressvariable, die Felder benennen, die Integeradressen halten können.
Sei also p eine Integeradressvariable. Der Befehl new(p) bewirkt, daß in das Feld p eine freie Integeradresse gelegt wird. Auf den Adressvariablen ist eine Selektionsoperation ^ definiert. Der Ausdruck p^ liefert uns die Adresse, die im Feld p liegt. Man kann den Ausdruck p^ also wie einen zweiten Namen für das Feld auffassen, der durch die in Feld p liegende Adresse benannt wird ( inh(p^) = inh(inh(p)) ).
Da der Inhalt von Feld p die Adresse eines anderen Felds ist, also sozusagen auf einen anderes Feld zeigt, nennt man ihn auch Zeiger oder Pointer und entsprechend p eine Pointervariable.
Die Datenstruktur Pointer besteht also aus den Feldern vom Typ Adresse
zusammen mit der Selektionsfunktion "^", die einem Adress-Feld die in ihm
liegende Adresse zuordnet und einer Insert-Funktion "new", die in ein Adress-Feld
eine freie Adresse legt.
| Statements | Ergebnis: |
| var p,q : ^integer; | Zwei Felder vom Typ Integer-Adresse werden bereitgestellt. |
| new p; | ein Feld vom Typ Integer wird bereitgestellt und seine Adresse in Feld p abgelegt. Sie ist jetzt unter p^ abrufbar. |
| p^:=4; | inh(inh(p)) = 4 |
| new p; | ein neues Feld vom Typ Integer wird bereitgestellt und nun diese Adresse in Feld p gelegt. Jetzt ist mit p^ diese Adresse gemeint. |
| p^:=x; | inh(inh(p)) = inh(x) |
| y:=p^; | inh(y) = inh(inh(p)) |
| q:=p | inh(q) = inh(p), die Felder p und q enthalten dieselbe Adresse |
| q^:=p^ | inh(inh(q)) = inh(inh(p)) |