Programmiersprache C/C++
7.x. Unterprogramme
Unterprogramme sind Programmeinheiten, d.h. Bausteine, mit deren Hilfe
die Konstruktion eines Programms vereinfacht werden kann.
Zu betrachten sind
-
die Deklaration (Spezifikation, Vereinbarung) von Unterprogrammen,
-
der Aufruf (Aktivierung) von Unterprogrammen
In vielen Programmiersprachen gibt es eine syntaktische Unterscheidung
zwischen
- Hauptprogramm und
- (verschiedenen Arten von) Unterprogrammen.
In C und C++ wird das Hauptprogramm in der gleichen Weise deklariert wie
Unterprogramme, lediglich der Name main entscheidet darüber,
das es sich bei der so benannten Programmeinheit um das Hauptprogramm
handelt.
Das Hauptprogramm kann in C wie jedes andere Unterprogramm sogar rekursiv
aufgerufen werden.
Die meisten Sprachen unterscheiden verschiedene Arten von Unterprogrammen:
-
Prozeduren (procedure)
der Aufruf ist i.a. eine eigenständige Anweisung, z.B.
up ( parameterliste );
up ist der Prozedurname.
-
Funktionen (function)
der Aufruf erfolgt innerhalb eines Ausdrucks, z.B. auf der rechten
Seite einer Ergibtanweisung
y = up ( parameterliste );
oder als aktueller Parameter beim Aufruf eines anderen Unterprogramms
subprogram( up ( parameterliste ) );
up ist der Funktionsname.
Mitunter kommt eine dritte Art hinzu:
-
Operatoren (operator)
der Aufruf erfolgt innerhalb eines Ausdrucks, z.B. auf der rechten
Seite einer Ergibtanweisung
y := operand up operand
oder als aktueller Parameter beim Aufruf eines anderen Unterprogramms
subprogram( operand up operand );
up ist das Operatorsymbol, die Parameterliste zerfällt hier
in Operanden.
C nimmt keine deutliche Trennung in Prozeduren und Funktionen vor.
Alle Unterprogramme (und das Hauptprogramm) sind als Funktionen realisiert.
Funktionen mit dem Datentyp void werden aufgerufen wie Prozeduren.
Funktionen mit einem "richtigen" Datentyp können wahlweise wie Funktionen
oder wie Prozeduren aufgerufen werden.
Die Möglichkeit, neue Operatoren zu definieren bzw. vorhandene zu
überladen, gibt es z.B. in Ada und in Pascal-XSC.
Prozeduren, Funktionen und Operatoren können innerhalb gewisser
Grenzen alternativ eingesetzt werden.
Beispiel: Addition zweier ganzer Zahlen
mittels Operator
int x, y, z;
z = x + y;
mittels Funktion
int add(int x, int y)
{
return x + y;
}
int x, y, z;
z = add(x, y);
mittels "Prozedur"
void add(int x, int y, int *z);
{
*z = x + y;
}
int x, y, z;
add(x, y, &z);
Im Vergleich zeigt sich folgendes:
-
Die Prozedur bietet das allgemeinste Konzept.
Es sind beliebig viele Ein- und Ausgangsgrößen
möglich (Parameterliste).
Eine Anweisung kann maximal einen Prozeduraufruf enthalten.
Prozedurnamen können aussagekräftig gewählt werden.
-
Die Funktion besitzt eine ausgezeichnete Ausgangsgröße
(Funktionswert).
Es sind beliebig viele Eingangsgrößen möglich
(Parameterliste). Die Parameterliste kann weitere Ausgangsgrößen
enthalten, dies ist jedoch untypisch für eine Funktion und sollte
vermieden werden.
Hinsichtlich des Typs des Funktionswertes existieren häufig
Einschränkungen.
Eine Anweisung kann mehrere Funktionsaufrufe enthalten.
Funktionsnamen können aussagekräftig gewählt werden.
-
Operatoren gestatten die kompakteste Darstellung.
Bei der in imperativen Programmiersprachen üblichen
Infix-Notation sind nicht mehr als ein oder zwei Eingangsgrößen
und genau eine Ausgangsgröße möglich.
Eine Anweisung kann mehrere Operatoren enthalten.
Operatorensymbole müssen sehr kurz und prägnant sein,
da sonst die Anweisung schwer lesbar wird. Meist werden
Sonderzeichen verwendet.
Operatoren werden typischerweise in den Sprachdefinitionen festgelegt.
Funktionen und Prozeduren werden durch Sprachstandards nicht immer bzw.
oft nur für sehr elementare Aufgaben definiert. (Ausnahmen sind C
bzw. C++ und Ada). Sprachrealisierungen fügen oft weitere Funktionen
und Prozeduren hinzu, die sich aber häufig voneinander unterscheiden
(Beispiele dafür sind Pascal und Modula, nichtportable Erweiterungen
gibt es aber auch in C-Systemen).
Datenaustausch mit Unterprogrammen
Für den Datenaustausch mit Unterprogrammen gibt es folgende prinzipielle
Möglichkeiten:
- Parameterliste
- gemeinschaftliche Daten
- globale Daten
- Dateien
Der Datenaustausch über Dateien bietet die größte
Flexibilität, ist jedoch mit gegebenfalls erheblich höherer Laufzeit
und dem Problem verbunden, daß 'Dritte' Zugriff zu den Daten erhalten
können.
Die Arbeit mit globale und gemeinschaftlichen Daten ist für den
Programmierer die - scheinbar - unaufwendigste Form für den Datenaustausch
innerhalb eines Programms:
-
Sie verringert den (Erst-)Codieraufwand für den Programmierer.
-
Sie verringert die Kontrollmöglichkeiten gegenüber fehlerhaften
bzw. illegalen Zugriffen und erschwert den Überblick über den
wirklich ablaufenden Datenaustausch.
Programmeinheiten, die diese Formen des Datenaustausches nutzen, sind nicht
bzw. nur eingeschränkt nachnutzbar.
Gemeinschaftliche Daten sind in speziellen Bereichen (COMMON-Blöcken)
angeordnet, auf die jede Programmeinheit bei Bedarf Bezug nehmen kann.
Im Gegensatz zu Dateien werden diese Daten permanent im Hauptspeicher
gehalten.
COMMON-Blöcke sind in C nicht üblich.
Globale Daten werden automatisch an alle Unterprogramme weitergegeben,
die der Deklaration dieser Daten folgen und die im gleichen Programmblock
angesiedelt sind.
Die Arbeit mit globalen Daten wird durch C unterstützt.
Parameter werden an ein Unterprogramm über eine definierte Schnittstelle
- die Parameterliste - übergeben.
Diese Schnittstelle legt fest, welche Daten (Anzahl, Datentyp) in welcher
Richtung (lesen, schreiben) ausgetauscht werden.
Die Organisation der Datenübergabe und Art und Umfang der dabei wirksamen
Kontrolle sind in den einzelnen Programmiersprach(version)en
unterschiedlich ausgebaut.
C stellt eine gewisse Kontrolle bereit, es gibt jedoch Möglichkeiten,
diese Kontrolle zu umgehen.
Der Datenaustausch mit Parametern ist aufwendiger zu programmieren, bietet
aber folgende Vorteile:
-
Es existiert eine eindeutig lokalisierte Schnittstelle, welche die
Möglichkeiten zum Datenaustausch beschreibt. Dies reduziert die
Fehlerquellen bei der Entwicklung und vor allem bei der Wartung von
Programmen.
-
Die Programmeinheiten werden nachnutzbar.
Die Arbeit mit Parameterlisten ist die sicherste Form des Datenaustausches.
Sie kann entweder ausschließlich oder in Kombination mit anderen Techniken
genutzt werden.
Funktionen müssen in C eine - gegebenenfalls leere - Parameterliste
besitzen.
Die an das Unterprogramm bei einem Aufruf zu übergebenden Daten
werden als aktuelle Parameter bezeichnet.
Als aktuelle Parameter können
- Konstanten (Literale oder symbolische Konstanten),
- Variablen und
- Ausdrücke
notiert werden.
Bei bestimmten Techniken der Parameterübergabe sind Variablen
vorgeschrieben.
Ausdrücke werden vor der Übergabe ausgewertet.
Die in der Deklaration eines Unterprogramms notierten Parameter werden
in Unterscheidung von den aktuellen Parametern auch als formale Parameter
bezeichnet.
Es gibt verschiedene Techniken der Parameterübergabe an ein Unterprogramm.
Dabei werden vor allem zwei Techniken eingesetzt:
- Referenzparameter (call by reference):
-
Das Unterprogramm erhält die Adresse des aktuellen Parameters.
Entweder darf es in dem Speicherbereich, den der aktuelle Parameter
belegt
- nur lesen oder
- lesen und schreiben, d.h. seinen Inhalt verändern.
Bei der zweiten Variante muß der aktuelle Parameter stets eine
Variable sein.
Das Unterprogramm arbeitet im Speicherbereich der aufrufenden
Programmeinheit.
- Wertparameter (call by copy):
-
Das Unterprogramm arbeitet mit einer Kopie, es arbeitet nicht im
Speicherbereich der aufrufenden Programmeinheit.
Es gibt folgende Varianten:
-
call by value
Wertparameter als Eingangsgröße
Das Unterprogramm erhält einen Wert, den es als Kopie in
einem eigenen Speicherbereich verwaltet. Dieser Speicherbereich
darf - eventuell - modifiziert werden. Bei Verlassen des
Unterprogramms wird der aktuelle Inhalt dieses Speicherbereiches
nicht an die aufrufende Programmeinheit zurückvermittelt.
-
call by value-result
Wertparameter als Ein- und Ausgangsgröße
Das Unterprogramm erhält einen Wert, den es als Kopie in
einem eigenen Speicherbereich verwaltet. Dieser Speicherbereich
darf modifiziert werden. Bei Verlassen des Unterprogramms wird der
aktuelle Inhalt dieses Speicherbereiches an die aufrufende
Programmeinheit zurückvermittelt.
-
call by result
Wertparameter als Ausgangsgröße
Das Unterprogramm erhält keinen Wert. Es legt für den
Parameter einen eigenen Speicherbereich an, der bei Aufruf des
Unterprogramms keinen (definierten) Wert erhält. Bei
Verlassen des Unterprogramms wird der aktuelle Inhalt dieses
Speicherbereiches an die aufrufende Programmeinheit
zurückvermittelt.
C arbeitet standardmäßig mit einem "call by value" !
Für Unterprogramme, die über Parameter Daten an die aufrufende
Programmeinheit zurückliefern sollen, erfordert das spezielle
Maßnahmen:
-
die aufrufende Programmeinheit muß dem Unterprogramm eine
Adresse übergeben
-
das Unterprogramm muß an die ihm übergebene Adresse einen
Wert kopieren
Diese Arbeitsweise birgt gewisse Risiken in sich:
Das Unterprogramm kann nicht entscheiden, ob ihm wirklich eine Adresse und
versehentlich ein "richtiger" Wert übergeben wurde.
-
variable parameter
Der Parameter kann als Ein- und Ausgangsgröße verwendet
werden.
Deklaration: VAR parameter : typ
-
value parameter
Der Parameter kann nur als Eingangsgröße verwendet werden.
Deklaration: parameter : typ
Delphi und XL Pascal (VM-Modus) gestatten eine dritte Form der
Parameterübergabe:
-
constant parameter
Der Parameter kann nur als Eingangsgröße verwendet werden.
Für das Unterprogramm gilt der Parameter als Konstante, der aktuelle
Parameter muß jedoch keine Konstante sein.
Deklaration: CONST parameter : typ
Mit welchen der oben beschriebenen Techniken der Parameterübergabe
diese Formen realisiert werden, kann von Pascal-System zu Pascal-System
variieren.
Es sollte in der Regel so programmiert werden, daß keine
Abhängigkeiten von der verwendeten Technik entstehen.
Beispiel: Als Eingangsgrößen deklarierte Parameter sollten im
Unterprogramm nie einen neuen Wert erhalten.
Die Technik der Parameterübergabe kann jedoch Einfluß auf
das Laufzeitverhalten und den Speicherbedarf des Programms haben. Ferner
besteht ein gewisser Einfluß die mögliche Höhe von
Schäden durch fehlerhaften Programmcode.
Folgende Faktoren spielen dabei unter anderem eine Rolle:
-
Wertparameter arbeiten mit Kopien, d.h. es wird zusätzlicher
Speicherbedarf entstehen. Je nach Datentyp der Parameter kann dieser
Bedarf marginal oder signifikant sein.
-
Referenzparameter sprechen den Orginal-Speicherbereich an. Dies kann
die Breite des Adressraumes beeinflussen, der zur Bearbeitung einer
Aufgabe gleichzeitig angesprochen wird. Bei Betriebssystemen mit virtueller
Speicherverwaltung kann dies Einfluß auf die Häufigkeit
des Ein- und Auslagerns von Speichersegmenten haben und damit die
Laufzeit erhöhen.
-
Referenzparameter sprechen den Orginal-Speicherbereich an. Fehler im
Programmcode können so größere Auswirkungen haben.
Solche mölichen Auswirkungen lassen sich durch zusätzliche
Maßnahmen reduzieren, die jedoch die Laufzeit verlängern.
Gestaltungsregeln
Für den Aufbau von Unterprogrammen gibt es einige Gestaltungsregeln:
-
Ein Unterprogramm sollte (genau) eine inhaltlich abgeschlossene Aufgabe
lösen.
-
Ein Unterprogramm sollte überschaubar, d.h. die zu lösende Aufgabe nicht
zu komplex sein.
Andererseits sollte die Anzahl der geschaffenen Unterprogramme nicht
unnötig groß sein, da dann inhaltliche Zusammenhänge schwerer erkennbar
werden und der Verwaltungsaufwand steigt.
-
Ein Unterprogramm sollte genau einen Eintrittspunkt und genau einen
Austrittspunkt besitzen.
Im Zusammenhang mit Programmausnahmen ist die Forderung hinsichtlich
des Austrittspunktes so zu sehen, daß die Behandlung der
Programmausnahmen konzentriert wird.
-
Die zwischen Eintritts- und Austrittspunkt liegenden Ablaufstrukturen
sollten linear durchlaufen werden, ohne das (explizite) Rücksprünge
erfolgen.
-
Der Datenaustausch der Einheit mit seiner Umgebung sollte über (genau)
eine eindeutig beschriebene Schnittstelle laufen.
-
Der Umfang der auszutauschenden Daten ist so gering wie möglich zu
halten.
Zurück zum Menü
Zurück zur vorigen Seite
Weiter zur nächsten Seite
P. Böhme, 12.02.1996