0100 99ff 0153 ... TURBO3.OK
.PL72
.MT1
.MB3
.FM1
.FO                                                              
.HE                                       TURBO-Pascal  Seite #
.PN66
.PO8



15. Pointertypen

Die bisher besprochenen Variablen sind statische Variablen,  d.h. 
ihre Form und Groesse sind im Deklarationsteil festgelegt und sie 
bleiben auch waehrend der gesamten Ausfuehrung des Blockes, indem 
sie erklaert sind,  existent.  Programme benoetigen jedoch häufig 
eine  Datenstruktur,  die  sich in Form und Groesse waehrend  der 
Ausfuehrung des Programmes aendern kann. Aus diesem Grunde wurden 
dynamische  Variable eingefuehrt,  die man erst  dann  generieren 
kann,  wenn sie gebraucht werden,  und die man nach ihrer Verwen-
dung, wenn sie nicht mehr bebraucht werden, beseitigen kann. 

Solche   dynamischen  Variablen  werden  nicht  explizit  in  der 
Variablendefinition   wie die statischen Variablen  erklaert  und 
man  kann  sich auf sie auch nicht direkt durch einen  Bezeichner 
beziehen.  Man  verwendet fuer sie spezielle  Variable,  die  nur 
jeweils   die   Speicheradresse  der   entsprechenden   Variablen 
enthalten,  die  also zu diesen Variablen zeigen  (point).  Diese 
speziellen Variablen werden als Pointervariablen bezeichnet.


15.1 Definition einer Pointervariablen

Ein  Pointertyp wird durch das Symbol ^ definiert.  Diesem Symbol 
folgt der Typbezeichner der dynamischen Variablen,  auf die  sich 
die Pointervariablen dieses Typs beziehen.

Das  folgende Beispiel zeigt,  wie ein Satz und die auf ihn  sich 
beziehenden Pointer definiert werden:

     type
          PersonPointer = ^PersonRecord;

          PersonRecord  = Record
                              Name   : String[50];
                              Job    : String[50];
                              Next   : PersonPointer;
                          end;
     
     var
          FirstPerson,LastPerson,NewPerson : PersonPointer;

Die Variablen FirstPerson, LastPerson und NewPerson sind Pointer-
variablen,  die  den Zugriff zu Saetzen vom Typ PersonRecord  ge-
statten.  Aus  dem Beispiel ist auch ersichtlich,  dass sich  der 
Typbezeichner  in einer Pointertypdefinition auf einen Bezeichner 
beziehen kann, der bis dahin noch nicht erklaert wurde.

15.2 Zuweisungsvariable (New)

Bevor es ueberhaupt einen Sinn hat,  eine von diesen Pointervari-
ablen zu verwenden, benoetigen wir natuerlich einige neue Variab-
le,  auf die sie zeigen. Die Generierung und Zuweisung zu solchen 
neuen Variablen von irgendeinem Typ erfolgt mit der  Standardpro-
zedur New.  Die Prozedur hat einen Parameter, der eine Pointerva-
riable von dem Typ ist, den wir generieren wollen.
Beispielsweise generiert 

               New(FirstPerson)

eine neue Variable vom Typ PersonRecord.  Damit zeigt FirstPerson 
auf einen dynamisch erzeugten Satz von Typ PersonRecord.

.pa

Zuweisungen zwischen Pointervariablen kann man solange durchfueh-
ren,  solange  sie  vom gleichen Typ sind.  Pointervariablen  vom 
gleichen Typ koennen ebenso durch die Vergleichsoperatoren =  und 
<>  verglichen  werden.  Diese Operationen geben  ein  Boolesches 
Ergebnis (True oder False) zurueck. 

Der  Pointerwert  nil gehoert jedem Pointertyp  an.  Dieser  Wert 
verweist auf keine dynamische Variable und kann  Pointervariablen 
zugewiesen  werden,  um anzuzeigen,  dass sie keinen verwertbaren 
Zeiger enthalten. Natuerlich kann nil auch im Vergleich verwendet 
werden.

Variable, die man mit der Standardprozedur New erzeugt, werden in 
einer  Stack-artigen Struktur aufgebaut.  Man bezeichnet sie  als 
Heap.  Das  TURBO-Pascal-System steuert den Heap durch Verwendung 
eines  Heap-Pointers,  der zu Programmbeginn auf die erste  freie 
Adresse  des Speichers weist.  Bei jedem Aufruf von New wird  der 
Heap-Pointer um soviele Bytes in Richtung Speicherende  weiterge-
stellt, als die dynamische Variable Bytes enthaelt.


15.3 Mark und Release

Wenn  eine  dynamische Variable nicht mehr im Programm  benoetigt 
wird,  kann man durch Verwendung der Standardprozeduren Mark  und 
Release  den Speicherplatz zurueckerhalten,  der dieser Variablen 
zugewiesen wurde. 

Die  Prozedur Mark weist den Wert des Heap-Pointers einer Variab-
len zu:
Syntax:
               Mark(Var);

wobei Var eine Pointervariable ist.

Die Prozedur Release setzt den Heap-Pointer auf die Adresse,  die 
in seinem Argument enthalten ist:
Syntax:
               Release(Var);

wobei Var eine Pointervariable ist, die vorher durch Mark gesetzt 
wurde.
Release gibt damit den gesamten Speicherplatz zurueck,  der ober-
halb der im Argument angegebenen Variablen liegt.  Es ist natuer-
lich nicht moeglich, den Speicherplatz von Variablen zurueckzuer-
halten, die in der Mitte des Heap liegen.

Mit der Standardfunktion MemAvail ist es moeglich zu einer belie-
bigen  Zeit,  die augenblickliche Groesse des vom Heap  benutzten 
Speicherplatzes zu erhalten. Genaueres darueber im Anhang A.


15.4 Verwendung der Pointer

