Home Computer Pascal Kurs Site Map counter

Jetzt lerne ich Pascal... Teil 3

Funktionen und Prozeduren

Bisher haben wir alles in das Hauptprogramm gepackt. Das ging bei unseren kleinen Programmen, aber für größere Programme ist das doch sehr unübersichtlich. Daher gibt es zwei Möglichkeiten Unterprogramme in Pascal zu realisieren.

Eine Funktion gibt einen Wert zurück, denn man z.B. in einer Zuweisung benutzen kann. Turbo Pascal verfügt zahlreiche Funktionen. Die sie nutzen können ohne Sie zu schreiben. So übergibt folgende Funktion den Sinus des Winkels:

Wert:=sin(Winkel);

Der Funktion sin wird ein Wert übergeben der in den Klammern steht. Es gibt auch Funktionen ohne Werte:

taste=Readkey;

liest ein Zeichen von der Tastatur ein und übergibt es als Ergebnis an die Variable Taste. Prozeduren unterscheiden sich formal von Funktionen nur dadurch das Sie keinen Rückgabewert haben. Sie haben schon die Prozeduren Readln und Writeln benutzt. Da Prozeduren etwas einfacher zu verstehen sind als Funktionen, beginnen wir mit Ihnen.

Prozeduren

Eine Prozedur ist in Pascal ein Unterprogramm und sie hat einen Namen mit einer Parameterliste und dann die Variablen, Konstanten, Typen und Code wie das Hauptprogramm, d.h. es können Konstanten und Variablen definiert werden, aber auch eigene Typen. (Zu Typen später mehr). Eine Prozedur ist also ein "Programm im Programm". Deswegen gibt es auch eine Besonderheit die sehr wichtig ist: Sie betrifft die in einer Prozedur definierten Variablen und Konstanten. Diese sind nun lokal. D.h. nur innerhalb der Prozedur, nicht aber in anderen Prozeduren bekannt.

Wenn man im Hauptprogramm eine Variable × definiert und in einer Prozedur auch eine Variable x, so betreffen alle Änderungen nur die lokale Variable, nicht die des Hauptprogramms. Dies gilt allgemein: Eine lokal definierte Variable "verdeckt" eine globale Variable.

Jede Prozedur kann wiederum eigene Prozeduren und Funktionen enthalten, sie müssen nur von dem Codeblock erscheinen. Auch hier gilt: Diese Funktionen/Prozeduren sind nur innerhalb dieser Prozedur bekannt und nicht außerhalb. Dies ist ein wichtiger Unterschied zu C, welches nur global sichtbare Funktionen kennt.

Allgemeine Form einer Prozedur Definition:

Procedure namen(Parameterliste);

Const Konstanten;
Var Variablen;
type Typen;

... eigene Funktionen und Prozeduren...

begin
...Code;
end;

Vorgeschrieben sind nur der Name und der Codeblock, d.h. folgendes ist die einfachste Prozedur:

Procedure tue_nichts;

begin
end;

Für Variablen, Typen und Konstanten gilt: Ab der Definition sind sie bekannt, bis die Prozedur endet. Folgen weitere (Unter) Prozeduren so kennen diese auch die Variablen. Variablen können in Prozeduren gleiche Namen wie globale Variablen haben. Sie verdecken diese dann. D.H. man kann nicht auf die globalen Variablen zugreifen. Anders als globale Variablen verlieren sie aber den Wert, sobald man die Prozedur verlässt. Man kann sich also nicht sicher sein, das der Wert erhalten bleibt. Man kann dies durch konstante Variablenparameter umgehen, die sozusagen das Gegenstück zu den static Variablen in C sind. die man aber nicht so häufig wie in C braucht, da Pascal eine bessere Rückgabemöglichkeit für Parameter hat.

Ein Beispiel:

const MaxX=100;
MaxY=200;

type Feldtyp = Array [1..MaxX,1..MaxY] of word;

function Suche_Maximum(const Feld: Feldtyp): word;

Var i: 1..MaxY;
erg: Word;

function Suche_Maxx(const y: integer): word;

var erg: word;
i: 1..maxx;

begin
erg:=0;
for i:=1 to maxx do if feld[y,i]>erg then erg:=feld[y,i];
Suche_Maxx:=erg;
end;

begin
erg:=0;
for I:=1 to maxy do
if
Suche_maxx(i)>erg then erg:=Suche_maxx(i);
Suche_maximum:=erg;
end;

In diesem etwas komplexeren Beispiel (das auch einiges enthält was wir noch nicht besprochen haben) achten Sie bitte nur auf die Variablen: Feld ist auch in der eingebetteten Prozedur Suche_maxx sichtbar, genauso wie die globale Konstante Maxx. Die lokalen Variablen i und erg verdecken aber die der Funktion Suche_maximum. D.h. Suche_maxx hat ein eigenes i und erg und verändert das von Suche_maximum nicht. Nur so kann ein i über den X Wertebereich und das andere über den Y Wertebereich laufen.

Parameter

