MezData-Logo

Kleine Geschichte der Unterprogramme

Die hatten Probleme damals

Früher mit Basic (Apple-Soft-Basic aus meiner Schulzeit bei Frau Knaier, ich war 15 also vor ca. 24 Jahren) aber auch heute in Assembler sieht ein Unterprogrammaufruf etwa so aus:

10 INPUT "Sag mir Deinen Namen: "; A$
20 PRINT "Ich spring in ein Unterprogramm"
30 GOSUB 100
40 Print "Bin wieder zurueck "; A$
50 Print
60 END
100 Print "Unterprogramm liebt "; A$
110 A$ = A$+"chen"
120 RETURN
Emulation mit Virtual ][

Wer Lust bekommen hat, Apple 2 Emulatoren gibt es auch für Windows..

The A2 Home Page for Apple II Emulators!

GOSUB 100: Go to Subroutine 100, Gehe zum Unterprogramm zur Zeile mit der Nummer 100. Es wurde in 10er Schritten gezählt, damit man später noch leicht Zeilen einfügen konnte.

Damals gab es Probleme: Variablen waren immer Global über das ganze Programm vorhanden, man musste sich absprechen, z.B. die Variablen fingen immer mit den Initialen des Programmierers an. Die Variablen beanspruchten auch Platz wenn sie nur einmal verwendet wurden. Mein erster Rechner hatte 64KiB Hauptspeicher also (1GiB = 230; 64KiB = 216; 230-16 = 214 = 16 384) mal weniger als heute! Die Namen der Unterprogramme waren nicht sehr aussagekräftig: GOSUB 100..

Die Probleme wurden gelöst

Heute würde das Programm in C so aussehen, ohne Zeilennummern aber noch mit globaler Variable a:

string a;                             // Globale Variable a

void unter(){                         // Unterprogramm muss vor erstem Aufruf deklariert / definiert werden
  cout << "Unterprogramm liebt " << a << endl;
  a = a+"chen";
}

void main() {                         // Der Anfang hat einen Namen, er heist main()  
  cout << "Sag mir Deinen Namen: ";
  cin >> a;
  cout << "Ich spring in ein Unterprogramm \n";
  unter();
  cout << "Bin wieder zurueck "<< a;
  cout << endl;
}

Frage: Warum steht da immer void? Antwort: void heist Lücke, es wird kein Rückgabetyp angegeben.

Lokale Variablen

Die geschweiften Klammern {} halten das Unterprogramm nicht nur zusammen sondern schirmen seinen Inhalt nach aussen ab:

void unter(){
  string lokal ="Der ";
  cout << lokal << a;
}

Lokale Variablen existieren nur innerhalb des Unterprogramms, ihr Speicherplatz wird nach Verlassen des Unterprogramms wieder frei gegeben. Auf die Variable lokal kann von aussen nicht zugegriffen (gelesen und geschrieben) werden.
Aus dem Unterprogramm heraus kann jedoch auf alle Umgebungsvariablen (Globale Variablen) zugegriffen werden.
Ein Verändern von globalen Variablen durch ein Unterprogramm wird auch als Seiteneffekt bezeichnet.

Datenaustausch mit Unterprogrammen

Um Daten in das Unterprogramm zu bekommen wieder globale Variablen verwenden? Wie wäre es denn mit einer "Durchreiche":

void unter(string a){ // a ist Parametervariable von unter
  cout << "Unterprogramm liebt " << a << endl;
  a = a+"chen";
}

void main() {
  string a;  // dieses a ist lokale Variable in main
  cout << "Sag mir Deinen Namen: ";
  cin >> a;
  cout << "Ich spring in ein Unterprogramm \n";
  unter(a);
  cout << "Bin wieder zurueck "<< a;
  cout << endl;
}

Schreibt man die Deklarration einer lokalen Variablen in die Klammer des Unterprogramms, kann beim Aufruf des Unterprogramms der Variable ein Wert übergeben werden. Wie man an der Ausgabe erkennen kann -das "chen" fehlt, handelt es sich nicht um die selben a-Variablen, es wurde der Wert des lokalen main-a beim Aufruf in das Parameter-unter-a kopiert, dieser Aufruftyp wird Call by Value (Aufruf mit dem Wert) genannt.

Einen Wert aus dem Unterprogramm zurück bekommen mit return

string unter(string a){ // a ist Parametervariable von unter
  cout << "Unterprogramm liebt " << a << endl;
  a = a+"chen";
  return a; // gib den Wert des lokalen a zurück
}

void main() {
  string a;  // dieses a ist lokale Variable in main
  cout << "Sag mir Deinen Namen: ";
  cin >> a;
  cout << "Ich spring in ein Unterprogramm \n";
  a = unter(a); // nimm den Wert von unter(a) und weise ihn a zu
  cout << "Bin wieder zurueck "<< a;
  cout << endl;
}

Sobald in einem Unterprogramm die Anweisung return Wert ausgeführt wird endet das Unterprogramm mit der Rückgabe des Wertes.

Call by Reference

void unter(string& a){ // a ist Referenzparameter durch &
  cout << "Unterprogramm liebt " << a << endl;
  a = a+"chen";
}

void main() {
  string a;  // dieses a ist lokale Variable in main
  cout << "Sag mir Deinen Namen: ";
  cin >> a;
  cout << "Ich spring in ein Unterprogramm \n";
  unter(a); // wende unter auf a an, a wird an unter übergeben
  cout << "Bin wieder zurueck "<< a;
  cout << endl;
}

In main() gibt es die lokale Variable a. Beim Aufruf von unter(a) wird nun nicht der Wert von a übergeben, sondern eine Referenz auf den Speicherplatz von a . Ein Zugriff in unter auf das unter-a ist wie wenn man direkt in main auf das main-a zugreift.