Nehmen  wir  an,  in  einem  Programm  sei die  Prozedure  New 
verwendet worden,  um  eine  Reihe  von Saetzen  des  Typs  PersonRecord 
(wie  im obigen Beispiel) aufzubauen.  Dabei sei das Feld Next so 
verwendet worden,  dass es auf den naechsten PersonRecord  weist. 
Dann  wuerden  die folgenden Anweisungen durch die  Liste  dieser 
Saetze   gehen   und   den  Inhalt  jedes   Satzes   ausschreiben 
(FirstPerson weist auf den ersten Satz in der Liste):

     while FirstPerson <> nil do
     begin
          Writeln(FirstPerson^.Name,'is a  ',FirstPerson^.Job);
          FirstPerson := FirstPerson^.Next;
     end;

Hierbei kann man FirstPerson^.Name lesen als FirstPerson's  Name, 
das ist das Feld Name in dem Satz, auf den FirstPerson zeigt.

Das  folgende Beispiel demonstriert die Verwendung  von  Pointern 
bei einer Liste,  die Namen und entsprechende Berufswuensche ent-
haelt.  Die Namen und Berufswuensche werden nacheinander eingege-
ben.  Die  Eingabe  der Liste wird durch Druecken nur der  Enter-
Taste bei der Eingabe des Namens beendet. Danach wird die gesamte 
Liste gedruckt.  Nach dem Druck wird der Speicherplatz  freigege-
ben,  der  von  der  Liste benutzt  wurde.  Die  Speichervariable 
HeapTop  wird  nur fuer den Zweck benutzt,  den ersten  Wert  des 
Heap-Pointers aufzuheben.  Seine Definition als ^Integer (Pointer 
zu Integer) ist daher voellig willkuerlich.


   program   Jobs;
   type
      PersonPointer  = ^PersonRecord;

      PersonRecord   = record
                          Name  : String[50];
                          Job   : String[50];
                          Next  : PersonPointer;
                       end;

   var
      HeapTop  : ^Integer;
      FirstPerson,LastPerson,NewPerson : PersonPointer;
      Name  : String[50];

   begin
      FirstPerson := nil;
      Mark(HeapTop);
      repeat
         Write('Enter Name:   ');
         Readln(Name);
         if Name <> '' then
         begin
            New(NewPerson);
            NewPerson^.Name := Name;
            Write('Enter profession:  ');
            Readln(NewPerson^.Job);
            Writeln;
            if FirstPerson = nil then
               FirstPerson := NewPerson
            else
               LastPerson^.Next := NewPerson;
            LastPerson := NewPerson;
            LastPerson^.Next := nil;
         end;
      until Name = '';
      Writeln;
      while FirstPerson <> nil do
      begin
         Writeln(FirstPerson^.Name,' is a ',FirstPerson^.Job);
         FirstPerson := FirstPerson^.Next;
      end;
      Release(HeapTop);
   end.

.pa

15.5 Speicherplatzzuordnung

Mit  der  Standardprozedur GetMem ist es  moeglich  Speicherplatz 
beliebiger  Groesse  aus dem Heap zuzuweisen.  Waehrend  New  nur 
soviel Speicherplatz zuweist, wie der Typ der im Argument verwen-
deten Pointervariablen benoetigt,  erlaubt GetMem dem Programmie-
rer  die  Groesse des zugewiesenen Speicherplatzes selbst zu  be-
stimmen. GetMem wird mit zwei Parametern aufgerufen:
Syntax:
               GetMem(Pvar,I);
wobei  Pvar irgendeine Pointervariable und I  in  Integerausdruck 
ist, der die Anzahl der zugewiesenen Bytes angibt.


.pa


16. Prozeduren und Funktionen

Ein  Pascal-Programm  besteht aus einem oder  mehreren  Bloecken. 
Jeder  von ihnen kann wiederum aus Bloecken  bestehen  etc.  Eine 
Prozedur,  wie eine Funktion,  ist einer von diesen Bloecken.  Im 
allgemeinen kann man sie als Unterprogramme auffassen.  Eine Pro-
zedur  ist  also  ein separater Teil eines  Programmes,  das  von 
irgendwelchen  anderen Stellen im Programm aus durch eine  Proze-
duranweisung  aktiviert wird (s.7.1.2).  Eine Funktion  ist  ganz 
aehnlich,  nur  berechnet  sie etwas und gibt diesen  berechneten 
Wert durch eine Variable zurueck,  wenn ihr Bezeichner (der Funk-
tionsaufruf) waehrend der Ausfuehrung des Programmes benutzt wird 
(s.6.2).


16.1 Parameter

Werte  koennen  den Prozeduren oder  Funktionen  durch  Parameter 
uebergeben werden.  Durch diese Parameter wird ein Substitutions-
mechanismus bereitgestellt,  der erlaubt, die Logik des Unterpro-
grammes  mit  verschiedenen Anfangswerten zuversehen,  sodass  es 
entsprechend verschiedene Ergebnisse produziert.
Die Prozeduranweisung oder der Funktionsbezeichner,  die das ent-
sprechende Unterprogramm aufrufen, koennen eine Liste von Parame-
tern  enthalten,  die man als die aktuellen Parameter bezeichnet. 
Diese werden den formalen Paramtern uebergeben,  die im Kopf  des 
Unterprogrammes  spezifiziert sind.  Die Zuordnung der  Parameter 
bei  der Uebergabe erfolgt in der Reihenfolge ihres Auftretens in 
der  Parameterliste.  Pascal unterstuetzt  zwei  unterschiedliche 
Methoden  der Parameteruebergabe:  Uebergabe der Parameter  durch 
Uebergabe  eines Wertes (Wertuebergabe) und Uebergabe der Parame-
ter  durch Austausch der formalen Parameter durch  die  aktuellen 
Parameter (Referenz).

