Inhalt Inhalt: 4. Programmiersprache C 4.1 Programmaufbau in C 4.2 Basisdatentypen und einfache Anweisungen 4.3 Steuerfluss-Konstrukte 4.4 Arbeit mit indizierten Größen (Felder) 4.5 Arbeit mit Pointern 4.6 Arbeit mit Zeichenketten 4.7 Funktionen 4.8 Strukturen 4.9 Typen, Variable und Konstante Peter Sobe 107 4.7 Funktionen Von den Struktogrammen und Programmablaufplänen ist das Konzept einer Prozedur bereits bekannt. Eine Prozedur fasst einen Teil des Algorithmus zusammen und erlaubt, diesen Teil in einem übergeordneten Programm aufzurufen. Die Prozedur wird durch Parameter mit Eingabedaten versorgt. Ausgabedaten werden ebenfalls über Parameter vermittelt. Ein Beispiel: Eingabe x Fakultaet(x,f) Ausgabe f Fakultaet(w,f) f = 1 ja w>1 i = 2 (1) w f = f * i nein Der Begriff Prozedur wird synonym zu Unterprogramm oder Subroutine (engl.) verwendet. In der Programmiersprache C werden Funktionen zur Realisierung von Prozeduren benutzt. R. Großmann, P. Sobe 108
Einsatzmöglichkeiten von Funktionen (1) Funktionen erlauben, dem Programmcode hierarchisch zu strukturieren ein Hauptprogramm steuert dabei die Abfolge von Schritten, die einzelnen Schritte können durch Funktionen realisiert werden mehrfach benötigte Programmteile nur einmal zu schreiben und mehrfach aufzurufen Programmteile mehrmals leicht variiert auszuführen, gesteuert durch Parameter Rekursive Algorithmen, indem eine Funktion sich selbst mit abgeänderten Parametern aufruft zur Laufzeit zu entscheiden, welches Unterprogramm aufgerufen wird (siehe Funktionen-Zeiger) R. Großmann, P. Sobe 109 Einsatzmöglichkeiten von Funktionen (2) Funktionen erlauben, Die Benutzung vorgefertigter Funktionen einer Standardbibliothek, z.b. für Ein- und Ausgabe, für Zeichenkettenverarbeitung usw. Zusammenstellung und Sammeln von Hilfs-Funktionen, die ein Programmierer persönlich oft benutzt R. Großmann, P. Sobe 110
Allgemeines Prinzip: Funktionsdefinition (1) Rückgabetyp Funktionsname (Parameterliste) { Block mit Deklarationen und Anweisungen return ausdruck ; R. Großmann, P. Sobe 111 Eine Funktion wird wie folgt definiert: Funktionsdefinition (2) Rückgabetyp Funktionsname ( Parameterliste ) { return ; Durch den Funktionsnamen wird die Funktion an anderer Stelle aufgerufen. Die Parameterliste enthält eine durch Komma getrennte Folge von Parametern, jeweils mit Typangabe und Variablenname. Eine Funktion kann einen Wert mit dem angegebenen Rückgabetyp zurückgeben. Dazu muss in der Funktion return Wert ausgeführt werden. R. Großmann, P. Sobe 112
C-Programme als Folgen von Funktionen Ein C-Programm stellt eine Folge von Funktionsdefinitionen dar, wobei eine Funktion als Hauptprogramm (main) gekennzeichnet sein muss. In den Funktionen befinden sich wiederum Aufrufe anderer Funktionen. Funktionsdefinitionen können in C nur global sein und dürfen nicht im Block einer anderen Funktion bzw. des Hauptprogramms stehen. R. Großmann, P. Sobe 113 Funktionen in C (1) Beispiel 1: Funktion mit float-rückkehrwert (return!) float max(float a, float b) { if (a>b) return a; else return b; Beispiel 2: Funktion ohne Rückkehrwert void druck(float x) { printf( \n x=%f,x); Beispiel 3: Hauptprogramm mit Funktions-Aufruf void main( ) { float c=4.0f, d=5.99f; druck(max(c,d)); R.Großmann, P.Sobe 114
Funktionen in C (2) Beispiel mit Ausgabeparameter in der Parameterliste: void fakultaet(int x, double *w) { int i; *w=1.0; for (i=2; i<=x; i++) *w = *w * (double)i; Aufruf der Funktion: int i; double y; for (i=0;i<40;i++) { fakultaet(i,&y); printf("i=%03d, fak(i) =%.0lf \n",i,y); R. Großmann, P. Sobe 115 Funktionen in C (3) Benennung einer Funktion: Funktionsnamen unterliegen den gleichen Einschränkungen wie Variablennamen. Beispielsweise darf keine Ziffer am Beginn eines Funktionsnamen stehen. Groß- und Kleinschreibung wird unterschieden, d.h. Myfun() und myfun() sind zwei unterschiedliche Funktionen. Parameter: Eine durch Komma getrennte Liste einzelner Typbezeichner und Variablenbezeichner beschreiben die Parameter. Die als Parameter angegeben Variablen sind innerhalb des Prozedurkörpers gültig und überlagern gleichnamige globale Variablen. Die Parameter werden bei Aufruf der Funktion auf diese Variablen kopiert (Call by Value). Parameter, die als Ausgabe einer Funktion agieren, sind als Zeiger zu übergeben (Call by Reference). Dann wird der Zeiger kopiert, die Änderung des Werts erfolgt auf dem Originalspeicherplatz. R. Großmann P. Sobe 116
Funktionen in C (4) Bei einer C-Funktionsdefinition stellen die Parameter quasi Platzhalter dar. Für diese Parameter werden dann beim Funktionsaufruf Argumente eingesetzt. Die Anzahl, die Reihenfolge und der Typ von Argumenten beim Funktionsaufruf muss immer mit Anzahl, Reihenfolge und Typ der Parameter bei der Funktionsdefinition übereinstimmen. Felder und Zeichenketten werden als Zeiger übergeben. Dabei wird ausgenutzt, dass der Feldbezeichner ein Zeiger auf das erste Element ist. void ausgabe_zeile(char *postitionsstring, int x, int y, float p) { printf( Luftdruck an Position %s (%d,%d): %f \n, postionsstring, x, y, p); ausgabe_zeile( Zugspitze, 51, 13, 998.17); R. Großmann, P. Sobe 117 Funktionen in C (5) Rückgabewert: Funktionen können einen Wert zurückliefern. Das erlaubt Benutzung in Zuweisungen... wert = myfun(a,b); // myfun liefert einen Wert zurück Benutzung einer Funktion als Parameter einer anderen Funktion printf("der Funktionswert an der Stelle %f ist %f \n", x, fun(x) ); Wird die Angabe des Rückgabetyps weggelassen, nimmt der Compiler standardmäßig integer an. Funktionen, die keinen Wert zurückgeben sollen, werden mit den Rückgabetyp void gekennzeichnet. R. Großmann, P. Sobe 118
Funktion Typ des Rückkehrwertes Bei einer C-Funktionsdefinition muss vor dem Funktionsnamen ein Typ (des Rückkehrwertes) angegeben werden. Dieser Typ kann ein in C bekannter Standardtyp, wie z.b. int, unsigned long, char, double,... sein. Aber auch Zeigertypen, wie z.b. float *, int *, char *,... sind zulässig. Es ist darauf zu achten, dass der Typ des Ausdrucks in der return- Anweisung mit dem Typ des Rückkehrwertes kompatibel ist. Wird ein Zeigerwert zurückgegeben, darf die Adresse nicht auf eine lokale Variable der Funktion zeigen. R.Großmann, P. Sobe 119 Funktionen mit Feldern als Parameter Bislang wurde nach Eingabe- und Ausgabeparametern unterschieden: Eingabeparameter in der Parameterliste als Variable (Call-by-Value) Ausgabeparameter als Rückkehrwert, oder als Zeiger in der Parameterliste (Call-by-Reference) Bei Funktionen, die Felder (ein- oder mehrdimensional) als Parameter haben, ist in C/C++ eine Besonderheit zu beachten. Bei der Behandlung des Zeigerkonzeptes wurde festgestellt, dass die Namen von Feldern wie Zeiger zu behandeln sind. Aus diesem Grund wird bei Feldern nicht nach Eingabeparametern bzw. Ausgabeparametern unterschieden, da ohnehin die Adressen der Felder übergeben werden. In der Parameterliste wird ein Feld durch den Typ der Elemente, den Namen und ein Paar eckige Klammern gekennzeichnet, oder alternativ als Zeiger auf den Typ der Feldelemente. R.Großmann, P. Sobe 120
Beispiel mit eindimensionalen Feldern als Parameter Funktion zur Berechnung des arithmetischen Mittels eines Feldes von float-zahlen mit n Elementen: Funktionsdefinition: float amittel(int n, float feld[ ]) // auch float *feld möglich { int i; float s=0.0f; for (i=0;i<n;i++) s=s+feld[i]; // auch *(feld+i) möglich return s/(float)n; Beachte: bei eindimensionalen Feldern kann ein leeres Klammerpaar [ ] angegeben werden. R.Großmann, P. Sobe 121 Beispiel mit eindimensionalen Feldern als Parameter Funktionsaufruf: void main() { int dim; float mittel,messwerte[100]; // Anzahl dim und Messwerte werden // durch Einlesen belegt mittel=amittel(dim, messwerte) ; printf( \narithm.mittel=%f,mittel); R.Großmann, P.Sobe 122
Beispiel mit mehrdimensionalen Feldern als Parameter Funktion zum Transponieren einer quadratischen Matrix mit n² Elementen des Typs integer. Funktionsdefinition: void trans(int n, int ma[ ][10]) { int i,j,h; for (i=0;i<n;i++) for (j=0;j<i;j++) { h=ma[i][j]; ma[i][j]=ma[j][i]; ma[j][i]=h; Beachte: bei mehrdimensionalen Feldern müssen nach dem leeren ersten Klammerpaar [ ] (Anzahl der Zeilen) unbedingt die nachfolgenden Klammerpaare mit Konstanten, die die genaue Anzahl für diese Dimension darstellen (z.b. genaue Anzahl der Spalten usw.) angegeben werden. Die Anzahl bezieht sich auf die Deklarationsgrenzen des übergebenen Argumentes, nicht auf die möglicherweise geringere Anzahl benutzter Elemente! R.Großmann, P. Sobe 123 Beispiel mit mehrdimensionalen Feldern als Parameter Funktionsaufruf: void main() { int j,i,m; int x[10][10]; printf("\nm="); scanf("%d",&m); // m<=10 //Belegung der Matrix x for (i=0;i<m;i++) for (j=0;j<m;j++) {printf("\nx[%d][%d]=",i,j);scanf("%d",&x[i][j]); ; //Transponieren trans(m,x); for (i=0;i<m;i++) for (j=0;j<m;j++) printf("\nx[%d,%d]=%d",i,j,x[i][j]); R.Großmann, P. Sobe 124
Funktionszeiger In C/C++ können Funktionen über Zeiger, die die Aufruf-Adresse der Funktion enthalten, aufgerufen werden. Ein Funktionszeiger wird folgendermaßen deklariert: typ (*f)(); f ist der Name der Funktion und typ der Typ des Rückkehrwertes. Der Funktionszeiger muss aber unbedingt mit der Adresse einer definierten Funktion belegt werden! void main() { int i,dim; float mittel,messwerte[100]; float (*fk)(); //Deklaration eines Funktionszeigers fk fk=&amittel; //Zuweisen der Aufrufadresse der definierten Funktion amittel printf("\nanzahl Werte="); scanf("%d",&dim); for (i=0;i<dim;i++) { printf("\nwert[%d]=",i); scanf("%f",&messwerte[i]); mittel=(*fk)(dim, messwerte) ; //Aufruf der Funktion amittel printf("\narithm.mittel=%f",mittel); R.Großmann, P. Sobe 125 Funktionen als Parameter von Funktionen In C/C++ müssen Funktionen, die als Parameter an andere Funktionen übergeben werden sollen, in der Parameterliste als Funktionszeiger gekennzeichnet werden. Zusätzlich zur normalen Funktionszeigernotation müssen zusätzlich die Typen der Parameter dieser zu übergebenden Funktion aufgeführt sein. In Ergänzung zur Deklaration als Funktionszeiger muss die Parameterliste des Funktionszeigers die Typen der an die Funktion zu übergebenden Parameter enthalten. Beispiel: float (*f)(float) das wäre der Funktionszeiger einer Funktion f(x), die einen float- Parameter x hat und mit einem float-wert zurück kommt (z.b. cos(x) ). Anwendungsbereiche: Funktionen müssen an Funktionen übergeben werden bei Berechnung von beispielsweise Nullstellenberechnung von Funktionen bestimmten Integralen Interpolation von Funktionen R.Großmann, P. Sobe 126
Beispiel für Funktionen als Parameter von Funktionen float funkt(float x) { return cos(x); float halb(float a,float b, float (*f)(float)) //Halbierungsverfahren zur Nullst.-berechnung { float m; m=(a+b)/2.0f; while (fabs((*f)(m))>0.00001) { m=(a+b)/2.0f; if ((*f)(m)*(*f)(b)<0.0f) a=m;else b=m; return m; void main() { float a,b,nullst; printf("halbierungsverfahren\n"); printf("eingabe Intervall\na="); scanf("%f",&a); printf("\nb="); scanf("%f",&b); nullst=halb(a,b,funkt); //Übergabe der Funktion funkt printf("\nnullstelle=%f\n",nullst); R.Großmann, P. Sobe 127 Rekursive Funktionen (1) In C/C++ können Funktionen rekursiv definiert werden. Das kann in direkter oder indirekter Form geschehen. Bei einer direkten Rekursion enthält die Funktionsdefinition einen Aufruf von sich selbst, während im indirekten Fall eine andere Funktion aufgerufen wird, die wiederum, die zu definierende Funktion ruft. Da die Verwaltung aller lokalen Größen ohnehin durch den Compiler in einem Runtime-Stack vorgenommen wird, muss der Programmierer bei der Definition rekursiver Funktionen nichts besonderes beachten. Allerdings müssen stets Anweisungen vorhanden sein, die den rekursiven Aufruf begrenzen, um ein Endlos-Aufrufen zu verhindern. R.Großmann, P. Sobe 128
Rekursive Funktionen Beispiel ( vgl. rekursive Algorithmen bei Struktogrammen FIBONACCI-Zahlen): fibo(n)= fibo(n-1) + fibo(n-2) Rekursionsabbruch fibo(1)=1 fibo(0)=1 int fibo(int n) //rekursive Definition { if (n<2) return 1;else return (fibo(n-1)+fibo(n-2)); void main() { int i=1,f=1; printf("\nberechnung der FIBONACCI-Zahlen im Intervall [0,100]"); printf("\n\n x fibo(x)\n------------"); while ((f=fibo(i))<=100) { printf("\n%2d %3d",i,f); i++; R.Großmann, P. Sobe 129 Funktionen - Zusammenfassung Eine Funktion besteht aus Rückgabetyp, Funktionsname, Parameterliste und einem Anweisungsblock, der in geschweifte Klammern {, eingefasst wird. Rückgabetyp kann jeder gültige Typ sein, auch selbstdefinierte Typen, Strukturen oder Zeiger. Die Anweisung return dient zur Rückgabe eines Wertes aus der Funktion. Die Ausführung wird dann mit der Anweisung fortgesetzt, die dem Funktionsaufruf folgt. Funktionen, die keinen Wert zurückgeben, erhalten den Rückgabetyp void. Funktionen können wie Variable gehandhabt werden. Funktionen können so als Parameter anderen Funktionen übergeben werden. Funktionen können sich selbst aufrufen: Rekursion R. Großmann, P. Sobe 130