Prozeduren und Funktionen
Suche:
print pdf

BedingungenInhaltsverzeichnisZeitablauf im Spiel

MissingLink für Verweise auf noch nicht erstellte Abschnitte:

Begriffe, für die auf eine Definition verwiesen wird; Altes grün 33AA11
Die Grundlage für diese Beschreibung bildet das Tutorial von Delphi-Source. Das Kapitel Prozeduren und Funktionen wurde nur auf die Gegebenheiten von X-Skript angepasst

Unterprogramme: Prozeduren und Funktionen


Prozeduren und Funktionen, auch "Unterprogramme" oder Routinen genannt, haben die Aufgabe, öfter wiederkehrenden Programmcode sozusagen als Baustein zusammenzufassen. Dieser Baustein erhält einen eindeutigen Namen, über den er ausgeführt werden kann.

Aufbau einer Prozedur

Jede Prozedur besteht aus dem Schlüsselwort procedure, gefolgt von einem gültigen Namen und evtl. einer Parameterliste in runden Klammern. Sind keine Parameter vorhanden, werden die Klammern sowohl bei der Deklaration als auch beim Aufruf weggelassen. Diesen Teil nennt man Kopf der Prozedur. Es folgen Variablen- und Konstantendeklarationen und anschließend zwischen begin und end die Anweisungen, die die Prozedur durchführen soll:

markieren
1
2
3
4
5
procedure <Name>(<Parameter>);
<Variablen- und Konstanten>
begin
  <Anweisungen>
end;

Beispiel: Die folgende Prozedur gibt so viele Töne über den PC-Lautsprecher aus, wie über den Parameter Anzahl angegeben.

markieren
1
2
3
4
5
6
7
procedure Toene(Anzahl: integer);
var 
  i: integer;
begin
  for i := 1 to Anzahl do
    beep;
end;
Der Aufruf für fünf Töne geschieht so:

markieren
1
Toene(5);

Aufbau einer Funktion

Eine Funktion unterscheidet sich nur geringfügig von einer Prozedur. Sie besitzt einen Rückgabewert und wird mit dem Schlüsselwort function deklariert anstelle von procedure.

markieren
1
2
3
4
5
  function <Name>(<Parameter>): <Rückgabetyp>;
  <Variablen- und Konstanten>
  begin
    <Anweisungen>
  end;

Beispiel: Eine Funktion, die drei Zahlen addiert und das Ergebnis zurückliefert.

markieren
1
2
3
4
  function SummeAusDrei(zahl1, zahl2, zahl3: integer): integer;
  begin
    result := zahl1 + zahl2 + zahl3;
  end;

Bei result handelt es sich um eine vordefinierte Variable, der der Rückgabewert zugewiesen wird, den die Funktion haben soll. Alternativ kann dafür auch der Funktionsname verwendet werden. Mit result kann innerhalb der Funktion jedoch gerechnet werden, während der Funktionsname nur auf der linken Seite einer Zuweisung stehen darf, da ansonsten von einem rekursiven Aufruf der Funktion (Funktion ruft sich selbst auf) ausgegangen wird.

Es ist möglich, result oder dem Funktionsnamen öfters einen Wert zuzuweisen. Letztlich bildet der Wert den Rückgabewert der Funktion, der der Variablen result oder dem Funktionsnamen als letztes zugewiesen wurde.

Im Gegensatz zu return in andern Sprachen bricht result den Ablauf in einer Routine nicht an dieser Stelle ab.

Der Rückgabewert kann dann an der Aufrufstelle ausgewertet werden (ergebnis sei eine Integer-Variable):

markieren
1
ergebnis:=SummeAusDrei(3,5,9);

forward--Deklarationen

Prozeduren und Funktionen können nur aufgerufen werden, wenn ihr Name im Code bekannt ist. Und dieser Gültigkeitsbereich beginnt erst an der Stelle der Definition. Soll eine Routine bereits vorher bekannt sein, wird eine forward-Deklaration eingesetzt.

Dabei wird lediglich der Kopf einer Routine, versehen mit der Direktive forward, an eine frühere Stelle im Code gesetzt. Für die Funktion SummeAusDrei sähe das so aus:

markieren
1
function SummeAusDrei(zahl1, zahl2, zahl3: integer): integer; forward

Die eigentliche Funktion, muss dann später im Code folgen.

markieren
1
2
3
4
5
6
7
8
9
10
11
procedure AufrufendeProzedur;
var 
  ergebnis: integer;
begin
  ergebnis := SummeAusDrei(3, 5, 9);  //Aufruf der Funktion
end;

function SummeAusDrei(zahl1, zahl2, zahl3: integer): integer;
begin
  result := zahl1 + zahl2 + zahl3;
end;

Die forward-Deklaration ist nur notwendig, wenn zwei Funktionen sich gegenseitig aufrufen. Dies sollte und kann in den meisten Fällen allerdings vermieden werden durch eine klare Strukturierung.

Parameter

In den meisten Fällen wird eine Routine zwar öfters gebraucht, allerdings - z. B. bei Berechnungen - nicht immer mit den gleichen Werten. Deshalb gibt es, wie oben bereits gesehen, die Möglichkeit, Routinen Werte beim Aufruf zu übergeben.

Beim Aufruf einer Prozedur/Funktion mit Parametern muss beachtet werden, dass Anzahl und Typ der Werte übereinstimmen.