Werden  Parameter  durch Wertuebergabe  uebertragen,  stellt  der 
formale Parameter eine selbstaendige logische Variable im  Unter-
programm dar und alle Wertveraenderungen an diesem formalen Para-
meter  im  Unterprogramm haben keinen Einfluss auf den  Wert  des 
aktuellen  Parameters nach Ausfuehrung des  Unterprogrammes.  Der 
aktuelle  Parameter  kann  ein  beliebiger  Ausdruck,  auch  eine 
Variable,  sein, muss aber den gleichen Typ wie der formale Para-
meter haben.  Solche Parameter werden als Wert-Parameter bezeich-
net und im Kopf des Unterprogrammes,  wie im folgenden  Beispiel, 
definiert. (Die folgenden Beispiele zeigen, dass die Prozedurver-
einbarungen gegenueber den Funktionsvereinbarungen, die in 16.3.1 
erklaert  sind,  ganz aehnlich sind,  der Funktionskopf definiert 
nur noch zusaetzlich den Typ des Ergebnisses).

Beispiel Wertparameter:

          procedure Example(Num1,Num2:Number; Str1,Str2:Txt);

Hierbei  sind Number und Txt vordeklarierte Typen  (z.B.  Integer 
und  String[255]) und Num1 ,  Num2,  Str1 und Str2  sind  formale 
Parameter,  denen  die  Werte der aktuellen Parameter  uebergeben 
werden.  Die  Typen der formalen und aktuellen Parameter  muessen 
uebereinstimmen. 
.pa

Bei der Definition  des Kopfes ist zu beachten,  dass der Typ der 
Parameter  durch einen Bezeichner eines vordeklarierten Typs spe-
zifiziert  werden  muss.  Die folgende Konstruktion  ist  deshalb 
nicht erlaubt:

          procedure Select(Model: array[1..500] of Integer);

Es  ist  notwendig  den Typ von Model in  der  Typdefinition  des 
Blockes  zu erklaeren.  Dann kann der Bezeichner des Typs in  der 
Parametererklaerung des Kopfes verwendet werden:

          type
               Range = array[1..500] of Integer;

          procedure Select(Model: Range);

Wird  ein Parameter durch Referenz ubergeben,  stellt in der  Tat 
der  aktuelle  Parameter  den  formalen  Parameter  waehrend  der 
Ausfuehrung  des  Unterprogrammes  dar.   Jede  im  Unterprogramm       
vorgesehene  Veraenderung  des Inhaltes des  formalen  Parameters 
aendert   waehrend   der  Ausfuehrung  des   Unterprogrammes   in 
Wirklichkeit  den  Inhalt des aktuellen  Parameters.  Aus  diesem 
Grunde  muss  der aktuelle Parameter stets  eine  Variable  sein. 
Solche   durch   Referenz  uebergebenen  Parameter   werden   als 
Variablen-Parameter  bezeichnet.  Die  Definition erfolgt wie  im 
folgenden Beispiel:

Beispiel Variablenparameter:

          procedure Example(var Num1,Num2:Number; Str1,Str2:Txt);

Hierbei  sind  Num1 und Num2 Variablenparameter und Str1 und Str2 
Wertparameter. 
Alle  Adressrechnungen werden erst zur Zeit des  Prozeduraufrufes 
ausgefuehrt.  Ist  eine  Variable beispielsweise  die  Komponente 
eines Array, wird ihr Index erst zum Zeitpunkt des Unterprogramm-
aufrufes berechnet.

Beachte,  File-Parameter  muessen  immer  als  Variablenparameter 
erklaert werden.

Wird  eine grosse Datenstruktur,  wie ein Array,  einem Unterpro-
gramm als Parameter uebergeben,  dann spart die Verwendung  eines 
Variablenparameters  Abarbeitungszeit und Speicherplatz,  da  als 
einzige  Information  zwei Bytes an das Unterprogramm  uebergeben 
werden,  die die Adresse des aktuellen Parameters enthalten.  Ein  
Wertparameter  wuerde  Speicherplatz fuer eine  extra  Kopie  der 
gesamten  Datenstruktur  verlangen  und ausserdem Zeit  fuer  das 
Kopieren benoetigen.


16.1.1 Reduzierung der Parametertyppruefung

Normalerweise  muessen bei Verwendung von Variablenparametern die 
formalen  und  aktuellen Parameter exakt vom gleichen  Typ  sein. 
Dies bedeutet, wenn ein Variablenparameter vom Typ String verwen-
det  wird,  duerfen als aktuelle Parameter nur Strings mit  exakt 
der im Unterprogramm definierten Laenge zugewiesen werden.  Diese 
Einschraenkung kann man durch Verwendung der V-Compiler-Direktive 
beseitigen.  Fuer  diese  Direktive erzeugt  der  Standard-aktiv-
Status {$V+} eine genaue Typpruefung, waehrend der passive Status 
{$V-} die Typpruefung soweit vermindert,  dass aktuelle Parameter 
mit  beliebiger  Stringlaenge,  unabhaengig von  der  definierten 
Laenge des formalen Parameters, uebertragen werden duerfen.
.pa


Beispiel:

     program NSA;
     {This program must be compiled with the $V-Directive}
     {$V-}
     type
          WorkString = string[255];

     var
          Line1 : string[80];
          Line2 : string[100];

     procedure     Encode(var LineToEncode : WorkString);
     var I : Integer;
     begin
          for I := 1 to Length(LineToEncode) do
          LineToEncode[I] := Chr(Ord(LineToEncode[i]) - 30);
     end;
     begin
          Line1 := 'This is s secret message';
          Encode(Line1);
          Line2 := 'Here is another (longer) secret message';
          Encode(Line2);
     end.



16.1.2 Nichttypisierte Variablenparameter