Wie sieht es nun mit den Parametern aus, die man in den Klammern übergibt? Eigentlich nicht anders als wenn man Variablen definiert: Es muss der Typ angegeben werden und ein Name, unter dem der Parameter in der Prozedur benutzen kann. Mehrere gleiche Parameter trennt man durch Komma und verschiedene durch Semikolon:

Procedure Kreis(Integer x,y; Double radius);

ist eine Prozedur mit 3 Parametern: Zwei Ganzzahlen für die Koordinaten und einer Flieskommazahl. Man kann diese Prozedur so aufrufen:

Kreis(10,10,r);

Danach haben innerhalb der Prozedur Kreis × und y je den Wert 10 und Radius den Wert von r. Beim Aufruf muss man natürlich nicht den Typ angeben und kein Semikolon, sondern trennt alle Parametern mit Kommas. Die Typen der Parameter müssen aber stimmen. Man kann zwar Zahlen umwandeln, aber man könnte Kreis nicht mit 3 Variablen des Typs Char aufrufen.

Wichtig: Es gibt 3 Typen von Parametern! Pascal bietet anders als Java oder C drei Schnittstellen zur Übergabe von Parametern an:

Wertparameter

Die Parameter die bisher übergeben wurde sind Wert Parametern, dass sind alle Parameter wenn nicht die Schlüsselwörter der beiden folgenden Fälle VAR und CONST dazugestellt werden. Wertparameter (englisch: Call by Value)  übergeben den Wert, aber nicht die Variable selbst. Würde unsere Funktion Kreis z.B. den Parameter Radius ändern, so ändert das nicht den Wert der aufrufenden Variable r. Zahlen oder andere Konstanten kann man nur an Wertparameter übergeben. Ein Variablenparameter benötigt dagegen immer eine Variable als Übergabeparameter. Man verwendet diese Art von Parametern wenn man sagen will: OK Funktion mach bitte dies und das mit folgenden Werten. Alle Mathematischen Funktionen errechnen z.B. ein Ergebnis, beeinflussen aber nicht die übergebenen Werte. Hier können die Parameter auch Ausdrücke sein, zum Beispiel:.

Pos('"',Zeile);

oder

a:=sin(pi/4);

Dies ist möglich weil die Prozedur eine Kopie erhält, denn ein Ausdruck hat keine Adresse wie sie bei Variablenparametern übergeben werden.

Variablenparameter

Setzt man vor den Namen eines Parameters ein VAR so ist es ein Variablenparameter.(englisch: Call by reference).  Hier übergibt man die Variable direkt. Ändert man den Wert eines Parameters, so führt man diese Änderung auch am Original aus Bsp.:

VAR eins: Integer;
zwei: Single;

Procedure Daemlich(Var Veraendern: Integer; Bleibt_so: Integer);

begin
veraendern:=2;
bleibt_so:=4;
end;

begin
eins:=1;
zwei:=2;
Daemlich(eins,zwei);
end.


Daemlich hat zwei Parameter. Der erste Parameter ist ein Variablenparameter. Die Änderung in der Prozedur bewirkt das jeder Aufruf von Daemlich(eins,zwei) bewirkt das eins den Wert 2 bekommt. Zwei dagegen wird nie durch "Daemlich" verändert. Bitte beachten Sie auch folgendes: Damit man VAR und normale Parameter in Daemlich benutzen kann habe ich beide durch ein Semikolon getrennt. VAR Veraendern, Bleibt_so: Integer) hätte zwei VAR (Variablen) Parameter definiert. Außerdem ist nun der Typ wichtig. Man kann einem VAR Parameter nur eine Variable übergeben, keine Konstante oder Ausdrücke und es muss derselbe Typ sein, also hier Integer. Der Aufruf von Daemlich (zwei,eins) ergibt einen Fehler, da Zwei vom Typ Single ist.

Da man bei Variablenparametern keine Kopie sondern die Adresse der Variable übergibt sollte man große Strukturen (Arrays etc.). nur so übergeben, auch wenn man sie nicht verändert - der Code ist schneller und spart Speicherplatz. Zum Typ ist noch zu sagen, das Turbo Pascal es hier bei den Standardtypen locker nimmt: Definiert man eine Routine wie

Function Suche(VAR Ergebnis: LongInt): Boolean;

So kann der übergebene Parameter von jedem Integertyp sein, also auch Byte, Word... Klar ist natürlich, das der Wertebereich erhalten bleibt: Eine übergebene Byte Variable bekommt Probleme wenn man in dieser Function Suche schreibt:

Suche:=1234;

