1 5 Funktionen und eine Einführung in Rekursion 2006 Pearson Education, Inc. All rights reserved. 2 5.1 Einführung 5.2 Programmkomponenten in C 5.3 Funktionen der math.h Bibliothek 5.4 Funktionsdefinitionen mit mehreren Parametern 5.5 Funktionsprototypen und Argumentkonvertierung 5.6 Headerdateien der C Standardbibliothek 5.7 Fallstudie: Erzeugung von Zufallszahlen 5.8 Fallstudie: Ein Glücksspiel und Einführung von enum 5.9 Statische Speicherklasse 5.10 Regeln für Gültigkeitsbereiche 5.11 Rekursion 5.12 Fallstudie: Fibonacci-Reihe 5.13 Fallstudie: Türme von Hanoi 5.14 Rekursion und Iteration 2006 Pearson Education, Inc. All rights reserved. 3 5.1 Einführung • Technik des ‘Teile und Herrsche’ (Divide and Conquer) – Konstruktion eines großen Programms aus kleinen, einfachen Teilen (Komponenten) • Funktionen – Erleichtern Design, Implementierung, Betrieb und Wartung von großen Programmen 2006 Pearson Education, Inc. All rights reserved. 4 5.2 Programmkomponenten in C • Funktionen – Erlauben dem Programmierer ein Programm zu modularisieren, indem einzelne Teilaufgaben in selbstständigen Einheiten erledigt werden • Die Anweisungen innerhalb des Funktionskörpers werden nur einmal geschrieben. – Können aber an mehreren Stellen im Programm verwendet werden – Vermeiden die Wiederholung von Code – Werden vor anderen Funktionen verborgen • Können auch in anderen Programmen wiederverwendet werden – Selbstdefinierte oder vordefinierte Bibliotheksfunktionen 2006 Pearson Education, Inc. All rights reserved. 5 Betrachtung zum Software Engineering Um die Wiederverwendung von Software zu fördern, sollte jede Funktion auf eine einfache, wohldefinierte Aufgabe beschränkt sein. Der Name der Funktion sollte diese Aufgabe auf zweckmäßige Art ausdrücken. Solche Funktionen erleichtern es, Programme zu schreiben, zu testen, zu debuggen und zu warten. 2006 Pearson Education, Inc. All rights reserved. 6 Tipp zur Fehlervermeidung Eine kleine Funktion, die eine einzige Aufgabe erledigt, ist leichter zu testen und zu debuggen als eine umfangreiche Funktion, die viele Aufgaben ausführt. 2006 Pearson Education, Inc. All rights reserved. 7 5.2 Programmkomponenten in C • C Standardbibliothek – Reichhaltige Sammlung von Funktionen für die Ausführung üblicher Aufgaben, wie z.B.: • • • • Mathematische Berechnungen Manipulation von Zeichenketten Manipulation von Zeichen Ein- und Ausgabe – Werden als Teil der C-Programmierumgebung zur Verfügung gestellt 2006 Pearson Education, Inc. All rights reserved. 8 5.3 Funktionen der math.h Bibliothek • Funktionen – Haben Funktionsprototypen, die üblicherweise in Headerdateien stehen • Können in jedem Programm wiederverwendet werden, das die Headerdatei inkludiert und das den Objektcode der Funktion mit einbindet – Beispiel: sqrt in der <math.h> Headerdatei • sqrt(900.0) berechnet die Quadratwurzel aus 900.0 • double sqrt(double) ist der Funktionsprototyp 2006 Pearson Education, Inc. All rights reserved. 9 5.3 Funktionen der math.h Bibliothek • Funktionsaufruf – Eine Funktion wird durch einen Funktionsaufruf gestartet. • Die aufgerufene Funktion gibt entweder ein Resultat zurück oder sie gibt an ihrem Ende nach Ausführung der gewünschten Anweisungen einfach die Kontrolle an die aufrufende Funktion zurück (‘Funktion mit Seiteneffekten’). • Funktionsparameter – Information, die einer Funktion beim Funktionsaufruf vom aufrufenden Programmteil übergeben wird • sqrt(x) hat einen Parameter x, an den beim Funktionsaufruf sqrt(900.0) das Funktionsargument 900.0 als Kopie übergeben wird. 2006 Pearson Education, Inc. All rights reserved. 10 Funktion Beschreibung Beispiel ceil( x ) rundet x auf die kleinste ganze Zahl, die nicht kleiner als x ist ceil( 9.2 ) ist 10.0 ceil( -9.8 ) ist -9.0 cos( x ) Kosinus von x (x in Radiant) cos( 0.0 ) ist 1.0 exp( x ) Exponentialfunktion ex exp( 1.0 ) ist 2.71828 exp( 2.0 ) ist 7.38906 fabs( x ) Absolutbetrag von x fabs( 5.1 ) ist 5.1 fabs( 0.0 ) ist 0.0 fabs( -8.76 ) ist 8.76 floor( x ) rundet x auf die größte ganze Zahl, die nicht größer als x ist floor( 9.2 ) ist 9.0 floor( -9.8 ) ist -10.0 fmod( x, y ) Rest von x/y als Gleitpunktzahl fmod( 2.6, 1.2 ) ist 0.2 log( x ) natürlicher Logarithmus von x (Basis e) log( 2.718282 ) ist 1.0 log( 7.389056 ) ist 2.0 log10( x ) Logarithmus von x (Basis 10) log10( 10.0 ) ist 1.0 log10( 100.0 ) ist 2.0 pow( x, y ) x hoch y (xy) pow( 2, 7 ) ist 128 pow( 9, .5 ) ist 3 sin( x ) Sinus x (x in Radiant) sin( 0.0 ) ist 0 sqrt( x ) Quadratwurzel von x (wobei x ein nicht-negativer Wert ist) sqrt( 9.0 ) ist 3.0 tan( x ) Tangens von x (x in Radiant) tan( 0.0 ) ist 0 Fig. 5.2 | Funktionen der math.h Bibliothek. 2006 Pearson Education, Inc. All rights reserved. 5.4 Funktionsdefinitionen mit mehreren Parametern 11 • Mehrere Parameter – Funktionen erfordern oft mehr als eine einzige Information, um ihre Aufgabe zu erfüllen. – Entsprechend kann eine Funktion auch mehrere Argumente vom aufrufenden Programmteil übernehmen. – Die zugehörigen Parameter werden im Funktionsprototyp und im Funktionskopf der Definition als eine kommaseparierte Liste angegeben. 2006 Pearson Education, Inc. All rights reserved. 5.4 Funktionsdefinitionen mit mehreren Parametern 12 • Beispiel: Maximum von drei Zahlen – Der Nutzer soll die von drei Studenten erreichten Punkte als ganze Zahlen zwischen 0 und 100 eingeben. – Das Programm soll das Maximum der drei Werte bestimmen und ausgeben. – Die Bestimmung des Maximums dreier ganzer Zahlen wird als Funktion mit drei Parametern realisiert, die aus main heraus aufgerufen wird. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.5: fig05_05.c 2 // Prototype, use and definition of function maximum with three parameters. 3 #include <stdio.h> Outline 13 4 5 int maximum( int x, int y, int z ); // function prototype 6 7 int main( void ) 8 { 9 fig05_05.c (1 von 2) int grade1, grade2, grade3; 10 11 12 printf( "%s", "Enter three integer grades: " ); scanf( "%d%d%d", &grade1, &grade2, &grade3 ); 13 Aufruf der Funktion maximum übergibt drei Argumente 14 // output maximum of grades entered: 15 printf( "Maximum is: %d\n", maximum( grade1, grade2, grade3 ) ); 16 } // end main 17 18 // function returns the maximum of its three integer parameters 19 int maximum( int x, int y, int z ) Kopf der Funktion maximum 20 { 21 int max = x; // assume x is the largest to start 22 // determine whether y is greater than max 23 24 if( y > max ) max = y; // make y the new max 25 // determine whether z is greater than max 26 if( z > max ) 27 28 Kommaseparierte Parameterliste max = z; // make z the new max return max; 29 } // end function maximum Rückgabe eines Wertes an den Aufrufer 2006 Pearson Education, Inc. All rights reserved. Enter three integer grades: 86 67 75 Maximum is: 86 Enter three integer grades: 67 86 75 Maximum is: 86 Outline 14 fig05_05.c (2 von 2) Enter three integer grades: 67 75 86 Maximum is: 86 2006 Pearson Education, Inc. All rights reserved. 15 Häufiger Programmierfehler Deklariert man Parameter des gleichen Typs double x, y als: anstelle von : double x, double y so ist dies ein Syntaxfehler. Die explizite Angabe eines Typs ist für jeden Parameter in der Parameterliste erforderlich. 2006 Pearson Education, Inc. All rights reserved. 16 Häufiger Programmierfehler Falls Funktionsprototyp, Funktionskopf und Funktionsaufruf nicht alle in bezug auf Anzahl, Typ und Reihenfolge der Argumente bzw. Parameter und im Rückgabetyp übereinstimmen, führt dies zu einer Fehlermeldung des Compilers. 2006 Pearson Education, Inc. All rights reserved. 17 Betrachtung zum Software Engineering Eine Funktion, die viele Parameter hat, erledigt möglicherweise zu viele Aufgaben. Die Funktion könnte in kleinere Funktionen zerlegt werden, die die einzelnen Aufgaben erledigen. Der Funktionskopf sollte nicht länger als eine Zeile sein. 2006 Pearson Education, Inc. All rights reserved. 5.5 Funktionsprototypen und Argumentkonvertierung 18 • Funktionsprototyp – Auch als Funktionsdeklaration bezeichnet – Gibt dem Compiler folgende Informationen: • Name der Funktion • Datentyp, den die Funktion zurückgibt • Parameter, die die Funktion übernimmt: – Anzahl der Parameter – Typen der Parameter – Reihenfolge der Parameter 2006 Pearson Education, Inc. All rights reserved. 19 Betrachtung zum Software Engineering Funktionsprototypen sind in C erforderlich. Um die Funktionsprototypen der Funktionen der C-Standardbibliothek zu erhalten, werden #include Präprozessordirektiven benutzt, die die Headerdateien der entsprechenden Bibliotheken einbinden (so ist z.B. der Prototyp der Funktion sqrt im Header <math.h> enthalten). Entsprechend kann #include zum Einbinden eigener Headerdateien mit Funktionsprototypen verwendet werden. 2006 Pearson Education, Inc. All rights reserved. 20 Häufiger Programmierfehler Falls eine Funktion vor ihrem ersten Aufruf definiert wird, dient der Funktionskopf dieser Definition auch als Funktionsprototyp, so dass ein separater Prototyp unnötig ist. Falls eine Funktion aufgerufen wird, bevor sie definiert wird und dem Compiler zu diesem Zeitpunkt kein Funktionsprototyp bekannt ist, führt dies zu einer Fehlermeldung des Compilers. 2006 Pearson Education, Inc. All rights reserved. 21 Betrachtung zum Software Engineering Es sollten immer Funktionsprototypen zur Verfügung gestellt werden, auch wenn es möglich ist sie wegzulassen. Sind Prototypen vorhanden, ist der Code unabhängig von der Reihenfolge, in der die Funktionen definiert werden, und somit stabiler und besser wartbar. 2006 Pearson Education, Inc. All rights reserved. 5.5 Funktionsprototypen und Argumentkonvertierung 22 • C Konvertierungsregeln – Geben an, wie verschiedene Typen ohne Informationsverlust ineinander umgewandelt werden können – Werden auf Ausdrücke, die Werte mit zwei oder mehr unterschiedlichen Datentypen enthalten, angewendet • Solche Ausdrücke werden auch als Ausdrücke mit Mischtypen (mixed-type expressions) bezeichnet. • Jeder Wert des Ausdrucks wird in den “höchsten” Typ, der in dem Ausdruck vorkommt, umgewandelt. – Eine temporäre Version von jedem Wert wird erzeugt und für den Ausdruck verwendet. • Die Originalwerte bleiben unverändert. 2006 Pearson Education, Inc. All rights reserved. 5.5 Funktionsprototypen und Argumentkonvertierung 23 • C Konvertierungsregeln – Eine Konvertierung findet auch statt, wenn der Typ eines Funktionsarguments nicht mit dem festgelegten Parametertyp übereinstimmt. • Die Konvertierung wird so ausgeführt, als ob der Argumentwert direkt der Parametervariablen zugewiesen würde. • Beispielsweise kann eine Funktion mit einem ganzzahligen Argument aufgerufen werden, auch wenn der Funktionsprototyp einen double-Wert als Parameter festlegt. – Die Funktion wird trotzdem korrekt arbeiten. 2006 Pearson Education, Inc. All rights reserved. 5.5 Funktionsprototypen und Argumentkonvertierung 24 • C Konvertierungsregeln – Konvertierung eines Wertes in einen ‘niedrigeren’ fundamentalen Typ: • Führt normalerweise zu Informationsverlust oder falschem Wert • Kann nur explizit durchgeführt werden – Indem der Wert an eine Variable des niedrigeren Typs zugewiesen wird (oft Compilerwarnung) oder – Indem ein Konvertierungsoperator (‘cast’) benutzt wird 2006 Pearson Education, Inc. All rights reserved. 25 Datentypen long double double float unsigned long int (Synonym mit unsigned long) long int (Synonym mit long) unsigned int int (Synonym mit unsigned) unsigned short int (Synonym mit unsigned short) short int unsigned char char bool (Synonym mit short) Fig. 5.6 | Konvertierungshierarchie für fundamentale Datentypen. 2006 Pearson Education, Inc. All rights reserved. 26 5.6 Headerdateien der C-Standardbibliothek • Headerdateien der C-Standardbibliothek – Jeder Header enthält • Funktionsprototypen der zugehörigen Funktionen • Definitionen verschiedener Datentypen • Konstanten, die von diesen Typen und Funktionen benötigt werden – Dem Compiler wird auf diese Weise mitgeteilt, wie die Schnittstellen für Bibliotheks- (oder auch selbstgeschriebene) Komponenten aussehen. – Alle C-Bibliotheks-Headerdateien sind in Winkelklammern < > eingeschlossen und enden mit .h 2006 Pearson Education, Inc. All rights reserved. 27 Headerdatei Erläuterung <stdio.h> Enthält die Funktionsprototypen für die Ein- und Ausgabefunktionen des C-Standards und von diesen verwendete Info. <stdlib.h> Enthält die Funktionsprototypen für Konvertierungen von Zahlen zu Text, Text zu Zahlen, Speicherallokation, Zufallszahlen und verschiedene andere Hilfsfunktionen. <math.h> Enthält die Funktionsprototypen für Funktionen der mathematischen Bibliothek. <string.h> Enthält die Funktionsprototypen für die Verarbeitung von Zeichenketten. Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 1 von 3). 2006 Pearson Education, Inc. All rights reserved. 28 Headerdatei Erläuterung <time.h> Enthält die Funktionsprototypen und Klassentypen für die Manipulation von Zeit und Datum. Enthält die Funktionsprototypen für Funktionen, die Zeichen in bezug auf bestimmte Eigenschaften (z.B. ob das Zeichen eine Ziffer oder ein Buchstabe ist) testen und Funktionsprototypen für Funktionen, die zur Umwandlung von Kleinbuchstaben in Großbuchstaben und umgekehrt verwendet werden. Enthält Funktionsprototypen und Makros, um verschiedene Umstände, die bei der Programmausführung auftreten können, zu behandeln. Definiert Makros zur Mitteilung von Fehlerzuständen. <ctype.h> <signal.h> <errno.h> Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 2 von 3). 2006 Pearson Education, Inc. All rights reserved. 29 Headerdatei Erläuterung <assert.h> Enthält Makros, die Diagnosehilfsmittel zum Debuggen eines Programms zur Verfügung stellen. <float.h> Enthält die Grenzen der Gleitpunktgrößen für das jeweilige System. <limits.h> Enthält die Grenzen der ganzzahligen Größen für das jeweilige System. Enthält Funktionsprototypen und weitere Informationen, die benötigt werden, um Daten in passender Form für unterschiedliche Sprachen zu verarbeiten (z.B. Währungsformate, Zeichendarstellung, Sortierung von Zeichenketten usw). Enthält Funktionsprototypen für Funktionen, die es erlauben, den normalen Funktionsaufrufmechanismus zu umgehen. Definiert Makros für die Behandlung einer Liste von Funktionsargumenten mit unbekannter Anzahl und Typ. Enthält allgemeine Definitionen von Typen, die C zur Durchführung bestimmter Berechnungen verwendet. <locale.h> <setjmp.h> <stdarg.h> <stddef.h> Fig. 5.7 | Headerdateien der C-Standardbibliothek (Teil 3 von 3). 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 30 • Die Funktion rand der C-Standardbibliothek – Ermöglicht die Nutzung von Zufallszahlen in Rechneranwendungen – Beispiel rand(); – Erzeugt eine (pseudo)zufällige ganze Zahl vom Typ unsigned int zwischen 0 und RAND_MAX (symbolische Konstante, die im Header <stdlib.h> definiert wird) – Der Funktionsprototyp für die rand Funktion befindet sich ebenfalls in <stdlib.h> 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 31 • Um zufällige ganze Zahlen in einem bestimmten Bereich zu erhalten, kann der modulo-Operator (%) zusammen mit rand benutzt werden. – Beispiel rand() % 6; – Erzeugt ganze Zahlen im Bereich von 0 bis 5 – Man spricht von Skalierung; 6 ist der Skalierungsfaktor. – Durch zusätzliche Verschiebung entsteht der Bereich von 1 bis 6: 1 + rand() % 6; 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.8: fig05_08.c 2 // Shifted and scaled random integers. 3 #include <stdio.h> 4 #include <stdlib.h> // contains function prototype for rand 5 6 int main( void ) 7 { Outline #include für die rand Funktion unsigned int counter; 9 // loop 20 times 10 for( counter = 1; counter <= 20; ++counter ) { 11 // pick random number from 1 to 6 and output it 12 printf( "%10u", 1 + rand() % 6 ); 13 // if counter is divisible by 5, start a new line of output 14 if( counter % 5 == 0 ) 16 fig05_08.c (1 von 1) 8 16 32 puts( "" ); Aufruf der rand Funktion } // end for 17 } // end main 6 5 6 6 6 1 6 2 5 1 2 3 5 5 4 4 6 3 2 1 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 33 • Beispiel: – Simulation, bei der ein fairer Würfel 6 Millionen mal geworfen wird – Ermitteln der entstehenden Häufigkeitsverteilung: • Jedes einzelne Auftreten einer der Zahlen von 1 bis 6 wird in einer eigenen Variablen, die über eine switch-Struktur angesprochen wird, gezählt. • Diese Strategie zur Ermittlung einer Häufigkeitsverteilung ist umständlich und wird in Kapitel 6 verbessert. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.9: fig05_09.c 2 // Roll a six-sided die 6,000,000 times. 3 4 #include <stdio.h> 5 6 #include <stdlib.h> // contains function prototype for rand 7 int main( void ) 8 { Outline 34 fig05_09.c 9 unsigned int frequency1 = 0; // count of 1s rolled 10 unsigned int frequency2 = 0; // count of 2s rolled 11 12 unsigned int frequency3 = 0; // count of 3s rolled unsigned int frequency4 = 0; // count of 4s rolled 13 unsigned int frequency5 = 0; // count of 5s rolled 14 unsigned int frequency6 = 0; // count of 6s rolled (1 von 3) 15 16 unsigned int roll; // roll counter, value 1 to 6,000,000 17 unsigned int face; // one roll of the die, value 1 to 6 18 19 // loop 6,000,000 times and summarize results 20 for( roll = 1; roll <= 6000000; ++roll ) { 21 face = 1 + rand() % 6; // random number from 1 to 6 Skalierung und Verschiebung des von rand erzeugten Wertes 2006 Pearson Education, Inc. All rights reserved. 22 23 // determine face value and increment appropriate counter 24 switch( face ) { 25 26 27 28 case 1: ++frequency1; // increment the 1s counter break; case 2: 29 ++frequency2; // increment the 2s counter 30 break; 31 32 33 34 case 4: 36 break; 40 case 5: ++frequency5; // increment the 5s counter break; case 6: 41 ++frequency6; // increment the 6s counter 42 break; 43 44 45 46 (2 von 3) ++frequency3; // increment the 3s counter break; ++frequency4; // increment the 4s counter 38 39 fig05_09.c case 3: 35 37 Outline 35 default: // invalid value puts( "Program should never get here!" ); } // end switch } // end for 2006 Pearson Education, Inc. All rights reserved. 47 48 printf( "%s%13s\n", "Face", "Frequency" ); // output header 49 printf( " 1%13u\n", frequency1 ); 50 printf( " 2%13u\n", frequency2 ); 51 printf( " 3%13u\n", frequency3 ); 52 printf( " 4%13u\n", frequency4 ); 53 printf( " 5%13u\n", frequency5 ); 54 printf( " 6%13u\n", frequency6 ); 55 } // end main Face 1 2 3 4 5 6 Frequency 999702 1000823 999378 998898 1000777 1000422 Outline 36 fig05_09.c (3 von 3) Jede Augenzahl des Würfels erscheint etwa 1.000.000 mal 2006 Pearson Education, Inc. All rights reserved. 37 Tipp zur Fehlervermeidung In einem switch sollte immer ein default Case vorhanden sein, der auf Fehlerzustände hinweist – auch wenn man absolut sicher ist, dass keine Fehler auftreten können! 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 38 • Funktion rand – Erzeugt pseudozufällige Zahlen – Bei jeder Programmausführung wird die gleiche, fest vorgegebene Zahlenfolge erzeugt. • Randomisierung – Ein Programm wird dazu gebracht, bei jeder Ausführung unterschiedliche Zufallszahlenfolgen zu erzeugen. • Die Funktion srand der C-Standardbibliothek – Übernimmt ein Argument vom Typ unsigned int – Legt in Abhängigkeit von ihrem Argument einen Einstiegspunkt in die (feste) Zahlenfolge, die rand erzeugt, fest. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.10: fig05_10.c 2 // Randomizing die-rolling program. 3 #include <stdio.h> 4 #include <stdlib.h> // contains prototypes for functions srand and rand Outline 39 5 6 int main( void ) 7 { unsigned ist eine Abkürzung für unsigned int (1 von 2) 8 unsigned counter; 9 unsigned seed; // number used to seed random number generator 10 fig05_10.c seed wird an srand übergeben, um die benutzte Zufallsfolge festzulegen 11 printf( "%s", "Enter seed: " ); 12 scanf( "%u", &seed ); 13 srand( seed ); // seed random number generator 14 15 // loop 10 times 16 for( counter = 1; counter <= 10; ++counter ) { 17 // pick random number from 1 to 6 and output it 18 printf( "%10u", 1 + rand() % 6 ); 19 // if counter is divisible by 5, start a new line of output 20 if( counter % 5 == 0 ) 21 22 puts( "" ); } // end for 23 } // end main 2006 Pearson Education, Inc. All rights reserved. Enter seed: 67 6 1 1 6 4 1 6 6 2 4 Enter seed: 432 4 3 6 1 3 5 1 4 6 2 Outline 40 fig05_10.c (2 von 2) Enter seed: 67 6 1 1 6 4 1 6 6 2 4 Nur unterschiedliche Werte von seed führen zu unterschiedlichen Zufallsfolgen 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 41 • Randomisierung, ohne jedes Mal einen anderen Wert von seed einzugeben: srand( time( NULL ) ); • Der Rechner liest seine interne Uhr, um einen Wert für seed zu erhalten. – Funktion time (mit dem Argument NULL) • Gibt die aktuelle Zeit als Anzahl der Sekunden zurück, die seit dem 1. Januar 1970, 0 Uhr (Greenwich Mean Time, GMT) vergangen sind. • Der Funktionsprototyp für time ist in <time.h> enthalten. 2006 Pearson Education, Inc. All rights reserved. 42 Häufiger Programmierfehler Wird die Funktion srand( time( NULL ) ) mehr als einmal in einem Programm aufgerufen, wird die Pseudozufallszahlenfolge wieder neu gestartet (wenn weniger als eine Sekunde vergangen ist, wieder an der gleichen Stelle wie zuvor!). In diesem Fall sind die von rand erzeugten Zahlen nicht mehr zufällig. 2006 Pearson Education, Inc. All rights reserved. 5.7 Fallstudie: Erzeugung von Zufallszahlen 43 • Skalierung und Verschiebung von Zufallszahlen – Um Zufallszahlen mit einem gewünschten Wertebereich zu erzeugen, kann folgende Anweisung benutzt werden: number = Verschiebungswert + rand() % Skalierungsfaktor; • Verschiebungswert entspricht dem ersten Wert im gewünschten Bereich von aufeinanderfolgenden ganzen Zahlen. • Skalierungsfaktor entspricht der Breite des gewünschten Wertebereiches von aufeinanderfolgenden ganzen Zahlen. – Dies ist die Anzahl von aufeinanderfolgenden ganzen Zahlen im gewünschten Wertebereich. 2006 Pearson Education, Inc. All rights reserved. 5.8 Fallstudie: Ein Glücksspiel und Einführung von enum 44 • Enumeration – Eine Menge von ganzzahligen Konstanten, die durch Bezeichner dargestellt werden • Die Werte der Enumerationskonstanten beginnen mit 0 – es sei denn, ein anderer ganzzahliger Startwert wird angegeben - und werden jeweils um 1 inkrementiert. • Die Bezeichner in einer Enumeration müssen eindeutig sein, aber unterschiedliche Enumerationskonstanten können den gleichen ganzzahligen Wert haben. – Definition einer Enumeration • Schlüsselwort enum • Erzeugt eine Typbezeichnung • Kommaseparierte Liste von Bezeichnern innerhalb geschweifter Klammern • Beispiel enum Months { JAN = 1, FEB, MAR, APR }; 2006 Pearson Education, Inc. All rights reserved. 5.8 Fallstudie: Ein Glücksspiel und Einführung von enum 45 • Regeln für das Würfelspiel Craps – Der Spieler spielt gegen die Bank. – Der Spieler wirft zwei Würfel. • 7 oder 11 beim ersten Wurf: Der Spieler gewinnt. • 2, 3 oder 12 beim ersten Wurf: Der Spieler verliert. • 4, 5, 6, 8, 9, 10 beim ersten Wurf: – Die geworfene Augenzahl wird zum "point“ des Spielers. – Um zu gewinnen, muss der Spieler seinen point noch einmal werfen, bevor er eine 7 wirft. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.11: fig05_11.c 2 // Craps simulation. 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <time.h> // contains prototype for function time Outline 46 fig05_11.c 6 7 // enumeration constants that represent the game status 8 enum Status { CONTINUE, WON, LOST }; // all caps in constants (1 von 4) 9 10 unsigned rollDice( void ); // function prototype Enumeration zur Darstellung des Spielzustands 11 12 int main( void ) 13 { 14 unsigned sumOfDice; // sum of rolled dice 15 unsigned myPoint; // point if no win or loss on first roll 16 enum Status gameStatus; // can contain CONTINUE, WON or LOST 17 18 // randomize random number generator using current time 19 srand( time( NULL ) ); Definition einer Variablen des Enumerationstyps Status 20 21 22 sumOfDice = rollDice(); // first roll of the dice Setzen des Zufallszahlengenerators mit der aktuellen Zeit 2006 Pearson Education, Inc. All rights reserved. 23 // determine game status and point (if needed) based on first roll 24 switch( sumOfDice ) { 25 case 7: // win with 7 on first roll 26 case 11: // win with 11 on first roll 27 gameStatus = WON; 28 break; 29 case 2: // lose with 2 on first roll 30 case 3: // lose with 3 on first roll 31 case 12: // lose with 12 on first roll 32 gameStatus = LOST; 33 break; 34 Outline Zuweisung einer Enumerationskonstanten an gameStatus fig05_11.c (2 von 4) default: // did not win or lose, so remember point 35 gameStatus = CONTINUE; // game is not over 36 myPoint = sumOfDice; // remember the point 37 printf( "Point is %u\n", myPoint ); 38 break; // optional at end of switch 39 } // end switch 40 Vergleich einer Variablen des Enumerationstyps mit einer Enumerationskonstanten 41 // while game is not complete 42 while( gameStatus == CONTINUE ) { // not WON or LOST 43 47 sumOfDice = rollDice(); // roll dice again 44 45 // determine game status 46 if( sumOfDice == myPoint ) // win by making point 47 48 49 50 51 gameStatus = WON; else if( sumOfDice == 7 ) // lose by rolling 7 before point gameStatus = LOST; } // end while 2006 Pearson Education, Inc. All rights reserved. 52 53 // display won or lost message 54 if( gameStatus == WON ) 55 56 57 Outline 48 puts( "Player wins" ); else puts( "Player loses" ); fig05_11.c 58 } // end main 59 (3 von 4) 60 // roll dice, calculate sum and display results 61 unsigned rollDice( void ) 62 { 63 unsigned die1; // first die 64 unsigned die2; // second die 65 unsigned sum; // sum of dice Funktion, die die Aufgabe des Werfens zweier Würfel erledigt 66 67 // pick random die values 68 die1 = 1 + rand() % 6; // first die roll 69 die2 = 1 + rand() % 6; // second die roll 70 sum = die1 + die2; // compute sum of die values 71 72 // display results of this roll 73 printf( "Player rolled %u + %u = %u\n", die1, die2, sum ); 74 return sum; // end function rollDice 75 } // end function rollDice 2006 Pearson Education, Inc. All rights reserved. Player rolled 2 + 5 = 7 Player wins Player rolled 6 + 6 = 12 Player loses Player rolled Point is 6 Player rolled Player rolled Player rolled Player rolled Player wins 3 + 3 = 6 Player rolled Point is 4 Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player loses 1 + 3 = 4 5 4 2 1 4 2 6 2 2 1 4 4 + + + + + + + + + + + + 3 5 1 5 6 4 4 3 4 1 4 3 = = = = = = = = = = = = Outline 49 fig05_11.c (4 von 4) 8 9 3 6 10 6 10 5 6 2 8 7 2006 Pearson Education, Inc. All rights reserved. 50 Praxis-Tipp Bezeichner für einen nutzerdefinierten Typ sollten mit Großbuchstaben beginnen. Enumerationskonstanten sollten komplett mit Großbuchstaben geschrieben werden. Dadurch fallen diese Konstanten im Programm besonders auf und können nicht mit Variablen verwechselt werden. 2006 Pearson Education, Inc. All rights reserved. 51 Praxis-Tipp Der Einsatz von Enumerationen anstelle von ganzzahligen Konstanten macht Programme klarer und damit besser wartbar. Der Wert einer Enumerationskonstanten wird einmal in der Deklaration der Enumeration gesetzt. 2006 Pearson Education, Inc. All rights reserved. 52 Häufiger Programmierfehler Versucht man das ganzzahlige Äquivalent einer Enumerationskonstanten einer Variablen vom Enumerationstyp zuzuweisen, führt dies zu einer Fehlermeldung des Compilers. Versucht man einer Enumerationskonstanten einen anderen Wert zuzuweisen, nachdem diese Konstante einmal definiert wurde, führt dies zu einer Fehlermeldung des Compilers. 2006 Pearson Education, Inc. All rights reserved. 53 5.9 Statische Speicherklasse • Jeder Bezeichner hat – Speicherklasse • Legt die Zeitdauer fest, während der ein Bezeichner im Speicher existiert – Gültigkeitsbereich • Legt fest, wo innerhalb eines Programms auf einen Bezeichner zugegriffen werden kann – dateiübergreifende Gültigkeit • Legt fest, ob ein Bezeichner nur in der Quelldatei, in der er deklariert wurde, bekannt ist oder in mehreren, einzeln übersetzen Dateien, die dann gemeinsam gebunden werden. 2006 Pearson Education, Inc. All rights reserved. 54 5.9 Statische Speicherklasse • Statische Speicherklasse – Variablen mit statischer Speicherklasse • Existieren ab dem Punkt, an dem die Programmausführung beginnt • Werden nur einmal an der Stelle ihrer Definition initialisiert • Existieren bis zum Programmende – Obwohl die Variablen vom Beginn der Programmausführung an existieren, bedeutet dies nicht, dass sie während des gesamten Programms benutzt werden können. 2006 Pearson Education, Inc. All rights reserved. 55 5.9 Statische Speicherklasse • Es gibt zwei Typen von Bezeichnern mit statischer Speicherklasse a) Externe Bezeichner • Globale Variablen (und globale Funktionsnamen) b) Lokale Variablen, die mit dem Speicherklassenspezifizierer static deklariert werden 2006 Pearson Education, Inc. All rights reserved. 56 5.9 Statische Speicherklasse • Globale Variablen – Werden erzeugt, indem die Variablendeklaration außerhalb jeder Klassen- oder Funktionsdefinition geschrieben wird – Behalten ihre Werte während der gesamten Programmausführung – Können aus allen Funktionen in der gleichen Quelldatei angesprochen werden, die nach ihren Deklarationen stehen 2006 Pearson Education, Inc. All rights reserved. 57 5.9 Statische Speicherklasse • Lokale Variablen, die als static deklariert werden – Sind nur innerhalb der Funktion bekannt, in der sie deklariert werden – Behalten ihre Werte, wenn die Funktion beendet wird und zu ihrem Aufrufpunkt zurückkehrt • Wenn die Funktion das nächste Mal aufgerufen wird, haben die static lokalen Variablen die Werte, die sie hatten, als die Funktion zuletzt beendet wurde. – Falls nummerische Variablen mit statischer Speicherklasse nicht explizit vom Programmierer initialisiert werden, werden sie automatisch mit Null initialisiert. 2006 Pearson Education, Inc. All rights reserved. 58 Betrachtung zum Software Engineering Wird eine Variable als global statt als lokal deklariert, können unbeabsichtigte Seiteneffekte auftreten: Eine Funktion, die eigentlich keinen Zugriff auf diese Variable benötigt, könnte diese unbeabsichtigt oder in schlechter Absicht verändern. Im allgemeinen sollten globale Variablen vermieden werden außer in bestimmten Situationen mit extremen Performanzanforderungen. 2006 Pearson Education, Inc. All rights reserved. 59 Betrachtung zum Software Engineering Dies ist ein Beispiel für das ‘Prinzip der möglichst geringen Berechtigungen’ (principle of least privilege). Dieses Prinzip sagt aus, dass Code nur das Ausmaß an Berechtigungen und an Zugriffserlaubnis bekommen darf, das für die Erfüllung der konkreten Aufgabe nötig ist, aber nicht mehr. 2006 Pearson Education, Inc. All rights reserved. 60 Betrachtung zum Software Engineering Variablen, die nur innerhalb einer bestimmten Funktion benutzt werden, sollten als lokale Variablen in dieser Funktion deklariert werden. 2006 Pearson Education, Inc. All rights reserved. 61 5.10 Regeln für Gültigkeitsbereiche • Gültigkeitsbereich (scope) – Teil des Programms, wo ein Bezeichner verwendet werden kann • Wichtige Gültigkeitsbereiche für einen Bezeichner sind: – Dateiscope – Blockscope 2006 Pearson Education, Inc. All rights reserved. 62 5.10 Regeln für Gültigkeitsbereiche • Dateiscope – Für einen Bezeichner, der außerhalb jeder Funktion oder Klasse deklariert ist • Ein solcher Bezeichner ist in allen Funktionen ab dem Punkt, an dem er deklariert wurde, bis zum Dateiende bekannt. – Globale Variablen, Funktionsdefinitionen und Funktionsprototypen außerhalb einer Funktion haben Dateiscope. • Sollen sie auch in einer weiteren Datei verwendet werden, können sie mit Hilfe des Schlüsselwortes extern dort deklariert werden. 2006 Pearson Education, Inc. All rights reserved. 63 5.10 Regeln für Gültigkeitsbereiche • Blockscope – Bezeichner, die innerhalb eines Blocks deklariert werden, haben Blockscope. • Blockscope beginnt an der Deklaration des Bezeichners. • Blockscope endet an der schließenden geschweiften Klammer (}) des Blocks, in dem der Bezeichner deklariert ist. – Lokale Variablen und Funktionsparameter haben Blockscope. • Der Funktionskörper ist ihr Block. – Jeder Block kann Variablendeklarationen enthalten. – Bezeichner in einem äußeren Block können durch einen lokalen Bezeichner in einem geschachtelten inneren Block mit gleichem Namen verborgen werden. – Lokale static Variablen haben Blockscope, obwohl sie auch außerhalb des Blocks weiter existieren. • Lebensdauer und Gültigkeitsbereich sind unabhängig voneinander. 2006 Pearson Education, Inc. All rights reserved. 64 Häufiger Programmierfehler Der versehentliche Gebrauch des gleichen Bezeichners für eine Variable in einem inneren Block und für eine andere Variable in einem äußeren Block ist normalerweise ein logischer Fehler (kein Übersetzungsfehler!). 2006 Pearson Education, Inc. All rights reserved. 65 Praxis-Tipp Variablennamen, die Namen in umschließenden Gültigkeitsbereichen verbergen, sollten vermieden werden. Dies kann erreicht werden, indem kein Bezeichner mehrfach in einem Programm eingesetzt wird. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.12: fig05_12.c 2 // A scoping example. 3 #include <stdio.h> Outline 66 4 5 void useLocal( void ); // function prototype 6 void useStaticLocal( void ); // function prototype 7 void useGlobal( void ); // function prototype 8 9 int x = 1; // global variable 10 11 int main( void ) 12 { 13 fig05_12.c Deklaration einer globalen Variablen außerhalb (1 von 4) jeder Klassen- und Funktionsdefinition Lokale Variable x, die die globale Variable x verbirgt int x = 5; // local variable to main 14 15 printf( "local x in main's outer scope is %d\n", x ); 16 17 18 { // start new scope int x = 7; // hides x in outer scope Lokale Variable x in einem Block, die die lokale Variable x im äußeren Gültigkeitsbereich verbirgt 19 20 21 printf( "local x in main's inner scope is %d\n", x ); } // end new scope 22 23 printf( "local x in main's outer scope is %d\n", x ); 2006 Pearson Education, Inc. All rights reserved. 24 25 useLocal(); // useLocal has automatic local x 26 useStaticLocal(); // useStaticLocal has static local x 27 useGlobal(); // useGlobal uses global x 28 useLocal(); // useLocal reinitializes automatic local x 29 useStaticLocal(); // static local x retains its prior value 30 useGlobal(); // global x also retains its value 31 32 Outline 67 fig05_12.c (2 von 4) printf( "\nlocal x in main is %d\n", x ); 33 } // end main 34 35 // useLocal reinitializes local x during Lokalevariable Variable, die bei each jedemcall Aufruf 36 void useLocal( void ) von useLocal neu erzeugt und initialisiert wird 37 { 38 int x = 25; // initialized each time useLocal is called 39 40 printf( "\nlocal x is %d on entering useLocal\n", x ); 41 ++x; 42 printf( "local x is %d on exiting useLocal\n", x ); 43 } // end function useLocal 2006 Pearson Education, Inc. All rights reserved. 44 45 // useStaticLocal initializes static local variable x only the 46 // first time the function is called; value of x is saved Outline 68 47 // between calls to this function 48 void useStaticLocal( void ) static lokale Variable, die nur einmal initialisiert wird fig05_12.c 49 { 50 static int x = 50; // initialized first time useStaticLocal is called 51 52 printf( "\nlocal static x is %d on entering useStaticLocal\n", x ); 53 ++x; 54 printf( "local static x is %d on exiting useStaticLocal\n", x ); (3 von 4) 55 } // end function useStaticLocal 56 57 // useGlobal modifies global variable x during each call 58 void useGlobal( void ) 59 { 60 printf( "\nglobal x is %d on entering useGlobal\n", x ); 61 x *= 10; 62 printf( "global x is %d on exiting useGlobal\n", x ); 63 } // end function useGlobal Die Anweisung gilt für die globale Variable x, da hier keine lokale Variable x existiert 2006 Pearson Education, Inc. All rights reserved. local x in main's outer scope is 5 local x in main's inner scope is 7 local x in main's outer scope is 5 local x is 25 on entering useLocal local x is 26 on exiting useLocal local static x is 50 on entering useStaticLocal local static x is 51 on exiting useStaticLocal Outline 69 fig05_12.c (4 von 4) global x is 1 on entering useGlobal global x is 10 on exiting useGlobal local x is 25 on entering useLocal local x is 26 on exiting useLocal local static x is 51 on entering useStaticLocal local static x is 52 on exiting useStaticLocal global x is 10 on entering useGlobal global x is 100 on exiting useGlobal local x in main is 5 2006 Pearson Education, Inc. All rights reserved. 70 5.11 Rekursion • Rekursive Funktion – Eine Funktion, die sich selbst aufruft, entweder direkt, oder indirekt (durch eine andere Funktion) • Rekursion kann als Wiederholungsstruktur eingesetzt werden – Durch Aufteilung der Funktion in • Basisfall (‘Base case’) – Die einfachste Variante der Problemlösung, die direkt in die Funktion hinein programmiert wird. • und wiederholten rekursiven Aufruf von sich selbst. 2006 Pearson Education, Inc. All rights reserved. 71 5.11 Rekursion • Rekursiver Aufruf (Rekursionsschritt) – Die Funktion teilt das zu lösende Problem in zwei begriffliche Teile auf: • Eine etwas einfachere oder kleinere Version des ursprünglichen Problems • Der Unterschied, der zwischen der einfacheren Version und dem ursprünglichen Problem besteht – Die Funktion startet (d.h. ruft auf) eine neue Kopie von sich selbst, die die einfachere Version des Problems bearbeiten soll. 2006 Pearson Education, Inc. All rights reserved. 72 5.11 Rekursion • Rekursiver Aufruf (Rekursionsschritt) – Dies kann zu vielen weiteren rekursiven Aufrufen führen, indem die Funktion weiterhin jede neu entstandene Version in die beiden begrifflichen Teile zerlegt. – Diese Folge von immer einfacheren Problemen muss schließlich in den Basisfall münden. • Falls nicht, würde sich die Rekursion unendlich fortsetzen. 2006 Pearson Education, Inc. All rights reserved. 73 5.11 Rekursion • Beispiel: Fakultät – Die Fakultät einer nichtnegativen ganzen Zahl n, geschrieben n! (und gesprochen “n Fakultät”), ist das Produkt n · (n – 1) · (n – 2) · … · 1 – Rekursive Definition der Funktion n! = n · (n – 1)! • Beispiel 5! = 5 · 4 · 3 · 2 · 1 5! = 5 · ( 4 · 3 · 2 · 1) 5! = 5 · ( 4! ) 2006 Pearson Education, Inc. All rights reserved. 74 Fig. 5.28 | Rekursive Entwicklung von 5! 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.29: fig05_29.c 2 // Recursive factorial function. 3 #include <stdio.h> Outline 75 4 5 unsigned long long factorial( unsigned long ); // function prototype fig05_29.c 6 7 int main( void ) 8 { 9 (1 von 2) unsigned int counter; 10 11 // calculate the factorials of 0 through 20 (try 21 also) 12 for( counter = 0; counter <= 21; ++counter ) 13 printf( "%2u! = %llu\n", counter, factorial( counter ) ); 14 } // end main Erster Aufruf der factorial Funktion 15 16 // recursive definition of function factorial 17 unsigned long long factorial( unsigned long number ) 18 { 19 20 21 22 if( number <= 1 ) // test for base case Basisfall (base case): einfach 1 zurückgeben return 1; // base cases: 0! = 1 and 1! = 1 else // recursion step return number * factorial( number - 1 ); 23 } // end function factorial Rekursiver Aufruf der factorial Funktion für ein etwas einfacheres Problem 2006 Pearson Education, Inc. All rights reserved. 0! 1! 2! 3! 4! 5! 6! 7! 8! 9! 10! 11! 12! 13! 14! 15! 16! 17! 18! 19! 20! 21! = = = = = = = = = = = = = = = = = = = = = = 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 6227020800 87178291200 1307674368000 20922789888000 355687428096000 6402373705728000 121645100408832000 2432902008176640000 14197454024290336768 Outline 76 fig05_29.c (2 von 2) 2006 Pearson Education, Inc. All rights reserved. 77 5.11 Rekursion • Beispiel: Fakultät – Der Umwandlungsspezifizierer %llu wird für die Ausgabe von unsigned long long int Werten benötigt. – Die Funktion factorial produziert so schnell sehr große Werte, dass auch unsigned long long int nur bis zum Wert für n = 20 ausreicht, bevor der darstellbare Zahlenbereich überschritten wird. – Hier zeigt sich eine Schwäche von C — die Sprache ist nicht leicht erweiterbar, um die Anforderungen unterschiedlicher Anwendungen erfüllen zu können. – Im zweiten Semester lernen wir mit C++ eine erweiterbare Sprache kennen, die es erlaubt durch “Klassen” einfach neue Datentypen zu erzeugen, die beispielsweise beliebig große ganze Zahlen verarbeiten können, wenn wir dies benötigen. 2006 Pearson Education, Inc. All rights reserved. 78 Häufiger Programmierfehler Falls man den Basisfall vergisst oder den Rekursionsschritt falsch realisiert, so dass er nicht zum Basisfall konvergiert, entsteht eine “unendliche” Rekursion, die schließlich den verfügbaren Speicher erschöpft. Dies ist vergleichbar mit einer Endlosschleife in einer iterativen (d.h. nichtrekursiven) Lösung des Problems. 2006 Pearson Education, Inc. All rights reserved. 79 5.12 Fallstudie: Fibonacci-Reihe • Die Fibonacci-Reihe – 0, 1, 1, 2, 3, 5, 8, 13, 21, … – Beginnt mit 0 und 1 – Jede folgende Fibonacci-Zahl ist die Summe der beiden vorangehenden Fibonacci-Zahlen. • Dies kann rekursiv definiert werden: F(0) = 0 F(1) = 1 F(n) = F(n – 1) + F(n – 2) 2006 Pearson Education, Inc. All rights reserved. 80 5.12 Fallstudie: Fibonacci-Reihe • Erste Alternative zur Rekursion: Iterative Lösung – Initialisierung von zwei Variablen mit den beiden Anfangswerten 0 und 1 – Verwendung einer Schleife, die • den nächsten Wert als Summe aus den beiden vorangegangenen Werten berechnet, • die Variablen, die die beiden vorangegangenen Werte enthalten, mit dem jeweils folgenden Wert der FibonacciReihe aktualisiert. 2006 Pearson Education, Inc. All rights reserved. 81 5.12 Fallstudie: Fibonacci-Reihe • Zweite Alternative zur Rekursion: Analytische Lösung – Differenzengleichung F(n) – F(n-1) – F(n-2) = 0 • Mit den Anfangsbedingungen F(0) = 0 F(1) = 1 – charakteristische Gleichung z² - z -1 = 0 2006 Pearson Education, Inc. All rights reserved. 82 5.12 Fallstudie: Fibonacci-Reihe – Lösungen der charakteristischen Gleichung z1, 2 1 5 2 2 – Allgemeine Lösung der Differenzengleichung F(n) 1 5 2 n 1 5 2 n 5 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 5.30: fig05_30.c 2 3 4 5 6 7 // Recursive, iterative and analytical computation of Fibonacci series. 8 9 10 11 12 13 unsigned long long int fiboIterative( unsigned int ); // iterative computation 14 15 16 17 18 19 20 #include <stdio.h> Outline 83 #include <time.h> #include <math.h> unsigned long long int fiboRecursive( unsigned int ); // recursive computation unsigned long long int fiboAnalytical( unsigned int ); // mathematical analysis fig05_30.c (1 von 4) int main( void ) { unsigned int n, nStart, nEnd; clock_t start; printf( "%s", "Fibonacci series.\n\nEnter first value of n: " ); scanf( "%u", &nStart ); printf( "%s", "Enter last value of n: " ); scanf( "%u", &nEnd ); 2006 Pearson Education, Inc. All rights reserved. 21 22 for( n = nStart; n <= nEnd; ++n ) { 23 start = clock(); 24 printf( "\nRecursive: 25 printf( " n = %u Outline 84 F(n) = %10llu", n, fiboRecursive( n ) ); (%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC ); 26 27 start = clock(); 28 printf( "Iterative: 29 printf( " fig05_30.c n = %u F(n) = %10llu", n, fiboIterative( n ) ); (%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC ); (2 von 4) 30 31 start = clock(); 32 printf( "Analytical: n = %u 33 printf( " 34 F(n) = %10llu", n, fiboAnalytical( n ) ); (%.2f sec)\n" ,(double)( clock() – start )/CLOCKS_PER_SEC ); } 35 } // end main 36 37 // recursive method 38 unsigned long long int fiboRecursive( unsigned int n ) 39 { 40 41 42 43 if( ( n == 0 ) || ( n == 1 ) ) // base cases return n; else // recursion step return fiboRecursive( n - 1 ) + fiboRecursive( n - 2 ); 44 } // end function fiboRecursive 45 2006 Pearson Education, Inc. All rights reserved. 46 // iterative method 47 unsigned long long int fiboIterative( unsigned int n ) 48 { 49 unsigned long long int previous, now, next; 50 unsigned int i; 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 previous = 0 ; // F(0) now = 1 ; // F(1) next = n ; // result in cases n = 0 or n = 1 Outline 85 fig05_30.c (3 von 4) for( i = 2 ; i <= n; ++i ) { next = now + previous; previous = now; now = next; } return next; } // end function fiboIterative // method using solution of difference equation unsigned long long int fiboAnalytical( unsigned int nPar ) { 67 double GOLDEN_RATIO, n; 68 GOLDEN_RATIO = ( 1. + sqrt( 5. ) ) / 2.; 69 n = (double) nPar; 70 return ( pow( GOLDEN_RATIO, n ) – pow( 1. - GOLDEN_RATIO, n ) ) / sqrt( 5. ); 71 } // end function fiboAnalytical 2006 Pearson Education, Inc. All rights reserved. Fibonacci series. Outline 86 Enter first value of n: 42 Enter last value of n: 46 Recursive: n = 42 Iterative: n = 42 Analytical: n = 42 F(n) = F(n) = F(n) = 267914296 267914296 267914296 (34.66 sec) (0.00 sec) (0.00 sec) Recursive: n = 43 Iterative: n = 43 Analytical: n = 43 F(n) = F(n) = F(n) = 433494437 433494437 433494437 (56.05 sec) (0.00 sec) (0.00 sec) Recursive: n = 44 Iterative: n = 44 Analytical: n = 44 F(n) = F(n) = F(n) = 701408733 701408733 701408733 (90.70 sec) (0.00 sec) (0.00 sec) Recursive: n = 45 Iterative: n = 45 Analytical: n = 45 F(n) = 1134903170 F(n) = 1134903170 F(n) = 1134903170 (146.64 sec) (0.00 sec) (0.00 sec) Recursive: n = 46 Iterative: n = 46 Analytical: n = 46 F(n) = 1836311903 F(n) = 1836311903 F(n) = 1836311903 (237.25 sec) (0.00 sec) (0.00 sec) fig05_30.c (4 von 4) 2006 Pearson Education, Inc. All rights reserved. 87 Fig. 5.31 | Einige rekursive Aufrufe der fibonacci Funktion. 2006 Pearson Education, Inc. All rights reserved. 88 5.12 Fallstudie: Fibonacci-Reihe • Vorsicht bei rekursiven Programmen – Jeder Rekursionsschritt in der Funktion fibonacci verdoppelt die Anzahl der Funktionsaufrufe: • Die Anzahl der rekursiven Aufrufe zur Berechnung der nten Fibonacci-Zahl ist von der Ordnung 2n, abgekürzt geschrieben: O(2n ). • Die 20te Fibonacci-Zahl benötigt O(220) oder etwa eine Million Aufrufe. • Die 30te Fibonacci-Zahl benötigt O(230) oder etwa eine Milliarde Aufrufe. – Exponentielle Komplexität • kann selbst die leistungsfähigsten Rechner alt aussehen lassen. 2006 Pearson Education, Inc. All rights reserved. 89 Tipp zur Performanz Rekursive Programme, die exponentielle Komplexität haben (so wie die gezeigte Berechnung von Fibonacci-Zahlen), sollten vermieden werden. 2006 Pearson Education, Inc. All rights reserved. 90 5.13 Fallstudie: Türme von Hanoi • Türme von Hanoi – Kombinatorisches Spiel, das Edouard Lucas auf der Pariser Weltausstellung 1889 ausstellte. – Geschichte dazu: Auf der ersten von drei Säulen im Tempel von Benares(!) stecken 64 goldene, von unten nach oben immer kleiner werdende Scheiben. – Die Scheiben sollen nach folgenden Regeln von der ersten auf die dritte Säule umgesetzt werden: • Die Scheiben dürfen nur einzeln umgesetzt werden. • Eine Scheibe darf nie auf eine kleinere gelegt werden. • Zum Umbau dürfen alle drei Säulen benutzt werden. 2006 Pearson Education, Inc. All rights reserved. 91 5.13 Fallstudie: Türme von Hanoi • Schritt 0: Startposition 2006 Pearson Education, Inc. All rights reserved. 92 5.13 Fallstudie: Türme von Hanoi • Schritt 2: 2006 Pearson Education, Inc. All rights reserved. 93 5.13 Fallstudie: Türme von Hanoi • Schritt 4: 2006 Pearson Education, Inc. All rights reserved. 94 5.13 Fallstudie: Türme von Hanoi • Schritt 7: 2006 Pearson Education, Inc. All rights reserved. 95 5.13 Fallstudie: Türme von Hanoi • Schritt 8 : 2006 Pearson Education, Inc. All rights reserved. 96 5.13 Fallstudie: Türme von Hanoi • Schritt 11: 2006 Pearson Education, Inc. All rights reserved. 97 5.13 Fallstudie: Türme von Hanoi • Schritt 12: 2006 Pearson Education, Inc. All rights reserved. 98 5.13 Fallstudie: Türme von Hanoi • Schritt 15: Endposition 2006 Pearson Education, Inc. All rights reserved. 99 5.13 Fallstudie: Türme von Hanoi • Gewünschte Konsolenausgabe: 2006 Pearson Education, Inc. All rights reserved. 100 5.13 Fallstudie: Türme von Hanoi 1 Gib numberOfDiscs ein 2 Rufe moveStack( numberOfDiscs,'A','B','C') auf 3 4 Definition von moveStack( count, source, dummy, destination ): 5 6 7 falls count gleich Eins ist gib aus "Scheibe Eins von source nach destination" sonst 8 Rufe moveStack( count-1, source, destination, dummy ) auf 9 gib aus "Scheibe count von source nach destination" 10 Rufe moveStack( count-1, dummy, source, destination ) auf Pseudocode: Rekursiver Algorithmus für die Türme von Hanoi. 2006 Pearson Education, Inc. All rights reserved. 101 5.14 Rekursion und Iteration • Basieren auf unterschiedlichen Kontrollanweisungen – Iteration: Wiederholungsstruktur – Rekursion: Auswahlstruktur • Realisierung der Wiederholung von Anweisungen – Iteration: expliziter Gebrauch der Wiederholungsstruktur – Rekursion: wiederholte Funktionsaufrufe • Endbedingung – Iteration: Schleifentest wird false – Rekursion: Erreichen des Basisfalls (base case) 2006 Pearson Education, Inc. All rights reserved. 102 5.14 Rekursion und Iteration • Graduelle Annäherung an das Endergebnis – Iteration: Änderung des Schleifenzählers – Rekursion: Erzeugung von immer einfacheren Versionen der Problemstellung • Möglichkeit der Endloswiederholung – Iteration: Schleifentest wird nie false – Rekursion: Rekursionsschritt konvergiert nicht zum Basisfall 2006 Pearson Education, Inc. All rights reserved. 103 5.14 Rekursion und Iteration • Stellen zwei unterschiedliche Programmierstile dar: – Iteration: Imperative Programmierung • Die Schritte zur Lösung des gegebenen Problems werden detailliert aufgeschrieben. • Der Programmcode selbst stellt schon die Lösung des Problems dar; beim Programmablauf werden dann nur noch konkrete Werte ermittelt. – Rekursion: Deklarative Programmierung • Das zu lösende Problem wird im Programmcode beschrieben. • Die Lösung ergibt sich erst während des Programmablaufs zur Laufzeit. 2006 Pearson Education, Inc. All rights reserved. 104 5.14 Rekursion und Iteration • Vorteil der Rekursion – Sehr einfache Formulierung der Lösung bestimmter Problemstellungen • Nachteile der Rekursion – Hoher Aufwand durch wiederholte Funktionsaufrufe • Jeder Rekursionsschritt führt zu neuen Kopien aller lokalen Variablen der Funktion. • Kann (zu) viel Prozessorzeit und Speicherplatz kosten 2006 Pearson Education, Inc. All rights reserved. 105 Betrachtung zum Software Engineering Jedes Problem, das rekursiv gelöst werden kann, kann auch iterativ gelöst werden (und umgekehrt). Ein rekursiver Ansatz ist dann sinnvoll, wenn er die Problemstellung direkt widerspiegelt und zu einem Programm führt, das leichter zu verstehen und zu debuggen ist. 2006 Pearson Education, Inc. All rights reserved. 106 Tip zur Performanz Wenn es auf die Performanz eines Programms ankommt, sollte Rekursion vermieden werden. Rekursive Funktionsaufrufe brauchen Zeit und zusätzlichen Speicherplatz. 2006 Pearson Education, Inc. All rights reserved.