Ist  der  Typ eines formalen  Parameters  nicht  definiert.  d.h. 
enthaelt  der  Parameterteil  im Kopf des  Unterprogrammes  keine 
Typdefinition,  dann  wird der Parameter als  nichttypisiert  be-
zeichnet.  Der  aktuelle  Parameter kann dann ein beliebiger  Typ 
sein.  Andererseits  ist der nichttypisierte Parameter mit  allen 
Typen  inkompatibel.  Aus diesem Grunde kann man  nichttypisierte 
formale Parameter nur dort verwenden, wo der Datentyp ohne Bedeu-
tung  ist.  Dies ist beispielsweise bei den Parametern von  Addr, 
BlockRead,   BlockWrite,  FillChar  oder  Move  und  bei  Adress-
spezifikationen von absoluten Variablen der Fall.

Im folgenden Beispiel wird bei der Prozedur SwitchVar die Verwen-
dung nichttypisierter Parameter demonstriert. Sie uebertraegt den 
Inhalt der Variablen A1 nach A2 und von A2 nach A1.

     procedure SwitchVar(var A1p,A2p; Size : Integer);
     type
          A = array[1..MaxInt] of Byte;
     var
          A1    : A absolute A1p;
          A2    : A absolute A2p;
          Tmp   : Byte;
          Count : Integer;
     begin
          for Count := 1 to Size do
          begin
               Tmp       := A1[Count];
               A1[Count] := A2[Count];
               A2[Count] := Tmp;
          end;
     end;

.pa


Definiert man:

     type
          Matrix = array[1..50,1..25] of Real;    
     var
          TestMatrix,BestMatrix : Matrix;

dann  kann man SwitchVar zum Austauschen des Inhaltes der  beiden 
Matrizen  verwenden. Der Prozeduraufruf lautet dann:

     SwitchVar(TestMatrix,BestMatrix,SizeOf(TestMatrix));


16.2 Prozeduren

Eine   Prozedur  kann  entweder  vordeklariert  (Standard)   oder 
nutzerdeklariert   (durch  den  Programmierer  vereinbart)  sein. 
Vordeklarierte Proceduren sind Teile des TURBO-Pascal-Systems und 
koennen  ohne  weitere  Vereinbarungen  verwendet  werden.  Einer 
nutzerdeklarierten Prozedur kann der Name einer  Standardprozedur 
gegeben werden,  aber dann ist die Standardprozedur innerhalb des 
Gueltigkeitsbereiches   der  nutzerdeklarierten  Prozedur   nicht 
aufrufbar.


16.2.1 Prozedurvereinbarung

Eine  Prozedurvereinbarung  besteht  aus einem  Prozedurkopf  und 
einem  ihm  folgenden  Block.  Dieser  Block  besteht  aus  einem 
Deklarationsteil und einem Anweisungsteil.

Der Prozedurkopf besteht aus dem reservierten Wort procedure, dem 
ein Bezeichner folgt,  der als Name der Prozedur bezeichnet wird. 
Gewoehnlich folgt ihm eine formale Parameterliste,  wie in  16.1. 
beschrieben.

Beispiele:
          procedure LogOn;
          procedure Position(X,Y : Integer);
          procedure Compute(var Data : Matrix; Scale : Real);

Der  Deklarationsteil einer Prozedur hat die gleiche Form wie bei 
einem   Programm.   Alle  in  der  formalen   Parameterliste   im 
Deklarationsteil  erklaerten  Bezeichner sind lokal zur  Prozedur 
und   zu   jeder   Prozedur  in   ihr.   Dieser   Bereich   heisst 
Gueltigkeitsbereich der Bezeichner.  Ausserhalb dieses  Bereiches 
sind  sie nicht bekannt.  Eine Prozedur kann jede in einem zu ihr 
äusseren Block definierte  Konstante,  Type,  Variable,  Prozedur 
oder Funktion verwenden. 

Der  Anweisungteil  spezifiziert die  Aktionen,  die  ausgefuehrt 
werden sollen, wenn die Prozedur aufgerufen wird. Er hat die Form 
einer  Verbundanweisung (s.7.2.1).  Wird  der  Prozedurbezeichner 
selbst innerhalb des Anweisungsteiles verwendet,  wird die Proze-
dur  rekursiv ausgefuehrt (bei CP/M80 muss dann beachtet  werden, 
dass  zu  diesem Zeitpunkt bei der Compilierung  die  A-Compiler-
Direktive passiv {$A-} gesetzt ist s.Anhang E).

.pa

Das  naechste  Beispiel  zeigt ein Programm,  das  eine  Prozedur 
verwendet  und  dieser  Prozedur  Parameter  uebergibt.   Da  die 
aktuellen  Parameter,  die  an die  Prozedur  uebergeben  werden, 
Konstante  (oder  einfache  Ausdruecke) sind,  muss  der  formale 
Parameter als Wertparameter definiert werden:


     program Box;
     var
          I :Integer;
     procedure DrawBox(X1,Y1,X2,Y2 : Integer);
          var  I : Integer;
          begin
               GotoXY(X1,Y1);
               for I := X1 to X2 do Write('-');
               for I := Y1+1 to Y2 do
               begin
                    GotoXY(X1,I); Write('!');
                    GotoXY(X2,I); Write('!');
               end;
               GotoXY(X1,Y2);
               for I := X1 to X2 do Write('-');
          end;
     begin
          ClrScr;
          for I := 1 to 5 do DrawBox(I*4,I*2,10*I,4*I);
          DrawBox(1,1,80,23);
     end.


Häufig  sollen Veraenderungen des formalen Parameters in der Pro-
zedur  direkt auch den aktuellen Parameter betreffen.  In  diesen 
Faellen  sind natuerlich Variablenparameter  anzuwenden,  wie  im 
folgenden Beispiel:

     procedure Switch(var A,B : Integer);
     var Tmp : Integer;
     begin
          Tmp := A; A := B; B := Tmp;
     end;