Da der Wertebereich von Byte bei 255 endet (Sie würden den Wert modulo 256 enthalten also hier 210 (210 + 4*256 = 1234). Bei Strings kann man durch Compilerschalter beeinflussen, ob nur gleich lange Strings erlaubt sind oder sie einfach abgeschnitten werden. Bei eignen Typen tragen Sie aber die Verantwortung:

Type
Zeichen1 = ['A'..'Z'];
Zeichen2 = ['A'..'Z'];

Var
a: Zeichen1;
b: Zeichen2;

procedure dummy(Var x: Zeichen1);

begin
end
;

begin
dummy(a);
dummy(b); {Hier gibt es einen Compilerfehler}
end...


läuft nicht. Zeichen1 und Zeichen2 sind zwei benutzerdefinierte Typen , die zufällig die gleiche Zeichenmenge enthalten. Bei Benutzerdefinierten Typen ist der Compiler streng. Auch wenn in diesen beiden Typen eigentlich dieselben Elemente enthalten sind. Dasselbe schlägt auch zu, wenn man geschrieben hätte:

Var a: 'A'..'Z';
b: 'A'..'Z';

Konstantenparameter

Erst ab Turbo Pascal 7.0 gibt es den Konstantenparameter. Es ist ein Parameter der von der Prozedur nicht verändert werden darf. Er wird durch ein CONST vor dem Namen angezeigt. Das erzeugt etwas schnelleren Code, wenn es sich nicht um einfache sondern komplexere Typen (Strings, Rekords, Arrays...) handelt. Vor allem aber ist es eine Möglichkeit Programmierfehler zu erkennen, da der Compiler Zuweisungen an Konstantenparameter bemäkelt. Für die Praxis kommt man aber auch gut mit normalen Wertparametern aus. (In C ist Const erheblich wichtiger, da es dort keine VAR Parameter gibt und man so alle VAR Parameter mit Zeigern emulieren muss). Trotzdem sollten sie immer Const verwenden wenn sie ihrer Funktionen einen Parameter als Information übergeben, z.B. als Grenze, als Index oder Argument. Es ist verlockend eine lokale Variable einzusparen, und den Wertparameter zu verändern. Doch das ist kein guter Stil und fehleranfällig

Funktionen

Für Funktionen gilt alles was bei Prozeduren gesagt worden ist. Es kommt nun nur noch etwas neues hinzu: Der Rückgabewert. Er wird hinter der Parameterliste getrennt durch einen Doppelpunkt angegeben:
function Kleinbuchstabe(buchstabe : char): boolean;

begin
if
(buchstabe>='a') and (buchstabe<='z') then kleinbuchstabe:=true
else kleinbuchstabe:=false;
end;

VAR zeichen : char;

begin
zeichen:='g';
writeln(kleinbuchstabespan class= "c12">(zeichen));
end.

Hier sieht man alle Änderungen: Anstatt Procedure erscheint nun function als Schlüsselwort. Der Rückgabetyp wird mit einem Doppelpunkt an die Parameterliste angeschlossen. Der Rückgabewert wird zugewiesen indem man dem Funktionsnamen einen Wert zuweist. Man kann also den Funktionsnamen als eine Variable des Rückgabetyps betrachten, bei Kleinbuchstaben also als eine Boolean Variable. Anders als in C kann man beliebig oft einen Wert zuweisen, jeweils der letzte wird bei Funktionsende zurückgeben. Auch wird bei Zuweisung nicht die Funktion verlassen. Nur Vorsicht: Eine Abfrage/i> des Wertes der Funktion bewirkt ein erneutes Aufrufen der Funktion. Man nennt dies Rekursion:

Function add100 : integer;

VAR c : integer;

begin
c:=add100;
ad100:=c+100;
end;

ist ein korrekter Code, der jedoch in einer Endlosschleife endet. In der Zeile c:=add100 wird nicht c der Aktuelle Wert von ad100 zugewiesen sondern es wird die Funktion add100 erneut aufgerufen, in der wiederum derselbe Fehler passiert, das geht so oft bis der Computer keinen Speicher mehr hat und einen Fehler meldet. Die ganze Verwechslung ist aber nur bei Funktionen mit keinem Parameter möglich, da man sonst bei einem Aufruf die Parameterliste übergeben müsste. Bei Delphi und auch FPK und VP gibt es noch die interne Variable Result, der man Werte zuweisen und die man abfragen kann. Ihr Wert wird am Funktionsende übergeben. Anders als beim Funktionsnamen ist es so möglich auch Werte abzufragen, und so auf eine Zwischenvariable zu verzichten.

Eine Funktion nehmen Sie immer dann, wenn sie einen und zwar genau einen Rückgabewert brauchen. Paradebeispiel sind mathematische Funktionen. Mehrere Parameter verwirklichen sie mit Prozeduren mit mehreren VAR Parametern. Wenn ihre Funktion aber auch Probleme haben kann, dann macht man es oft so, das die Funktion nur einen Boolean Rückgabewert hat, der z.B. false ist wenn es Probleme gab und das eigentliche Ergebnis steckt dann in einem VAR Parameter.

Speichermangel....

Wenn Sie mit einem 16 Bit System arbeiten (Turbo Pascal 1-7, Delphi 1), so werden sie bei größeren Programmen feststellen, das der Speicher begrenzt ist. Max. 64 KByte sind für Variablen möglich. Dies gilt aber Gott sei Dank nur für jeweils einen Block, d.h. das Hauptprogramm kann 64 K haben, jede einzelne Funktion aber auch 64 K. Braucht man mehr so kann man dynamisch Variablen erzeugen, dann belegt ein Pointer 4 Byte und kann wiederum auf 64 K zeigen. (dazu in späteren Kapiteln mehr). Einschneidender ist aber die Einschränkung, dass eine Struktur max. 64 K Belegen kann. z.B.:

VAR Editor = Array [1..1000] of String[80];

gibt Probleme, da das Array insgesamt 80 Kilobyte belegt (1000 × 80) und so die 64 K Grenze überschreitet. Auch wenn man auf eigene Routinen ausweicht und Speicher dynamisch holt (mehr dazu in den folgenden Teilen), bleibt diese Grenze bestehen. Sie können also nie eine Datenstruktur erzeugen die größer als 64 K ist z.B. um ein Bild darin zu speichern. Hier gibt es eigentlich nur einen Ausweg: Aufsteigen auf ein 32 Bit System (FPK, VP, Delphi 2 aufwärts). /p>

TTypen

Bisher haben wir Variablen benutzt die einen Typ hatten, wir können aber auch eigene Typen definieren. Ein Typ ist praktisch eine Schablone. Man sagt: Ich will Variablen haben und die sollen alle gemeinsame Merkmale haben. Ein Typ ist aber keine Variable, sondern eine Variable kann erzeugt werden, wenn man den Typ definiert hat. Der Typ ist also praktisch eine Vereinbarung, während eine Variable immer mit konkreten Werten belegt werden kann. Es gibt in Pascal mehrere Typen die wir sukzessiv kennen lernen, hier eine Einführung in die Typendefinition. Sie beginnt mit dem Wort Type und einer Definition ähnlich wie bei einer Konstante in der Form:

NeuerTyp = Typart;

Sie können damit z.B. Typen umdefinieren: /p>

Type Single = Double;

würde bewirken, dass sie auch dann mit Doppelter Genauigkeit rechnen wenn Sie eine Single Variable benutzen. Sie haben Single als Double umdefiniert. Wichtiger sind aber drei neue Typen:

Type
Aufzaehlungstyp = (Bube,Dame,Koenig,Ass);
Grussbuchstabentyp = 'A'..'Z';
Grossbuchstabenmenge = Set of Grussbuchstabentyp;

VAR
Karte: Aufzaehlungstyp;
Buchstabe: Grussbuchstabentyp;
zeichen = Grossbuchstabenmenge;

Dies zeigt alle 3 Typen und ihre Benutzung. TYPE legt nur den Typ fest, erst mit VAR kreieren sie eine Variable mit diesem Typ. Sie hätten auch schreiben können:

Karte: (Bube,Dame,Koenig,Ass);

Das hätte aber den Nachteil, das Sie bei einer zweiten Definition

Karte2: (Bube,Dame,Koenig,Ass);

zwar eine neue Variable haben, aber diese haben nicht denselben Typ, eine Zuweisung Karte:=Karte2 ergibt einen Fehler. Leider eine der wenigen Unstimmigkeiten in Pascal. So ist es sinnvoll alle benötigte Typen zu deklarieren und diese dann für die Variablen zu verwenden. Da eine Typdeklaration nur wichtig für den Compiler ist, keinen Speicher belegt und keine Prozessorzeit benötigt spricht nichts dagegen am Kopf des Programms alle Typen die man braucht zu deklarieren und dann nur noch mit dem Typnamen zu arbeiten. Wenn sie später auch wissen was Units sind bietet es sich an solche Typen und Konstanten in eine eigene Unit auszulagern. Dann stehen sie allen benötigten Programmteilen durch Einbinden zur Verfügung.

Der erste Typ ist ein Aufzählungstyp. Er erlaubt es der Variable Karte einer der 4 Werte die angegeben sind zuzuweisen. Das hat eine enorme Verbesserung der Lesbarkeit zur Folge z.B. im Programmtext:

if karte=Ass then...

Man kann mit der Funktion Ord(variable) herausfinden welche Nummer der Wert hat. Diese werden von 0 durchgezählt und Ass hätte so die Nummer 3.

Der Teilbereichstyp 'A'..'Z' ist eine Untermenge eines anderen Typs, z.B. hier Char. Wozu das werden Sie fragen? Nun zum einen macht es den Programmtext erklärender. Zeichen: 'a'..'z' zeigt, das es sich bei dieser Variable nicht nur um eine Char Variable handelt, sondern auch nur Kleinbuchstaben als Werte zulässig sind. Zudem kann der Compiler bei der Laufzeit Überschreitungen feststellen, wenn man dies wünscht - sehr geschickt für das Debuggen. Die Zuweisung von "#" an Zeichen ist möglich, denn es ist ja eine Char Variable, aber es sprengt den angegebenen Wertebereich und wenn man wünscht, sagt einem das der Compiler. (Und das auch noch zur Laufzeit)

Mit dem Wort span class="c14">SET OF definieren Sie eine Menge eines anderen Typs. Bislang konnte jeder Typ nur einen Wert annehmen: Großbuchstabe z.B. nur ein 'A', aber nicht gleichzeitig den Wert 'A' und 'F'. Bei dem Mengentyp ist dies nicht so. Die Großbuchstabenmenge kann die Summe aller Werte von 'A' bis 'Z' - also 26 Stück annehmen. Die Klammern kennzeichnen dies als eine Menge (Sie kommen auch noch als Array Klammern vor).

Man wird dies einsetzen wenn ein Wert nicht nur eine Option hat, sondern mehrere. Sie kennen das von den Kästchenbuttons in Windows, hier können Sie auch mehrere Optionen mit einem Kreuz markieren.

Für Mengen gibt es eigene Rechenoperationen: + für die Vereinigungsmenge und - für die Differenzmenge. Besonders häufig - auch ohne Mengendefinition - braucht man den Operator IN::

if (Menueoption in ['0'..'9','ä','ü','ö']) then

führt den THEN Zweig genau dann aus, wenn Menueoption einen der angegebenen Werte hat und erspart einem so einen ganzen Wust von CASE oder IF THEN Abfragen, die in C nötig wären. Da '+' und '-' nun mit Mengen arbeiten muss man nun auch einzelne Werte in den eckigen Klammern setzen.:

Menueoption:=Menueoption+ ['5'];
Menueoption:=Menueoption-['ä'..'ü'];

Fügt 5 zur Menge hinzu und entfernt die Elemente 'ä','ö' und 'ü' aus der Menge. Die beiden Punkte.. geben einen Teilbereich an, also von..bis (einschließlich). Man verwendet sie auch sonst bei Arrays für die Dimensionen,

Strings /h3>Strings sind Zeichenketten. Wie einfach in Pascal (aber auch Basic) Strings zu benutzen sind habe ich erst gemerkt als ich in C,C++ und Java programmieren musste. Anders als C kann man in Pascal sehr komfortabel mit Zeichenketten arbeiten. Definiert werden Strings so:
    
    VAR
String1 : String [80];
String2 : String;

CCONST
vordef_string String[70] = 'Hallo!';

String1 ist eine Zeichenkette von maximal 80 Zeichen Länge - mehr wird abgeschnitten. String2 ist eine Zeichenkette mit der maximalen Länge von 255 Zeichen. Gibt man keine Längenangabe an so wird die maximale Länge angenommen und das sind 255 Zeichen. Mehr geht unter Turbo Pascal 1-7 nicht. 32 Bit Systeme wie Delphi 2.., VP und FPK sprengen diese Grenze und erlauben bis zu 2 Gigabyte für einen String. Da diese genauso wie kurze Strings funktionieren, gehe ich darauf nicht ein, alles was hier über die kleinen Strings gesagt wird ist auch auf die langen übertragbar. Diese sind auch nicht solche Speicherfresser wie die hier vorgestellten statischen Strings. (Jeder String belegt bei Turbo Pascal soviele Bytes wie hier angegeben, egal ob nur ein Teil belegt ist. Bei den 32 Bit Systemen belegt ein String nur soviele Bytes wie er lang ist).

Wichtig ist, das ein String immer ein Byte mehr als die angegebene Länge belegt. Der String1 also 81 Byte. Egal ob der String leer ist oder 80 Zeichen enthält, er belegt immer 80 Bytes, also wenn Sie wissen das sie nie mehr als 20 Byte benötigen so definieren Sie nur einen String[20]. Bei Variablenparametern kann man dem Compiler sagen ob er nur Strings zulässt die dieselbe Länge haben oder nicht. Es macht wenig String einer Prozedur die das 40.ste Zeichen eines Strings verändern will einen String mit 20 Zeichen Länge zu übergeben.... Es ist deswegen nicht dumm im Typenteil eigene Stringtypen zu definieren:

type
Zeilentyp String [80];
Worttyp String[20];

VAR
zeile : Zeilentyp;
wort : Worttyp;

Sie können, um auf ein bestimmtes Zeichen innerhalb eines Strings zuzugreifen, diesen als ein ARRAY of CHAR mit der angegebenen Maximallänge interpretieren:

Wort[9]:='c';
{interpretiert Worttyp als Array [1..20] of Char}

Jedes Zeichen hat einen Index der von 1 ausgeht und bis zur deklarierten Maximallänge geht. Im Zeichen 0 steht die Länge. Sie sollten jedoch verzichten diese direkt zu manipulieren (ich habe das auch so gemacht und dann unter Delphi sehr merkwürdige Probleme gehabt, da dort die Strings anders organisiert sind...). Strings sind zueinander zuweiskompatibel, d.h. sie können auch schreiben: Wort:=zeile. In diesem Fall enthält Wort aber nur die ersten 20 Zeichen von Zeile, ist ja klar, mehr geht nicht rein!.

Stringfunktionen

Man kann Strings mit "+" verketten und mit:= zuweisen, sowie mit = vergleichen. Für alles andere aber braucht man ein paar Funktionen und Prozeduren. Die wichtigsten:
  • Function length(Stringvar: String): Integer: liefert die Länge der angegeben Stringvariable zurück, d.h. nicht die maximal möglichen Zeichen, sondern wieviel Zeichen aktuell gespeichert sind.
  • Function Copy(Stringvar: String; Start,Laenge: Integer): String liefert einen Teilstring von Länge Zeichen aus dem String ab der Position Start. Ist Länge größer als die Länge des Strings, so bekomt man den Rest bis zum Ende und keinen Laufzeitfehler wie bei Java.
  • Procedure Insert(Source: String; VAR Dest: String; Start: Integer) fügt den String Source an die Position Start in Dest ein.
  • Procedure Delete(VAR Stringvar; Start,Anzahl) Löscht aus Stringvar von Start ab genau Anzahl Zeichen. Auch hier gibt es keinen Fehler wenn Start/Länge größer als die tatäschliche Länge sind.
  • function Pos(Source,Ziel: String): integer liefert das erste Vorkommen des Strings Source in Ziel oder 0 wenn dieser nicht gefunden wurde.
  • function sizeof(stringvar): Liefert die Größe der Variable im Speicher, also zuszüglich des Längenbytes (funktioniert so nur bei Strings fester Länge!)
Wenn sie schon mal in C/Java programmieren mussten so stellen Sie fest, das die Stringfunktionen sehr gutmütig sind. Ist der Index oder die Länge größer als die akt. Länge so machen die Funktionen das beste daraus und bearbeiten nur das Stück des Strings, das noch dazu passt oder eben gar nichts. Bei C schreiben die Funktionen dagegen wild im Speicher herum (Sogannte Buffer-Oberflows) und in Java werden Exceptions nach ihnen geworfen, die sie wiederum auffangen müssen.

Mehr über Stringfunktionen und weitere Funktionen und Prozeduren erfahren Sie in der Online Hilfe von ihrem Pascal Compiler.(Shift-F1). Insgesamt ist das String Handling in Pascal sehr viel komfortabler als in C. Vor allem können sie nicht andere Speicherbereiche überschreiben. Es ist also problemlos möglich einem String[4] den String 'Hallo'+'Du Da!' zuzuweisen. Es steht eben dann nur "Hall" da drinn. IN C/C++ hätten sie die darauf folgende Variable auch überschrieben. Es gibt eine Ausnahme bei 32 Bit Systemen. Wenn sie die Strings als Arrays dort bearbeiten, also z.B Zeile[5]:='4'; dann verlassen Sie den sicheren Boden der Stringfunktionen und müssen gewährleisten, das in diesem Fall der String auch mindestens 5 Zeichen lang ist. Dies macht man meistens über eine Wächterabfrage mitels Length.

Arrays

Ein Array ist ein Feld mit Elementen aus einem Typ. Die Größe muß vorher festgelegt werden, der Typ wird mit of angegeben:
type farben Karo,Pik,Herz,Kreuz)

