7. Grundlagen von Funktionen

7. Grundlagen von Funktionen
Funktionen in C++
Bestandteile eines C++ - Programms
C++ Kern
Standardbibliothek
Anwender
Sonstiges
Dr. Norbert Spangler /Programmieren
vordefinierte Datentypen, Operatoren, Befehle
vordefinierte Funktionen (und Klassen)
selbsterstellte Funktionen (und Klassen)
externe Bibliotheken
21.11.2015
2
Funktionen in C++: Standardbibliothek
Mathematische Funktionen mittels #include <cmath>
abs (x*x-a) >=1.0e-10 sqrt(summe)
Argument und Wert z.b. double
Weitere Funktionen in cmath: exp log log10 pow asin sin
cos tan acos atan sinh cosh tanh …
rand() ganzzahlige Zufallszahl
Argument keines, Funktionswert int
strlen(ca) Zeichenzahl eines Character-Arrays
Argument Character-Array, Funktionswert int
cin.eof() wahr/falsch bei Dateiende
Dr. Norbert Spangler /Programmieren
21.11.2015
3
Funktionen in C++: Anwender-Funktionen
Eigene Formeln
p-q-Formel bei quadratischen Gleichungen
Argumente: p und q
Funktionswert : eine Lösung
loesung = pqFormel(p,q)
Suchen eines Elements in einem Array
Argumente: Arrayelemente + Anzahl der Elemente + Suchelement
Funktionswert : gefundener Index (Stelle)
stelle = suche(anzahl,array, suchelement)
Sortieren der Elemente eines Arrays
Argumente: Arrayelemente + Anzahl der Arrayelemente
Funktionswert : void
Regel:
sortiere(anzahl,array)
Funktionen machen keine Ausgaben
Eingabe von Daten
am Bildschirm, es sei denn,
Argumente: Daten
es handelt sich um eine Ausgabefunktion
Funktionswert : void oder bool
Ausgabe von Daten
Argumente: Daten
Funktionswert : void
Dr. Norbert Spangler /Programmieren
21.11.2015
4
Motivation: wofür?
Funktionen lösen spezielle (Teil-)Aufgaben
mathematische Formeln
spezielle Algorithmen: Suchen, Sortieren, Gleichungen, Auswertungen
Ein-/Ausgabe für Tastatur, Bildschirm, Dateien usw.
Steuerungen von Geräten, Anlagen,…
Funktionen sind Zusammenfassungen von Arbeitspaketen und bieten eine
Möglichkeit zur Verteilung des Programmieraufwands auf Teams
Voraussetzung
Die Schnittstellen (d.h. die Kommunikation) der Funktionen
untereinander sind klar festgelegt bzw. beschrieben.
-> Welche Daten werden benötigt, um die Funktion auszuführen ?
-> Welche Daten/Ergebnisse liefert die Funktion?
Dr. Norbert Spangler /Programmieren
21.11.2015
5
Motivation: Programmqualität
Funktionen sind damit „eigenständige" Programmteile innerhalb einer
Anwendung zur
- Strukturierung/Beherrschung der Komplexität (Komponenten),
- Lösung von Teilaufgaben,
- Arbeitsorganisation d.h. Aufteilung auf Teams,
- Verbesserung der Übersichtlichkeit und Lesbarkeit,
- Verbesserung des Testens,
- Wiederverwendung (z. Bsp. Ein-/Ausgabe von Arrays),
- Archivierung in Bibliotheken,
- Übersetzung: jede Funktion wird durch einen eigenen Compilerlauf bearbeitet.
Damit könnten auch unterschiedliche Programmiersprachen verwendet werden
Funktionen erhöhen die Programmqualität
Dr. Norbert Spangler /Programmieren
21.11.2015
6
7.1 Beispiel einer Anwender-Funktion: Musterprogramm
Wurzel berechnen
Wurzel berechnen
Eingabe a
Eingabe a
a<=0
a <= 0
Ja
a muss >0
sein
ja
Nein
nein
a muss > 0 sein
Ausgabe wurzel(a)
x=a
Solangex*x-a ungenau
Wurzel(a)
x*x-a
x = x - ------2x
Ausgabe x
Das "große unübersichtliche" Struktogramm wird ersetzt
durch "kleine" übersichtliche" für jede Funktion, sowie
das eigentliche Programm:
- Eingabe mit Plausibilitätskontrolle->einfach zu testen
- „eigentliche“ Aufgabe->keine unzulässigen Daten mehr
Dr. Norbert Spangler /Programmieren
x=a
Solangex*x-a ungenau
x*x-a
x = x - ------2x
Ergebnis x
21.11.2015
7
main mit Verwendung einer Funktion wurzel
// Wurzel berechnen
#include <iostream>
using namespace std;
void main()
{
double radikand;
double wurzel(double);
cout <<"\n radikand eingeben ";
cin>>radikand;
//Plausibilitaetskontrolle
if ( radikand<0 )// unzulaessige Eingabe
cout <<"\n darf nicht < 0 sein ";
else
//Ergebnis
cout <<"\n Wurzel von "<<radikand
<<" = "<< wurzel(radikand)
<<endl;
}
Dr. Norbert Spangler /Programmieren
Funktion1: main
Prototyp:
Funktionstyp ist double
1 Parameter vom Typ double
Dadurch ist wurzel in main bekannt
und kann verwendet werden.
Aufruf
Hier wird wurzel verwendet.
aktuelle Parameter
Mit diesen Daten aus dem main soll
wurzel arbeiten
21.11.2015
8
Die Funktion wurzel
formale Parameter (Platzhalter, lokale Variable der Funktion wurzel)
double wurzel(double a)
{
const double genauigkeit=0.001;
double x=a;
if (a==0.0)
return 0.0;
else
while ( abs (x*x-a)>genauigkeit );
x=x-(x*x-a)/(2*x);
return x;
}
Funktionskopf
Definition
Funktionsblock
Ergebnis (Funktionswert)
Dr. Norbert Spangler /Programmieren
21.11.2015
9
Dateien
Üblicherweise sollte man jede Funktion innerhalb eines
Projekts in einer eigenen Datei speichern.
Anmerkung: es geht auch mit einer Datei.
main
Funktion
in Datei main.cpp
in einer Datei func.cpp
Compiler erzeugt main.obj
Compiler erzeugt func.obj
Linker
projekt.exe-Datei
Dr. Norbert Spangler /Programmieren
21.11.2015
10
7.2 Aufgabenstellung
Prinzipielle Ausgangssituation:
main (ist eine benutzerdefinierte Funktion); sie verwendet
wurzel (ist eine weitere benutzerdefinierte Funktion), diese verwendet
abs (ist eine bereitgestellte Funktion)
Allgemein:
Eine Funktion function1 verwendet eine andere Funktion function2.
Dr. Norbert Spangler /Programmieren
21.11.2015
11
Aufgabenstellung: Anforderungen
Situation: funktion1 verwendet funktion2
z.B. funktion1 geschrieben von Programmierer/Team a
funktion2 geschrieben von Programmierer /Team b
Wie erkennt funktion1 dass es funktion2 gibt?
Verfügbarkeit:
Was passiert wenn funktion2 nicht zur Verfügung steht?
Wie kann funktion1 programmiert werden wenn funktion2 nicht verfügbar ist?
Kann funktion2 programmiert/getestet werden wenn funktion1 nicht existiert?
Schnittstellen:
Wie erfährt funktion2, mit welchen Daten aus funktion1 sie arbeiten muss?
Wie wird das Ergebnis von funktion2 an funktion1 übermittelt?
Bei Mehrfachverwendung von funktion2 in funktion1:
An welcher Stelle in funktion1 geht es weiter?
Dr. Norbert Spangler /Programmieren
21.11.2015
12
Schnittstellen funktion1(main) – funktion2 (f)
main
f(t)
Programmcode
y=f(x);
Sprung
Rücksprung/Ergebnis Stelle 1
z=f(h)+1;
Rücksprung/Ergebnis Stelle 2
return ergebnis
x
y
h
z
Variante 1: Wert ausrechnen kopieren
Daten funktion 2
Daten funktion 1
Programmcode
Sprung
t
ergebnis
Variante 2: Adresse verraten
t wird an derselben Stelle gespeichert
wie h
Dr. Norbert Spangler /Programmieren
21.11.2015
13
Erfüllung der Anforderungen/1
Wie erkennt funktion1 dass es funktion2 gibt?
Ein Hinweis ist erforderlich: Prototyp
Was passiert wenn funktion2 nicht zur Verfügung steht?
Fehlermeldung des Linkers
Wie kann funktion1 programmiert werden wenn funktion2 nicht
verfügbar ist?
Man erstellt eine einfache Vorabversion von funktion2 mit korrekten
Schnittstellen aber ohne wesentliche Funktionalität: Stub
Kann funktion2 programmiert/getestet werden wenn funktion1 nicht
existiert?
Man erstellt ein separates Testprogramm für funktion2: Treiber
Dr. Norbert Spangler /Programmieren
21.11.2015
14
Erfüllung der Anforderungen/2
Wie erfährt funktion2, mit welchen Daten aus funktion1 sie arbeiten muss?
Bei der Verwendung der Funktion (Aufruf) müssen diese mittels Parameter
übermittelt werden. Hierzu gibt es unterschiedliche Techniken
(durch Kopien oder mittels Adressen).
Wie wird das Ergebnis von funktion2 an funktion1 übermittelt?
Hierzu gibt es unterschiedliche Techniken.
Die einfachste Variante ist mittels des Returnbefehls: return Funktionswert;
Alternative: Ändern von Parametern (später)
Bei Mehrfachverwendung von Funktion2:
An welcher Stelle in funktion1 geht es weiter?
Die Stelle (Rücksprungadresse) muss beim Wechsel vom Programmcode
von funktion1 in den von funktion2 gemerkt und bei der Rückkehr verwendet
werden. Dies wird automatisch erledigt.
Dr. Norbert Spangler /Programmieren
21.11.2015
15
Weitere Aspekte
Eine Funktion hat einen Wert (Typ)
Prinzipiell ist jeder Typ zulässig, also auch int, short,char,….
Hat eine Funktion "keinen Wert" wird als Typ void verwendet.
Funktionen vom Typ void werden direkt aufgerufen
Beispiel : tausche(x,y);
sortiere(n,x);
ausgabeArray(n,a);
Funktionen mit Typ erscheinen als R-Wert
Beispiel: w=wurzel(radikand) ;
cout<<wurzel(b*b-4*a*c) <<endl;
Eine Funktion hat kann beliebig viele Parameter haben
Parameter werden durch Komma getrennt
Beispiel wurzel(a,genauigkeit)
allgemein funktionsname(parameter1,parameter2,.....,parameter_n)
aber auch void main() //ohne Parameter
int rand();// Typ int aber keine Parameter
Dr. Norbert Spangler /Informatik
21.11.2015
16
7.3 Programmierung von Funktionen
Die Funktion sollte sinnvollerweise in einer eigenen cpp-Datei im Projekt
stehen. Daher ist zu main eine weitere cpp-Datei hinzuzufügen.
Der Name kann beliebig gewählt werden; üblich ist Funktionsname.cpp.
Diese Datei enthält dann die Funktionsdefinition.
Sie besteht aus dem Funktionskopf und dem Funktionsblock.
Je nach Größe des Projekts bzw. Anzahl oder Umfang der Funktionen kann
man auch mehrere Funktionsdefinitionen in einer Datei zusammenfassen.
Ziel sollte generell die Übersichtlichkeit des Gesamtprogramms sein.
Dr. Norbert Spangler /Programmieren
21.11.2015
17
Funktionsdefinition = Funktionskopf + Funktionsblock
Angabe des Funktionskopfs
Typ der Funktion
entspricht dem Typ des return-Wertes
Name der Funktion
wie ein Variablenname
Deklarationsliste
Typ und Name der Parameter
genauer: formale Parameter (Platzhalter)
Allgemeine Funktionsdefinition:
typfunktion namefunktion(typ1 p1,…,typn pn)
Beispiele:
double wurzel(double a) //1 Parameter
double wurzel(double a, double genauigkeit) //2 Parameter
double loesung1(double a, double b, double c) //quadratische Gleichung - 3 Par.
int rand() //kein Parameter- ganzzahlige Zufallszahl
void ausgabeKreisdaten( float radius) // Typ der Funktion void
Dr. Norbert Spangler /Programmieren
21.11.2015
18
Funktionsdefinition = Funktionskopf + Funktionsblock
Funktionsblock
{ Befehle der Funktion}
In einem Paar geschweifter Klammern stehen die Befehle
der Funktion.
Er kann alle bekannten Befehle enthalten. Insbesondere ist es möglich, neu benötigte
Größen zu deklarieren. Diese sind dann lokale Größen im Funktionsblock und nach
Verlassen des Blocks, d.h. nach Beendigung der Funktion, nicht mehr existent.
Für die formalen Parameter im Funktionskopf , wie sie bisher dargestellt wurden,
werden ebenfalls lokale Variable deklariert.
Eine Funktion wird mittels return beendet. Danach erfolgt der Rücksprung in die
aufrufende Funktion. Hierbei gibt es 2 Varianten.
Dr. Norbert Spangler /Programmieren
21.11.2015
19
Beendigung der Funktion mit return
Wenn die Funktion vom Typ void ( z.B. void main() , void ausgabeKreisdaten(...)
ist, wird sie üblicherweise mittels return ohne zusätzlichen Angaben beendet.
Beispiel:
if ( a< 0 )
{
cout<<"Fehler: unzulaessige Eingabe";
return;
}
Ein return am Ende des Funktionsblocks, d.h. am Ende der Funktion kann
auch weggelassen werden.
Dr. Norbert Spangler /Programmieren
21.11.2015
20
Beendigung der Funktion mit return wert
Wenn die Funktion nicht vom Typ void ist, dann muss sie immer mit
return wert;
beendet werden. Dabei ist wert ein Ausdruck vom selben Typ wie die
Funktion also etwa im Falle des Typs double ( wie bei wurzel) :
return 0.0; //Konstante vom Typ double
return x; // Variable vom Typ double
return x*x-1; //Ausdruck vom Typ double
Der Funktioswert funktion(Parameter) ist dann als R-Wert zu verwenden.
Dr. Norbert Spangler /Programmieren
21.11.2015
21
Definition/Beispiel 1
#include<cmath>
using namespace std;
double wurzel(double a)
{
const double genauigkeit=0.001;
double x=a;
if (a==0.0)
return 0.0;
else
{
while ( abs (x*x-a)>genauigkeit )
x=x-(x*x-a)/(2*x);
}
return x;
}
Dr. Norbert Spangler /Programmieren
Wegen Verwendung von abs
Achtung:
Alle hier vorkommenden Größen sind
lokale Größen der Funktion
a
genauigkeit
x
21.11.2015
22
Definition: weitere Beispiele/1
#include <string>
using namespace std;
bool ist_palindrom(string s)//Typ bool
{
int laenge=s.length();
for ( int i=0;i<laenge/2;i++)
if ( s[i]!=s[laenge-1-i] )
return false;
return true;
}
Überprüft, ob eine Variable vom Typ
string ein Palindrom enthält.
Lokale Variable:
s
laenge
i
Lokale Variable:
radius
pi
void ausgabeKreisdaten(float radius)
{
float pi=3.14159f;
cout<<"Radius "<<setw(10)<<setprecision(3)<<fixed<<radius<<endl;
cout<<"Durchmesser "<<setw(10)<<setprecision(3)<<fixed<<radius*2<<endl;
cout<<"Umfang
"<<setw(10)<<setprecision(3)<<fixed<<(2*radius*pi)<<endl;
cout<<"Flaeche "<<setw(10)<<setprecision(3)<<fixed<<(radius*radius*pi)<<endl;
}
Dr. Norbert Spangler /Programmieren
21.11.2015
23
Definition: weitere Beispiele/2
#include <string>
using namespace std;
string notentext(int note)
{
if ( note==1 )
return "sehr gut";
else if ( note==2 )
return "gut";
else if ( note==3 )
return "befriedigend";
else if ( note==4 )
return "ausreichend";
else if ( note==5 )
return "mangelhaft";
else
return "unbefriedigend";
}
Dr. Norbert Spangler /Programmieren
int rabatt_prozent(int menge)
{
if ( menge<10 )
return 0;
else if ( menge<50 )
return 5;
else if ( menge<100 )
return 10;
else
return 20;
}
21.11.2015
24
7.4 Verwendung einer Funktion/Aufruf
Will man in einer funktion1 (z.B. main) eine andere funktion2
(z.B. wurzel oder sin ) verwenden, so muss man sie
im Falle von Standardfunktionen mittels include bereitstellen bzw.
im Falle eigenentwickelter Funktionen dem Compiler zur
Plausibilitätskontrolle alle relevanten Informationen mitteilen wie
- Name der Funktion
- Typ der Funktion ( genauer des Ergebnisses)
- Anzahl der Parameter
- Typ der Parameter
- Reihenfolge der Parameter
Dies geschieht durch die Angabe des Prototyps.
Dr. Norbert Spangler /Programmieren
21.11.2015
25
Prototyp (Deklaration)
Der Prototyp ist ähnlich dem Funktionskopf in der Definition.
Es kann auf die Angabe der Namen der Parameter verzichtet werden. Der Typ ist
jedoch erforderlich.
Der Prototyp muss vor der ersten Verwendung angegeben sein.
Form:
typderfunktion namederfunktion(parametertyp1,…,parametertypn);
Beispiele:
double wurzel(double);
int rabatt(int);
bool ist_palindrom(string);
int rabatt_prozent(int);
string notentext(int);
void ausgabeKreisdaten(float]);
Dr. Norbert Spangler /Programmieren
21.11.2015
26
Aufruf der Funktion
Beim Aufruf der Funktion müssen die (formalen) Parameter durch die
aktuell zu verwendenden Parameter (Ausdrücke) ersetzt werden.
Es gibt 2 Arten des Aufrufs:
Für Funktionen vom Typ void:
Die Funktion wird mit aktuellen Parametern direkt hingeschrieben:
ausgabeKreisdaten(r);
Für Funktionen mit Typ:
Die Funktion hat damit einen Wert, welcher wie ein R-Wert
verwendet wird.
Dr. Norbert Spangler /Programmieren
21.11.2015
27
Aufruf der Funktion/ Beispiele
Für Funktionen mit Typ:
Die Funktion hat damit einen Wert, welcher wie ein R-Wert
verwendet wird.
summe=bestellmenge*stueckpreis; //Berechnung Rabattbetrag
rabatt=summe * rabatt_prozent(bestellmenge) /100;
cout<<" Note "<<n<<" ist "<<notentext(n)<<endl;
void main()//Treiber Palindrom
{
string text;
bool ist_palindrom(string);
cout<<" Text ";
cin>>text;
if ( ist_palindrom(text) )
cout<<text<<" ist Palindrom"<<endl;
else
cout<<text<<" ist Palindrom"<<endl;
} Dr. Norbert Spangler /Programmieren
21.11.2015
28
Parameterübergabe
Beim Aufruf der Funktion wird (lokaler) Speicherplatz bereitgestellt für
formale Parameter, sonstige lokale Variable, und die Rücksprungadresse
Es werden die aktuellen Parameter ausgewertet (Ausdrücke).
Diese Werte werden auf die lokalen Variabler für die formalen Parameter
kopiert.
Technik der Parameterübergabe : Call by Value
Die Funktion arbeitet also immer mit Kopien und nie mit Variablen aus der
aufrufenden Funktion (=Originaldaten). Damit können die aktuellen
Parameter aus der aufrufenden Funktion nicht geändert werden (da nicht
bekannt)! Sollte dies erforderlich sein müssen andere Techniken gewählt
werden (->später).
Nachteilig ist, dass Parameter mit hohem Speicherbedarf dupliziert werden.
Dr. Norbert Spangler /Programmieren
21.11.2015
29
Prinzipieller Ablauf des Aufrufs
main
Aufruf, Rücksprungadresse merken
wurzel(b*b-4*a*c);
Aktuelle Parameter auswerten und auf formale Parameter kopieren
wurzel( double a );
Funktionsblock ausführen
(lokale Objekte : a, genauigkeit, x)
Rücksprungadresse holen, Wert mittels return übergeben
main
Mit Wert/Ergebnis arbeiten
Dr. Norbert Spangler /Programmieren
21.11.2015
30
7.5 Test einer Funktion
Funktionen werden in der Regel separat getestet mittels eines
zugehörigen Testprogramms (Treiber).
Dabei sind anstelle der bisher verwendeten „Eingaben“ jetzt
üblicherweise die Parameter zu variieren. Da aber diese stets einen fest
vorgegebenen Typ haben, können hier keine „verkehrten“ Datentypen
vorkommen.
Damit entfallen Testbeispiele mit unzulässigen Daten, wenn dies die
Datentypen betrifft. Es sind allenfalls „unsinnige“ Daten zu betrachten
wie negative Anzahlen etc.
Der Test einer Funktion ist daher in der Regel einfacher als der eines
Programms mit Eingaben und Plausibilitätskontrollen.
Dr. Norbert Spangler /Programmieren
21.11.2015
31