Wenn diese Prozedur durch die Anweisung Switch( I,J ); aufgerufen 
wird,   werden  die  Werte  von  I  und  J  ausgetauscht.  Wuerde 
stattdessen faelschlicherweise der Prozedurkopf durch

     procedure Swap( A,B : Integer);

d.h.  mit  einem  Wertparameter erklaert,  dann werden durch  die 
Anweisung Swap( I,J ); die Werte von I und J nicht veraendert.


.pa

16.2.2 Standardprozeduren

TURBO-Pascal enthaelt eine Anzahl von Standardprozeduren:

     1) Stringbehandlungsprozeduren (s. 9.5),
     2) Filebehandlungsprozeduren (s. 14.2, 14.5.1, 14.7.1),
     3) Zuordnungsprozeduren fuer dynamische Variable (s. 15.2, 15.5) 
     4) Input- und Output-Prozeduren (s. 14.6).

Zusaetzlich  werden  die folgenden  Standardprozeduren  bereitge-
stellt,  vorausgesetzt, die entsprechenden Terminalkommandos sind 
installiert.


16.2.2.1 Delay
Syntax:   Delay(Time)

Diese  Prozedur erzeugt eine Warteschleife,  die in ungefaehr so-
viel  Millisekunden durchlaufen wird,  wie im Argument  angegeben 
ist.  Die  exakte Zeit kann wegen der  unteschiedlichen  Hardware 
etwas davon abweichen.

16.2.2.2 ClrEol
Syntax:   ClrEol

Diese  Prozedur  loescht alle Zeichen ab Cursorposition  bis  zum 
Ende der Zeile, ohne die Cursorposition zu veraendern.

16.2.2.3 ClrScr
Syntax:   ClrScr

Diese Prozedur loescht den Bildschirm und setzt den Cursor in die 
linke obere Ecke. (Bei einigen Bildschirmtypen koennen dabei auch 
eventuell  vorhandene  Videoattribute bzw.  vom  Nutzer  gesetzte 
Attribute veraendert werden).

16.2.2.4 DelLine
Syntax:   DelLine

Diese  Prozedur loescht die Zeile,  in der der Cursor  steht  und 
schiebt alle darunter stehenden Zeilen um eine Zeile nach oben.

16.2.2.5 InsLine
Syntax:   InsLine

Diese  Prozedur fuegt an der Cursorposition eine leere Zeile  ein 
und schiebt alle Zeilen unterhalb um eine Zeile nach  unten,  die 
letzte Zeile wird weggerollt.

16.2.2.6 Init
Syntax:   Init

Diese Prozedur sendet die  Terminal-Initialisierungszeichenkette, 
die  bei der Installierung von TURBO-Pascal definiert  wurde,  an 
den Bildschirm.

16.2.2.7 Exit
Syntax:   Exit

Diese  Prozedur sendet die Terminal-Reset-Zeichenkette,  die  bei 
der Installierung definiert wurde, an den Bildschirm.
.pa


16.2.2.8 GotoXY
Syntax:   GotoXY(Xpos,Ypos)

Diese  Prozedur  setzt den Cursor an die Position auf  dem  Bild-
schirm,  die durch die Integerausdruecke Xpos (horizontaler  Wert 
oder  Zeile)  und  Ypos (vertikaler Wert oder  Spalte)  angegeben 
werden. Die linke obere Ecke (Home-Position) ist (1,1).

16.2.2.9 LowVideo
Syntax:   LowVideo

Diese  Prozedur  setzt im Bildschirm das Attribut,  das  bei  der 
Installation al "Ende der Hellsteuerung" festgelegt wurde.

16.2.2.10 HighVideo
Syntax:   HighVideo

Diese  Prozedur  setzt im Bildschirm das Attribut,  das  bei  der 
Installation als "Hellsteuerung" definiert wurde.

16.2.2.11 Randomize
Syntax:   Randomize

Diese Prozedur erzeugt mittels Zufallszahlgenerator eine Zufalls-
zahl.

16.2.2.12 Move
Syntax:   Move(var1,var2,num)

Diese  Prozedur  kopiert im Speicher eine  bestimmte  Anzahl  von 
Bytes.  Hierbei  sind var1 und var2 zwei Variable von  beliebigem 
Typ  und num ist ein Integerausdruck.  Die Prozedur kopiert einen 
Block von num Bytes von der Stelle des ersten Bytes von var1  zur 
Stelle des ersten Bytes von var2.  Move behandelt automatisch bei 
der  Uebertragung auftretende Ueberlappungen,  sodass "moveright" 
und "moveleft" Prozeduren nicht benoetigt werden.

16.2.2.13 FillChar
Syntax:   FillChar(var,num,value)

Diese Prozedur fuellt einen Bereich im Speicher mit eine, gegebe-
nen  Wert.  Hierbei ist var eine Variable eines beliebigen  Typs, 
num  ist ein Integerausdruck und Value ist ein Ausdruck  vom  Typ 
Byte oder Char.  Es werden durch die Prozedur num Bytes beginnend 
ab dem ersten Byte von var mit dem Wert von value gefuellt.
.pa



16.3 Funktionen

Funktionen   sind   wie  Prozeduren   entweder   (vordeklarierte) 
Standardfunktionen oder sie sind vom Programmierer definiert.


16.3.1 Funktionsvereinbarung

Eine  Funktionsvereinbarung besteht aus einem  Funktionskopf  und 
einem Block, der aus einem Deklarationsteil und einem Anweisungs-
teil besteht.

Der  Funktionskopf  ist mit dem Prozedurkopf  equivalent,  ausser 
dass der Funktionskopf mit dem reservierten Wort function eroeff-
net  wird und auch den Typ des Ergebnisses mit  definieren  muss. 
Dies  wird durch Anfuegung eines Doppelpunktes und eines Types an 
den Funktionskopf erreicht.