VAR
Speicher : [Array [1000..2000] of Byte;
trumpf : Array [farben] of Aufzaehlungstyp;

Sie sehen das der Index nicht bei 0 anfangen muß und auch eine Teilbereich oder Aufzählungstyp als Index dienen kann. Das macht Pascalprogramme gut verständlich. Mit dieser Definition wäre z.B. folgendes möglich:

Trumpf[Herz]:=Koenig;
if Trump(Pik]=Ass then ...

- ist doch sofort verständlich oder?
Mehrere Dimensionen erreicht man durch mehrere Teilbereiche durch Kommas getrennt:

Wuerfel: [1..5,0..4,10..20] of Double;

legt ein Array mit 5 × 5 × 11 Elementen fest. Der Zugriff auf ein Element geht so:

Wuerfel[1,0,12]:=pi;

oder: Wuerfel[1][0][12]:=pi;

Beide Schreibweisen sind möglich. Will man ein Array als vorgelegte Variable unter CONST definieren so fasst man einen Indexbereich in klammern () zusammen und trennt Elemente mit Kommas, mehrdimensionale Arrays werden von vorne nach hinten definiert:

Const Tic-Tac-Toe = Array [1..3,'A'..'C'] of Byte =((0,0,0),(1,1,1),(0,1,0));

Das Element [1,'C'] hätte so den Wert 0. Es ist hier farblich hervorgehoben. Es werden also zuerst die Elemente 1..3 der Zeile 'A' gesetzt, dann dasselbe für 'B' und 'C'. In anderen Worten: Die Dimensionen werden von rechts nach links barbeitet.

Offene Array Parameter

Sehr lästig beim Bearbeiten in Funktionen und Prozeduren ist, das diese nur Arrays einer bestimmten Größe akzeptieren. Eine Funktion die z.B. einen Parameter vom Typ "Array [1..10] of xy" hat akzeptiert keine Übergabe einer Variable mit dem Typ "Array [1..20] of xy". Obgleich es sich um diesselbe Struktur handelt, nur die Anzahl der Elemente unterschiedlich ist.

Man kann dies umgehen indem man in der Deklaration folgendes schreibt

Procedure MachWas(var par: array of xy);

Hier fehlt die Angabe der Dimension, also Anzahl der Elemente ganz. In der Funktion wird par als ein Array mit der Untergrenze 0 und einer variablen Obergrenze angesehen. Die Funktion kann über zwei Funktionen die Anzahl der Elemente und die Größe ermitteln:

  • High(par) liefert die Nummer des letzten Elements (Index: 0..High)
  • Low(par) liefert immer 0
  • sizeof(par) liefert die Größe des Arrays in Byte
Man iteriert dann in etwa so über das Array:

for I:=0 to high(par) do par[i]:=...

Eine Anwendung ist hier gezeigt, es handelt sich um den Quicksort Algorithmus, der durch die Verwendung von Prozedurtypen und offenen Arrays so angepasst wurde, das er generell ist:

type
  Element = record
    name: string;
    Gehalt: Double;
    Abteilung: string [3];
  end;
{Vergleichsfunktion}
Comparefunc = function (var A,B: Element): Boolean;
    { Liefert True wenn a<b, sonst false}
function Vergleich(var A,B: Element): Boolean; far;
function Int2Str(Zahl: Integer ): string;
var Temp: string;
begin
  Str(Zahl:5,Temp);
  Int2Str:=Temp;
end;
begin
  Vergleich:=A.Abteilung+Int2Str(Trunc(A.Gehalt))>B.Abteilung+Int2Str(Trunc(B.Gehalt));
end;
{Klassische Quicksort Implementierung}
procedure Quicksort(Compare: Comparefunc; var Keys: array of Element);
procedure Sort(L, R: Integer);
var
    I, J: Word;
    X, Y: Element;
begin
  I:= L; J:= R; X:= Keys[(L+R) div 2];
  repeat
    while Compare(Keys[I],X) do Inc(I);
    while Compare(X,Keys[J]) do Dec(J);
    if I <= J then
    begin
      Y:= Keys[I]; Keys[I]:= Keys[J]; Keys[J]:= Y;
      Inc(I); Dec(J);
    end;
  until I > J;
  if L < J then Sort(L, J);
  if I < R then Sort(I, R);
end;
begin
  Sort(0,High(Keys));
end;
{Hauptpogramm}
var Abteilungen: array [1..100] of Element;
    I          : Integer;
begin
  Randomize;
  for I:=1 to 100 do
  with Abteilungen[I] do
  begin
    name:=Chr(65+Random(26))+'XYZName';
    Gehalt:=Random(10000);
    Abteilung:=Chr(65+Random(26))+'BC';
  end;
  Quicksort(Vergleich,Abteilungen);
  for I:=1 to 100 do
  with Abteilungen[I] do WriteLn(Abteilung,' ',Gehalt:5:0,' ',name);
end.

Der Vorteil dieser Vorgehensweise ist die:

  • Sie übergeben eine Vergleichsfunktion. Einfache Datentypen kann man natürlich direkt vergleichen, wenn sie jedoch Records nach Feldinhalten vergleichen wollen, so können sie hier einen Funktionstype übergeben den sie dann an ihre Zwecke anpassen können.
  • Dadruch das nur ein Array of Element übergeben wird, akzeptiert diese Prozedur jede Arraygröße
  • In der Praxis werden Sie Element und Vergelichsfunc in einer Unit (Siehe Teil 4) implementieren, die sie einbinden. Ihr Quicksort Algorithmus muss nie angepasst werden, bei einem neuen Programm ändern sie nur die Vergleichsfunktion und die Definition eines Elements und schon können sie ein Array sortieren lassen.

Untypisierte Parameter

Parameter ohne Typ benötigt man in Pascal vor allem für low Level Funktionen. Die Parameter haben im Funktionskopf dann gar keine Typangabe. Anwendungen sind vielleicht schnelle Kopier oder Vergleichsfunktionen, da man dann mit Move ganze Arrays kopieren kann ohne Elementweise zu kopieren. Für Übergabe an Assembler oder zugelinkte Funktionen kann dies auch manchmal nützlich sein.

In einer Funktion muss man, wenn man etwas konkretes mit den untypisierten Variablen machen will sie "casten". Daher mal hier ein Einschub für das in Pascal selten gebrauchte Casten unterhalb dieses Absatzes. Man tut dann im Block so als wäre die Variable vom gewünschten Typ :

function Gethighest(Var par; n : integer): byte;;

type bytearray array [0..maxint] of byte;
var temp : Byte;
i : integer;

begin
temp:=0;
for i:=0 to n-1 do
if
bytearay(par)[i]>temp then temp:=bytearay(par)[i];
gethighest:=temp;
end;
Hier ermittelt die Funktion das Byte in dem übergebenen Parameter mit dem höchsten Wert. Dazu wird zweimal gecastet. Zum einen wird zuerst mal so getan als wäre der Parameter ein Array of Byte beliebiger Größe (Maxint ist die größte Zahl die ein Ganzzahlwert annehmen kann). Danach wird ein Eintrag gemerkt und der Variable temp zugewiesen. Bitte beachten Sie: Damit unterlaufen sie komplett die Typüberprüfung des Compilers und sind auf dem Niveau von C angelangt. Man kann diese Funktion intelligent aufrufen indem man ihr ein Byte oder Char Array (String) übergibt, man kann aber auch unsinniges machen:

a:=GetHighest(3.14159);

Was da rauskommt hat keinerlei Sinn, denn Flieskommazahlen bestehen nicht aus einem Array von Byte Werten.

Casten

Ein Cast ist eine Umwandlung einer Variable eines Typs in einen andern Typ. Casten braucht man viel in C, in Pascal wegen der automatischen Typkonvertierung wenig. Es gilt folgende Regeln
  • Sie müssen nicht Casten wenn der Empfänger Typ größer als der Sender ist: z.B. können sie eine Integer Variable an eine Double Variable zuweisen. Das umgekehrte geht nicht, dann würden Sie Nachkommastellen verlieren. Analoges gilt bei Strings und Zeichen.
  • Typen einer gleichen Kategorie sind zueinander zuweisungskompatibel. D.H. Sie können alle Integer Typen einander zuweisen. Allerdings können dann Werte verloren gehen, z.B. wenn sie einen Longint einer Byte Variable zuweisen.
  • Umgewandelt können Typen werden, wenn der neue Typ auf ähnlichen internen Abbildungen basiert. Zum Beispiel werden alle Zeichen, Integervariablen, Aufzählungs und Teilbereichstypen intern als ganze Zahlen (unterschiedlicher Größe) gespeichert. Es sind damit Umwandlungen ineinander möglich.
Ein Cast wird gemacht indem man schreibt

Variable:=Casttyp(Variable);

z.B:

var i : integer;

begin
i:=Integer('A'); {A ist ein char Wert!}
end.
Diese Zeile weist der Variable i den Wert 65 zu (ASCII Wert von 'A'). Es wird dabei der Char Wert in einen Integer Wert gecastet. Man könnte auch sagen: Es wird eine Funktion aufgerufen die einen umgewandelten Integer Wert zurückgibt. Man braucht das Casten insbesondere, wenn man auf Datenträgern Werte wie Boolean oder Aufzählungstypen speichern will und diese mit writeln / readln schreibt und liesst. Dann konvertiert man in Integer beim Schreiben und in den gewünschten Typ beim Lesen.:
var boolvar : Boolean;
temp : Byte;
begin
Writeln(datei,Byte(Boolvar));
readln(datei,temp);
boolvar:=boolean(Temp);
end;

Ihre Übungen

Heut einmal keine neuen Programme schreiben, aber versuchen Sie die Lösungen aus Teil 1+2 mit Funktionen und Proceduren zu lösen und vergleichen sie hinsichtlich Übersichtlichkeit. Schauen Sie sich vor allem mal in der Online Hilfe an, welche Proceduren und Funktionen ihr Pascal Compiler alle hat und probieren Sie einige davon aus. Ich werde bewusst hier keine erwähnen, weil sobald sie verstehen können was eine Funktion / Prozedur ist - ein eigenständiges Unterprogramm mit Parametern - sie zum Benutzen von Funktionen ihres Compilers eigentlich nur noch die Funktionsköpfe und eine Beschreibung brauchen, und dazu ist die Online Hilfe da.
Schon mit dem jetzigen Wissen können Sie eigene Funktionen programmieren die sehr nützlich in eigenen Programmen sind. Dazu zwei Beispiele:

Die trigonometrischen Funktionen sin(, cos( und arctan( arbeiten nur mit Bogenmaß (Periode 2 Pi), will man dagegen im Winkelmaß arbeiten so muß man umrechnen - das erledigt eine eigene Funktion besser als das 10 mal im Quelltext zu tun.

function sinus(winkel : extended): extended;

const umrechungsfaktor 360 / (2*Pi);

begin
sinus:=sin(winkel/Umrechnungsfaktor);
end;

Oft benötigt man auch eine Funktion die bei einer Eingabe Leezeichen entfernt, die vor der eigentlichen Eingabe stehen:

function Trim(eingabe : string): String;

begin
while
(length(eingabe)>0) and (eingabe[1]=' ') do
delete(eingabe,1,1);
trim:=eingabe;
end;

Das Beispiel funktioniert in dieser Form nur bei eingeschaltetem Kurzschluss-Auswerten von logischen Operationen (Warum?). Es zeigt auch wie man den Parameter bei Wert Parametern ungefährlich für die Übergabe verändern kann. Schreiben Sie nun auch mal eine Funktion die Leerzeichen am Ende der Eingabe entfernt.

Was sie jetzt auch schon machen können ist mit dem Computer zahlreiche Denksportaufgaben lösen. Hier mal eine Aufgabe aus der ARD Sendung "Kopfball" vom 23.12.2001: "Eine Bahnhofsuhr mit 7 Segment Anzeige (Wie bei Taschenrechnern) zeigt die Zeit an. Dabei hat sie je zwei Ziffern für Stunden, Minuten und Sekunden, die erste Stundenziffer leuchtet aber erst ab 10 Uhr (die
führende Null wird nicht dargestellt). Bei welcher Uhrzeit leuchten die meisten Segmente?

Klar man kann nachdenken oder den computer einfach mal von 0:00:00 bis 23:59:59 alle Uhrzeiten durchprobieren lassen. Das wäre doch eine nette kleine Aufgabe. Herauskommen sollte dann so was. Falls sie eine andere Lösung finden: Die korrekte Uhrzeit ist 20:08:08.


Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.

Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.

Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.

Mehr über das Buch auf dieser eigenen Seite.

Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.

Sitemap Kontakt Impressum / Datenschutz Neues Hier werben / advertisment here Buchshop Bücher vom Autor Top 99