Kapitel 2 Funktionen Mit Funktionen kann man in C++-Programmen bestimmte Aufgaben erledigen. Mit der Funktionsdefinition gibt man an, was die Funktion tun soll. Eine Funktion kann nicht aufgerufen werden, solange sie nicht deklariert wurde. Funktionen geben entweder einen Wert oder void (das heißt kein Rückgabetyp) zurück. Funktionen gliedern sich in Kopf und Rumpf. Der Kopf besteht aus dem Rückgabetyp, dem Namen der Funktion und den Parametern. Ein typischer Funktionskopf sieht folgendermaßen aus: int sum(int a, int b) Der Name der Funktion samt Parameter wird auch als Signatur bezeichnet. Der Rumpf einer Funktion besteht aus einer öffnenden geschweiften Klammer, einer beliebigen Zahl von Anweisungen, und einer schließenden geschweiften Klammer. Funktionen können einen Wert mit Hilfe der return Anweisung zurückgeben. Falls man keine return Anweisung vorsieht, liefert die Funktion automatisch void zurück. Folgende Funktion add.cc liest zwei Werte ein und gibt die Summe der beiden Zahlen: 1: #include<iostream> 2: using namespace std; 3: 4: int add(int x, int y); 5: 6: int main() 7: { 8: int a, b, c; 9: cout << "Bitte geben Sie zwei Zahlen ein: " << endl; 10: cin >> a; 11: cin >> b, 12: c = add(a,b); 13: cout << "Die Summe von " << a << " und " << b << " ist " << c << endl; 14: return 0; 1 15: 16: 17: 18: 19: 20: } int add(int x, int y) { return (x+y); } Die Funktionsdeklaration steht in Zeile vier. Die Funktionsdefinition steht in den Zeilen 18 bis 20. Die Funktionsdefiniton und -deklaration müssen bis auf das Semikolon identisch sein, sonst erhält man einen Compilerfehler. Die Anweisungen innerhalb des Rumpfes der Funktion müssen mit einem Semikolon abgeschlossen sein. Variablen, die in Funktionen deklariert sind, heißen lokale Variablen. Sie existieren nur innerhalb der Funktion. Variablen, die außerhalb aller Funktionen definiert sind, heißen globale Variablen. Sie sind in jeder Funktion sowie in main verfügbar. Lokale Variablen verdecken globale: Bei Variablen mit gleichem Namen wird bei Aufruf dieser Variablen zuerst in der lokalen Ebene gesucht. Im Allgemeinen vermeidet man globale Variablen. Funktionsargumente müssen nicht alle vom selben Typ sein. Es ist auch möglich, Funktionen als Parameter zu übergeben. Die an eine Funktion übergebenen Werte sind lokal. Das demonstriert folgendes Programm swap_wert.cc. #include <iostream> void swap(int x, int y); int main() { int x = 5, y = 10; std::cout << "Vor der Vertauschung x: " << x << " y: " << y << std::endl; swap(x,y); std::cout << "Nach der Vertauschung x: " << x << " y: " << y << std::endl; return 0; } void swap (int x, int y) { int temp; temp = x; x = y; y = temp; } 2 Die Ausgabe lautet: Vor der Vertauschung x: 5 y: 10 Nach der Vertauschung x: 5 y: 10 Die Funktion swap legt lokale Kopien der Werte an und vertauscht nur diese Kopien. Nach Rückehr aus der Funktion void in die Funktion main werden diese lokalen Kopien gelöscht. In main ist kein Unterschied festzustellen. 2.1 Rückgabewerte Funktionen geben entweder einen Wert oder void zurück. Das Schlüsselwort hierfür ist return, gefolgt von dem zurückzugebenden Wert. Dieser Wert kann auch ein Ausdruck sein, der einen Wert liefert. return 1; return (x>1); Der Wert in der zweiten Anweisung ist 1, falls x größer als 1 ist, sonst 0. Alle Anweisungen, die nach return stehen, werden nicht mehr ausgeführt. 2.2 Standardparameter Falls man eine Funktion int function (int x) deklariert hat, so muss diese Funktion tatsächlich eine Integer-Variable übernehmen, damit man keinen Compilerfehler erhält. Man kann allerdings auch mit Standardoder Defaultparametern arbeiten. Dies bedeutet, dass die Funktion einen Standardparameter verwendet, falls man keinen anderen Wert bereitstellt. int function (int x=50) Falls man diese Funktion ohne Argument aufruft, benutzt sie den Standardwert 50. Die Funktionsdefinition ändert sich hierdurch nicht. Man kann allen Parametern einer Funktion oder einem Teil davon Standardwerte zuweisen. Die Regeln hierfür sind wie folgt: Der Funktion int function (int x, int y, int z) kann für y nur ein Standardwert zugeordnet werden, wenn man für z einen Standardwert festgelegt hat. An x lässt sich nur dann ein Standardwert zuweisen, wenn sowohl y als auch z Standardwerte haben. 3 2.3 Funktionen überladen Es ist in C++ möglich, mehrere Funktionen mit demselben Namen zu erzeugen, die sich nur in der Parameterliste (durch unterschiedliche Typen oder eine andere Anzahl an Parametern) unterscheiden. int function (int x, int y); int function (double x, double y); int function (int x); Ein Überladen bezüglich des Rückgabetyps ist nicht möglich. Das Überladen von Funktionen bezeichnet man auch als Funktionspolymorphie. 2.4 Inline Funktionen Normalerweise springt der Compiler beim Aufruf einer Funktion zu den Anweisungen der Funktion und geht bei Rückkehr aus dieser Funktion in die nächste Zeile der aufrufenden Funktion. Das bedeutet für den Prozessor zusäzlich Arbeit. Falls man oft Funktionen benutzt, die nur aus wenigen Zeilen bestehen, empfielt sich daher eine andere Technik. Man definiert diese Funktion als inline. Dann erzeugt der Compiler keine Funktion im engeren Sinn, sondern kopiert den Code für die Inline-Funktion direkt in die aufrufende Funktion. Die Sprünge des Compilers fallen weg und es ist so, als hätte man die Anweisungen der Funktion direkt in die aufrufende Funktion geschrieben. Dadurch wird die Datei größer. Generell kommen nur Funktionen als inline in Frage, die sehr klein sind (wenige Anweisungen). Die Deklaration einer Inline-Funktion schreibt sich: inline int function (int); Die Deklaration einer Funktion als inline ist nur eine Empfehlung an den Compiler, die dieser auch ignorieren kann. 2.5 if-Anweisung Wie in anderen Programmiersprachen kann man in C++ auch die if-Anweisung verwenden. Die Syntax ist if (Ausdruck1) Anweisung1; Solange Ausdruck1 wahr ist, wird die Anweisung ausgeführt. Auch die else-Anweisung kann verwendet werden: 4 if (Ausdruck1) Anweisung1; else Anweisung2; Auch kompliziertere if-else-Anweisungen sind möglich, allerdings müssen Aneisungen über mehrere Zeilen in geschweiften Klammern stehen. if (Ausdruck1) { if (Ausdruck2) Anweisung1; else { if (Ausdruck3) Anweisung2; else Anweisung3; } } else Anweisung4; 2.6 switch-Anweisung Mit der switch-Anweisung lassen sich Verzweigungen in Abhängigkeit von mehreren unterschiedlichen Werten aufbauen. Die Synatx einer allgemeinen switch-Anweisung lautet: switch (Ausdruck) { case Wert1: Anweisung; break; case Wert2: Anweisung; break; ... case WertN: Anweisung; break; default: Anweisung; } Ausdruck ist jeder gültige C++-Ausdruck, Anweisung jede beliebige C++-Anweisung. switch wertet Ausdruck aus und vergleicht das Ergebnis mit den Werten hinter 5 case. Stimmt kein Wert überein, wird die Anweisung hinter default ausgeführt. Die Auswertung erfolgt nur auf Gleichheit. Falls die break-Anweisung am Ende eines case-Zweigs fehlt, führt das Programm den nächsten case-Zweig aus. 2.7 2.7.1 Schleifen while Eine while-Schleife wiederholt eine Folge von Anweisungen, solange die Startbedingung wahr ist. while(Ausdruck) { Anweisung1; Anweisung2; } Falls das Programm zu Anfang der while-Schleife zurückkehren soll, noch bevor alle Anweisungen abgearbeitet sind, benutzt man die continue-Anweisung. Die breakAnweisung führt zum Austritt aus der while-Schleife und die Programmausführung setzt sich nach der schließenden Klammer fort. 2.7.2 for Die for-Schleife hat drei Anweisungen. In der ersten Anweisung wird üblicherweise die Zählervariable initialisiert. Die zweite Anweisung testet eine Bedingung und die dritte Anweisung inkrementiert oder dekrementiert normalerweise die Zählervariable. Eine typische for-Schleife sieht wie folgt aus: for (int i=0; i<10; i++) { cout << "i= " << i << endl; } Man kann auch mehrere Variablen in einer Schleife initialisieren und inkrementieren: for (int i=0; j=0; i<10; i++, j++) { cout << "i= " << i << " j: " << j << endl; } 6 2.8 Statische Variablen Statische Variablen sind mit globalen Variablen vergleichbar. Sie werden nur beim ersten Aufruf der Funktion durch ihre Definition initialisiert. Das Beispiel static.cc verdeutlicht dies: #include<iostream> using namespace std; void f(int a) { while(a>0) { static int n = 0; int x = 0; cout << "n == " << n++ << "; x == " << x++ << endl; a--; } } int main() { f(3); } mit der Ausgabe n == 0; x == 0 n == 1; x == 0 n == 2; x == 0 Die Variable n wird nur einmal initialisiert, die Variable x hingegen a-mal. 2.9 Typcast Zum Typcast dienen die Operatoren static_cast, dynamic_cast und const_cast. Die Syntax (für alle Operatoren gleich) ist static_cast<Typ>(Ausdruck) Beim Aufruf der Operatoren wird Ausdruck geprüft und in Typ umgewandelt. static_cast castet zwischen elementarenDatentypen und Klassen. dynamic_cast castet Zeiger oder Referenzen auf miteinander verwandte ploymorphe Klassen. Dieser Cast wird im Gegensatz zum statischen Cast zur Laufzeit ausgeführt. const_cast dient zur Entfernung der Konstantheit eines Typs. 7