Beispiele:
     funktion KeyHit : Boolean;
     function Comput(var Value : Sample) : Real;
     function Power(X,Y : Real) : Real;

Der Ergebnistyp einer Funktion muss ein Skalartyp (d.h.  Integer, 
Real, Boolean, Char deklariert als Skalar- oder Teilbereich), ein 
Stringtyp oder ein Pointertyp sein.

Der Deklarationsteil einer Funktion ist der gleiche wie bei einer 
Prozedur.

Der Anweisungsteil einer Funktion ist eine  Verbundanweisung,  so 
wie  in  7.2.1 beschrieben.  Innerhalb des Anweisungsteiles  muss 
wenigstens eine Ergibtanweisung auftreten,  die dem  Funktionsbe-
zeichner einen Wert zuweist.  Die allerletzte dieser Ergibtanwei-
sungen zum Funktionbezeichner ergibt den Wert der Funktion.  Wenn 
der Funktionsbezeichner selbst als Funktionsaufruf im Anweisungs-
teil  der  Funktion  auftritt,  dann wird die  Funktion  rekursiv 
aufgerufen.  In  diesem  Falle muss zu diesem  Zeitpunkt  die  A-
Compiler-Direktive {$A-} passiv sein (s. Anhang E).

Das  folgende  Beispiel zeigt die Verwendung einer  Funktion  zur 
Berechnung der Summe eine Reihe ganzer Zahlen von I bis J:


     function RowSum(I,J : Integer) : Integer;
          function SimpleRowSum(S : Integer) : Integer;
          begin
             SimpleRowSum := S * (S+1) div 2;
          end;
     begin
          RowSum := SimpleRowSum(J) - SimpleRowSum(I-1);
     end;

Die Funktion SimpleRowSum ist in die Funktion RowSum eingebettet. 
SimpleRowSum  ist  deshalb nur im Gueltigkeitsbereich von  RowSum 
zulaessig.
.pa


Das folgende Beispiel zeigt die Verwendung rekursiver Funktionen:

     program Fact;
     var number : Integer;
     function factorial(value : Integer) : Real;
     begin
        if value = 0 then factorial := 1
        else factorial := value * factorial(value-1);
     end;
     begin
        Write('input number: ');
        Readln(number);
        if number < 0 then Writeln('non valid input!')
        else Writeln(number,'! = ',factorial(number):8:0);
     end.

Bei  der Definition eines Funktiontyps ist zu beachten,  dass der 
in  der  Definition  verwendete  Typ  vorher  als   Typbezeichner 
erklaert   sein  muss.   Aus  diesem  Grunde  ist  die   folgende 
Konstruktion nicht erlaubt:

     function LowCase(Line : UserLine) : string[80];

Man  muss  stattdessen  vorher  den Typ  string[80]  durch  einen 
Bezeichner   erklaeren   und  mit  diesem  dann   den   Typ   des 
Funktionsergebnisses definieren:

     type
        str80 = string[80];
     function LowCase(Line : UserLine) : str80;

Wegen der Art der Implementation der Prozeduren Write und Writeln 
darf  eine Funktion,  die irgendeine der Standardprozeduren Read, 
Readln,   Write  oder  Writeln  verwendet,  niemals  durch  einen 
Ausdruck in einer Write oder Writeln Anweisung aufgerufen werden.


16.3.2 Standardfunktionen

Die folgenden (vordeklarierten) Standardfunktionen sind in TURBO-
Pascal implementiert:
     
     1) Stringbehandlungsfunktionen (s. 9.5),
     2) Filebehandlungsfunktionen (s. 14.2, 14.5.1),
     3) Pointerbezogene Funktionen )s. 15.2, 15.3, 15.5)

und darueber hinaus folgende Funktionen:


16.3.2.1 Arithmetische Funktionen

16.3.2.1.1 Abs
Syntax:        Abs(num)

Gibt  den absoluten Wert von num zurueck.  Das Argument num  muss 
entweder Real oder Integer sein und das Ergebnis ist vom gleichen 
Typ, wie das Argument.


16.3.2.1.2 Arctan
Syntax:        Arctan(num)

Gibt den Winkel im Bogenmass zurueck,  dessen Tangens gleich  num 
ist.  Das Argument num muss entweder Real oder Integer sein,  das 
Ergebnis ist Real.



16.3.2.1.3 Cos
Syntax:        Cos(num)

Gibt den Cosinus von num zurueck. Das Argument num wird im Bogen-
mass  ausgedrueckt und muss entweder Real oder Integer sein.  Das 
Ergebnis ist Real.

16.3.2.1.4 Exp
Syntax:        Exp(num)

Gibt  die Exponentialfunktion von  num  zurueck,  d.h.  e^x.  Das 
Argument  num muss entweder Real oder Integer sein.  Das Ergebnis 
ist Real.

16.3.2.1.5 Frac
Syntax:        Frac(num)

Gibt den gebrochenen Teil von num zurueck, d.h. Frac(num) = num - 
Int(num).  Das Argument num muss entweder Real oder Integer sein. 
Das Ergebnis ist Real.

16.3.2.1.6 Int
Syntax:        Int(num)

Gibt  den ganzen Teil von num zurueck,  d.h.  die groesste  ganze 
Zahl,  die kleiner oder gleich num ist,  fall num >= 0 ist,  oder 
die kleinste ganze Zahl,  die groesser oder gleich num ist, falls 
num  < 0 ist .  Das Argument num muss entweder Real oder  Integer 
sein. Das Ergebnis ist Real.

16.3.2.1.7 Ln
Syntax:        Ln(num)