Anhand der Reihenfolge der Werte steht in obigem Beispiel fest, dass die Variable zahl1 den Wert 3, zahl2 den Wert 5 und zahl3 den Wert 9 erhält. Diese Variablen werden nicht wie üblich über var deklariert. Ihre Deklaration erfolgt durch die Nennung im Funktionskopf. Außerdem gelten sie nur innerhalb der Funktion. Von außerhalb (z. B. nach Beendigung der Funktion) kann nicht mehr auf sie zugegriffen werden.

Mehrere Parameter werden bei der Deklaration mit Semikolon getrennt und beim Aufruf mit Komma. Mehrere Parameter vom gleichen Typ können wie bei Variablen auch zusammengefasst (siehe obiges Beispiel) werden. Beispiel:

markieren
1
2
3
4
5
6
7
procedure DateiKopieren(AlteDatei, NeueDatei: String; Ueberschreiben: Boolean);
begin
  ...
end;

// Aufruf
DateiKopieren('A.bmp','Kopie.bmp',true);
Die Prozedur DateiKopieren erwartet also zwei Parameter vom Typ string und einen vom Typ Boolean.

Um das Ganze etwas komplizierter zu machen, gibt es verschiedene Arten von Parametern, die durch var, const oder out gekennzeichnet werden.

Wert- und Variablenparameter

In obigen Beispielen wird immer eine Kopie eines Wertes an die Prozedur/Funktion übergeben. Wenn dieser Wert also innerhalb der Prozedur/Funktion verändert wird, ändert sich nicht die Variable, die beim Aufruf verwendet wurde:

markieren
1
2
3
4
5
6
7
8
9
10
11
12
procedure machwas(zahl: integer);
begin
  zahl := zahl+5;
end;

procedure aufruf;
var 
  einezahl: integer;
begin
  einezahl := 5;
  machwas(einezahl);
end;
Im Beispiel ruft also die Prozedur aufruf die Prozedur machwas mit dem Wert der Variable einezahl auf. In machwas wird dieser Wert über zahl angesprochen. Und obwohl zahl nun verändert wird, ändert sich der Wert in einezahl nicht. Er ist am Ende immer noch 5. Man spricht von einem Wertparameter, es wird nur der Inhalt der Variablen übergeben.

Im Fall des Variablenparameters wird das "Original" übergeben. Ein solcher Parameter wird mit dem Schlüsselwort var gekennzeichnet.

markieren
1
2
3
4
5
6
7
8
9
10
11
12
13
procedure machwas(var zahl: integer);
begin
  zahl := zahl+5;
  ...
end;

procedure aufruf;
var 
  einezahl: integer;
begin
  einezahl := 5;
  machwas(einezahl);
end;
Hier wird keine Kopie des Variableninhalts übergeben, sondern eine Referenz (also die Speicheradresse) der Variablen einezahl. Wird der Wert in machwas nun um 5 erhöht, geschieht dies auch mit der Variablen einezahl, weil es sich um dieselbe Variable im Speicher handelt. Sie wird nur von den beiden Prozeduren mit anderen Namen angesprochen. Im Gegensatz zum Fall der Wertparameter braucht ein Referenzparemeter in einer Prozedur also keinen zusätzlichen Speicherplatz, da die "originale Variable" weiterverwendet wird.

Über den Umweg des var-Parameters kann man sogar Prozeduren dazu bewegen, Werte zurückzugeben:

markieren
1
2
3
4
5
6
7
8
9
10
11
12
procedure machwas2(var wert1, wert2: integer);
begin
  wert1 := 2;
  wert2 := 3;
end;

procedure aufrufen;
var 
  zahl1, zahl2: integer;
begin
  machwas2(zahl1, zahl2);
end;
Dass die Variablen zahl1 und zahl2 vor der Übergabe an machwas2 nicht initialisiert wurden, macht nichts, da sie dort sowieso nicht ausgelesen werden. In machwas2 werden wert1 und wert2 Werte zugewiesen - und da es sich dabei um Referenzparameter handelt, automatisch auch zahl1 und zahl2. Wenn machwas2 abgearbeitet wurde, enthält zahl1 also den Wert 2 und zahl2 den Wert 3.

Array-Parameter

Es ist natürlich auch möglich, Arrays als Parameter einer Routine zu verwenden. Jedoch nicht auf die Art, die man intuitiv wählen würde:

markieren
1
procedure MachWasAnderes(feld: array [1..20] of integer); //falsch!
Stattdessen muss zunächst ein eigener Typ definiert werden. Dieser kann dann in Prozedur- oder Funktionsköpfen verwendet werden:

markieren
1
2
3
4
type 
  TMeinFeld = array [1..20] of integer;

procedure MachWasAnderes(feld: TMeinFeld);  // Richtig

Prozeduren und Funktionen abbrechen

Nach dem Ausführen einer Prozedur bzw. Funktion wird die Programmausführung an der aufrufenden Stelle fortgesetzt. Wenn man dort aber weitermachen will, bevor die Prozedur/Funktion vollständig ausgeführt wurde, kann man exit verwenden. exit bricht eine Prozedur/Funktion ab und setzt das Programm an der aufrufenden Stelle fort. Bei Funktionen ist darauf zu achten, dass bereits ein Rückgabewert definiert wurde.


Letzte Änderungen - Menü bearbeiten
Zuletzt bearbeitet am 06.08.2007, 16:32 von Christian Reich
Bearbeiten - Attribute - Historie