Gibt den natuerlichen Logarithmus von num zurueck.  Das  Argument 
num muss entweder Real oder Integer sein. Das Ergebnis ist Real.

16.3.2.1.8 Sin
Syntax:        Sin(num)

Gibt  den Sinus von num zurueck.  Das Argument num muss im Bogen-
mass ausgedrueckt sein und sein Typ ist entweder Real oder  Inte-
ger. Das Ergebnis ist Real.

16.3.2.1.9 Sqr
Syntax:        Sqr(num)

Gibt das Quadrat von num zurueck,  d.h. num*num. Das Argument num 
muss entweder Real oder Integer sein.  Das Ergebnis ist vom glei-
chen Typ wie das Argument.

16.3.2.1.10 Sqrt
Syntax:        Sqrt(num)

Gibt  die  Quadratwurzel von num zurueck.  Das Argument num  muss 
entweder Real oder Integer sein. Das Ergebnis ist Real.

.pa


16.3.2.2 Skalarfunktionen

16.3.2.2.1 Pred
Syntax:        Pred(num)

Gibt den Vorgaenger von num zurueck (falls er existiert). num ist 
ein beliebiger Skalartyp.


16.3.2.2.2 Suc
Syntax:        Suc(num)

Gibt den Nacgfolger von num zurueck (falls er existiert). num ist 
ein beliebiger Skalartyp.


16.3.2.2.3 Odd
Syntax:        Odd(num)

Gibt  den  Booleschen Wert True zurueck,  wenn num eine  ungerade 
Zahl ist und False,  wenn num eine gerade Zahl ist.  num muss vom 
Typ Integer sein.



16.3.2.3 Konvertierungsfunktionen

Die Konvertierungsfunktionen werden verwendet,  um den Wert eines 
Skalartyps in den eines anderen Skalartyps umzuwandeln.  Zusaetz-
lich zu den folgenden Funktionen kann man "Retyping" (s.8.3) fuer 
diese Zwecke verwenden.


16.3.2.3.1 Ord
Syntax:        Ord(num)

Gibt die Ordnungszahl des Wertes von num in der durch den Typ von 
num  definierten  Menge zurueck.  Ord(num) ist  mit  Integer(num) 
equivalent.  num kann ein beliebiger Skalartyp sein. Das Ergebnis 
ist vom Typ Integer.,


16.3.2.3.2 Round
Syntax:        Round(num)

Gibt  den  Wert von num gerundet zur naechsten  ganzen  Zahl  wie 
folgt zurueck:
     wenn num >= 0, dann ist Round(num) = Trunc(num+0.5),
     wenn num < 0 , dann ist Round(num) = Trunc(num-0.5).
num muss vom Typ Real sein. Das Ergebnis ist vom Typ Integer.


16.3.2.3.3 Trunc
Syntax:        Trunc(num)

Gibt  fuer num >= 0 die groesste ganze Zahl zurueck,  die kleiner 
oder gleich num ist.  Wenn num < 0 ist,  dann gibt diese Funktion 
die  kleinste ganze Zahl zurueck,  die groesser oder  gleich  num 
ist.  num  muss  vom Typ Real sein und das Ergebnis ist  vom  Typ 
Integer.
.pa
 

16.3.2.4 Verschiedene Standarfunktionen


16.3.2.4.1 Hi
Syntax:        Hi(I)

Das niederwertige Byte des Ergebnisses enthaelt das hoeherwertige 
Byte des Wertes vom Integerausdruckes I.  Das hoeherwertige  Byte 
des Ergebnisses ist Null. Das Ergebnis ist vom Typ Integer.


16.3.2.4.2 KeyPressed
Syntax:        Keypressed

Gibt  den  Wert  True zurueck,  wenn eine Taste auf  der  Console 
gedrueckt  wurde.  Das  Ergebnis wird durch  Aufruf  der  Consol-
Status-Routine des BIOS realisiert.


16.3.2.4.3 Lo
Syntax:        Lo(I)

Gibt  das  niederwertige  Byte des Wertes vom  Integerausdruck  I 
zurueck,  wobei das hoeherwertige Byte auf Null gesetzt wird. Der 
Typ des Ergebnisses ist Integer.


16.3.2.4.4 Random
Syntax:        Random

Gibt eine Zufallszahl zurueck,  die groesser oder gleich Null und 
kleiner als Eins ist. Der Typ ist Real.


16.3.2.4.5 Random(I)
Syntax:        Random(num)

Gibt eine Zufallszahl zurueck,  die groesser oder gleich Null und 
kleiner  als num ist.  num und die Zufallszahl sind beide vom Typ 
Integer.


16.3.2.4.6 SizeOf
Syntax:        SizeOf(name)

Gibt die Anzahl von Bytes zurueck, die von der Variablen oder dem 
Typ name belegt werden. Das Ergebnis ist vom Typ Integer.


16.3.2.4.7 Swap
Syntax:        Swap(I)

Die Swapfunktion vertauscht vom Wert des Integerausdruckes I  das 
hoeher- und niederwertige Byte und gibt das Ergebnis als Integer-
zahl  aus.  Beispiel  Swap($1234) gibt $3412 zurueck  (Werte  zur 
Verdeutlichung in hexadezimaler Schreibweise ).

.pa


16.4 Vorwaerts Referenz

Ein  Unterprogramm  wird vorwaerts deklariert,  indem man  seinen 
Kopf  separat  von seinem  Block  spezifiziert.  Dieser  separate 
Unterprogrammkopf  ist exakt der gleiche,  wie der eines normalen 
Unterprogrammes, ausser dass er mit dem reservierten Wort forward 
endet.  Der  Block  selbst folgt spaeter innerhalb  des  gleichen 
Deklarationsteiles.  Der Block beginnt mit einer Kopie des vorher 
definierten Kopfes ohne Parameter,  Typen etc,  d.h.  nur mit dem 
Namen.

Beispiel:

     program Catch22;
     var
        X : Integer;
     function Up(var I : Integer) : Integer; forward;
     function Down(var I : Integer) : Integer;
     begin
        I := I div 2; Writeln(I);
        if I <> 1 then I := Up(I);
     end;
     function Up;
     begin
        while I mod 2 <> 0 do
        begin
           i := I*3+1; Writeln(I);
        end;
        I := Down(I);
     end;
     begin
        Write('Enter any integer: ');
        Readln(X);
        X := Up(X);
        Write('Ok Program stopped again.');
     end.

Wird  das Programm gestartet und eine 6 eingegeben,  dann  ergibt 
sich folgendes Bild:

     Enter any integer : 6
     3
     10
     5
     16
     8    
     4
     2
     1
     Ok Program stopped again.

.pa


Das  obige Programm ist eine kompliziertere Version des folgenden 
Programmes:

     program Catch222;
     var
        X : Integer;
     begin
        Write('Enter any integer: ');
        Readln(X);
        while X <> 1 do
        begin
           if X mod 2 = 0 then X := X div 2
                          else X := X*3+1;
           Writeln(X);
        end;
        Write('Ok Program stopped again');
     end.
 

Sie sind sicher ueberrascht, dass man bei diesem kleinen und sehr 
einfachen Progamm im Voraus nicht einschaetzen kann, wie lange es 
bei Eingabe einer beliebigen ganzen Zahl läuft.

.pa


17. Einfuegen von Programmteilen

Die  Tatsache,  dass der TURBO-Editor den gesamten  Quelltext  im 
Speicher  bearbeitet,  schraenkt die Groesse des Quelltextes ein. 
Diese  Beschraenkung  kann man durch Verwendung  der  I-Compiler-
Direktive  umgehen.  Dazu  teilt man den  gesamten  Quelltext  in 
kleinere Einheiten.  Eine Einheit, das Rahmenprogramm, bildet den 
Kern  des  Programmes und in diese werden die anderen  Teile  zur 
Zeit  der  Uebersetzung mittels I-Compiler-Direktive  eingefuegt. 
Diese  Include-Option gestattet ein uebersichtliches  Programmie-
ren.  Es eroeffnet auch die Moeglichkeit der Verwendung einzelner 
Programmteile  in anderen Programmen und damit kann man  Bibilio-
theken von Dateien schaffen,  die fuer den flexiblen Aufbau  ver-
schiedener Programme zur Verfuegung stehen.

Die Syntax fuer die I-Compiler-Direktive ist

               {$I filename}

wobei filename ein beliebiger erlaubter kompletter Dateiname ist. 
Blanks  werden ignoriert und kleine Buchstaben in grosse umgewan-
delt.  Wurde kein Typ spezifiziert,  wird .PAS angehaengt.  Diese 
Direktive  muss allein in einer Zeile des Rahmenprogrammes  defi-
niert werden.
Beispiele:
          {$I first.pas}
          {$I STDPROC}
          {$I COMPUTE.MOD}

Zur  Demonstration der Include-Option nehmen wir an,  in  unserer 
Bibliothek  exisitiere die Datei STUPCASE.FUN.  Sie enthalte  die 
Funktion  StUpCase,   die  mit  einem  Zeichen  oder  String  als 
Parameter  aufgerufen wird und die den Wert des Parameters  unter 
Umwandlung aller Kleinbuchstaben in Grossbuchstaben zurueckgibt.

Datei STUPCASE.FUN:

     function StUpCase(St : ALlStrings) : AllStrings;
        var I : Integer;
        begin
           for I := 1 to Length(St) do
           St[I] := UpCase(St[I]);
           StUpCase := St;
        end;

In  einem  anderen  Programm  kann man dann  diese  Funktion  zur 
Umwandlung  von Klein- in Grossbuchstaben  verwenden,  indem  man 
diese Datei mittels  der Include-Option einfuegt,  anstatt sie in 
das Programm einzukopieren:

     program STUPCASE;
     type
        InData     = string[80];
        AllStrings = string[255];
     var
        Answer : InData;
        I : Integer;
     {$I F:STUPCASE.FUN}
     begin
        Writeln('Enter Name: ');
        Readln(Answer);
        Writeln(StUpCase(Answer));
     end.
.pa


Diese  Methode ist nicht nur einfacher und Speicherplatz  sparen-
der,  sie  ermoeglicht auch einen sicheren und  einfachen  Aende-
rungsdienst.  Eine  Verbesserung  in einer solchen Routine  wirkt 
dann  sofort automatisch auf alle Programme,  die  diese  Routine 
mittels Include einfuegen.

Man  sollte auch beachten,  dass TURBO-Pascal fuer die  einzelnen 
Bestandteile  des Deklarationsteiles keine feste Ordnung  fordert 
und  diese  auch mehrfach auftreten koennen.  Damit  besteht  die 
Moeglichkeit,  bestimmte  häufig  verwendete  Typdefinitionen  in 
einer  Bibliothek aufzunehmen und sie von dort aus in die einzel-
nen Programme einzufuegen.

Alle Compiler-Direktiven, ausser B und C, sind lokal zu dem File, 
in dem sie auftreten. Das heisst, wenn eine Compiler-Direktive in 
einem  Include-File  veraendert wird,  wird  sie  nach  Verlassen 
dieses Include-Files auf den urspruenglichen Wert zurueckgesetzt. 
Die Compiler-Direktiven B und C sind immer global. Eine Beschrei-
bung aller Compiler-Direktiven steht im Anhang E.

Include-Files  koennen  nicht  geschachtelt  werden,   d.h.   ein 
Include-File  kann  immer  nur  von  einem  "Rahmenprogramm"  aus 
aufgerufen und eingefuegt werden,  niemals von einem eingefuegten 
Programm aus.