Eine Einführung in die Programmiersprache C++

Werbung
Eine Einführung in die Programmiersprache C++
Thomas Kastl
5. April 2004
Die folgenden Ausführungen zu C++ stellen keine vollständige Beschreibung dieser Programmiersprache dar. Es sind hauptsächlich die Bestandteile der Sprache beschrieben,
die auch in der Programmiersprache C zu finden sind.
1
Compiler
Um von einer Problemstellung zu einem lauffähigen Programm zu gelangen, sind mehrere
Schritte notwendig.
Der Programmtext (Quellcode) muss zunächst mit einem Texteditor (nedit, emacs, ...)
in Form einer ASCII-Datei (Quelldatei) gespeichert werden. Der Dateiname sollte dabei
mit .cpp enden.
Danach wird der Compiler (Übersetzer) unter Angabe der Quelldatei(en) aufgerufen
$ CC kreis.cpp -o kreis
Hierbei übersetzt der Compiler (CC) die Quelldatei kreis.cpp in ein ausführbares Programm (“Executable”) mit dem Namen kreis.
Die Erzeugung des ausführbaren Programmes führt der Compiler in mehrerem Phasen
durch. Die Dateien werden zuerst vom Präprozessor bearbeitet, d.h. es wird die sogenannte Makroverarbeitung ausgeführt. Dabei werden sämtliche Präprozessor-Kommandos
(beginnend alle mit #) durch entsprechenden Programmcode ersetzt. Die Anweisung
#include <iostream.h>
bewirkt etwa, dass diese Zeile durch die gesamte Header-Datei iostream.h ersetzt wird.
Das Ergebnis dieses “Preprocessing”, der interne Quelltext, wird schließlich von dem
eigentlichen Compiler weiterverarbeitet.
1
2 GRUNDLAGEN
Getrennte Übersetzung
Für größere Projekte ist es sinnvoll den Programmcode auf mehrere Quelldateien aufzuteilen. Die physische Trennung eines Programms in getrennte Dateien sollte dabei von der
logischen Struktur des Problemes geleitet werden.
Der Compiler übersetzt nun jede einzelne Quelldatei für sich. Dabei ist zu beachten,
dass der Compiler beim Übersetzen einer Datei nichts vom Inhalt der anderen Dateien
weiß. Um eine getrennte Übersetzung zu ermöglichen, muss der Programmierer daher
Deklarationen für Funktionen zur Verfügung stellen.
Nach dem Übersetzen aller Quelldateien wird der sogenannte Linker (Binder) gestartet.
Der Linker ist ein Programm, das die getrennt übersetzten Teile zusammenbindet.
2
Grundlagen
Mit dem folgenden C++-Quellcode kann ein Programm generiert werden, dass den Umfang
und die Fläche eines Rechtecks berechnet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// rechteck.cpp
21.11.2002
/*
berechnet den Umfang und die Flaeche eines Rechteckes
nach der Eingabe von Laenge und Breite
*/
#include <iostream.h>
// zusaetzliche Funktionen
/* Hauptprogramm */
int main()
{
float laenge, breite;
// Variablendeklaration
cout << "Umfang- und Flaechenberechnung eines Rechteckes\n\n";
// Eingabe von Laenge und Breite
cout << "Bitte die Laenge eingeben: ";
cin >> laenge;
cout << "Bitte die Breite eingeben: ";
cin >> breite;
2
2 GRUNDLAGEN
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
if ((laenge > 0) && (breite > 0))
{
float flaeche, umfang;
// Variablendeklaration
// Flaeche und Umfang berechnen
flaeche = laenge * breite;
umfang = 2.0 * (laenge + breite);
// Resultate ausgeben
cout << endl;
cout << "Umfang = " << umfang << endl;
cout << "Flaeche = " << flaeche << endl;
}
else
{
cout << endl;
cout << "Falsche Eingabe !!!\n";
}
}
Die Ausführung des “Executable” erzeugt auf dem Bildschirm den folgendem Dialog zwischen Anwender und Computer:
Umfang- und Flaechenberechnung eines Rechteckes
Bitte die Laenge eingeben: 3
Bitte die Breite eingeben: 4
Umfang = 14
Flaeche = 12
Kommentare
In C++ werden Kommentare mit zwei aufeinanderfolgenden Schrägstrichen (//) eingeleitet, d.h. der Compiler ignoriert alle folgenden Zeichen bis zum jeweiligen Zeilenende.
Zusätzlich können Passagen von beliebiger Größe durch /* ... */ auskommentiert werden (Zeilen 3-6).
Struktur eines C++-Programmes
Ein einfaches C++-Programm hat die folgende Struktur:
3
3 DATENTYPEN, VARIABLEN, KONSTANTEN
#include <Headerdatei>
// Header-Datei(en) einbinden
Datentyp Funktion(Parameterliste)
{
Anweisungen;
}
// Funktionsdefinition
int main()
{
Anweisungen;
}
// Hauptprogramm
Die Anweisung #include teilt dem Präprozessor mit, dass eine sogenannte HeaderDatei eigefügt werden soll. Diese enthält u.a. Funktionsdeklarationen (vgl. Abschnitt 9.3).
Aufgrund deren Einbindung können in der aktuellen Quelldatei zusätzlichen Funktionen
verwendet werden.
Die Funktion main() muss in jedem C++-Programm genau einmal vorkommen, denn bei
der Ausführung eines Programms wird genau diese Funktion abgearbeitet. Alle Anweisungen, die in einer Funktion ausgeführt werden sollen, müssen in einem Block zusammengefasst werden. Blöcke werden in C++ von geschweiften Klammern ({ ... }) umschlossen.
C++ ist eine formatfreie Sprache, d.h. zusätzlich eingefügte Füllzeichen (Leerzeichen,
Tabulatoren, Zeilenumbrüche) haben keine Auswirkungen auf den Quellcode. Damit der
Compiler erkennt, an welcher Stelle eine Anweisung aufhört, müssen diese mit einem
Semikolon (;) abgeschlossen werden. Außerdem ist C++ case-sensitive, d.h. es wird
zwischen Groß- und Kleinschreibung unterschieden.
Um einen wiederholt auszuführenden Programmteil auszulagern, können in einem C++Programm Funktionen definiert werden. Diese werden in Abschnitt 9 näher vorgestellt.
3
Datentypen, Variablen, Konstanten
Eine Variable bezeichnet einen Hauptspeicherbereich, in dem man zu unterschiedlichen
Zeitpunkten beim Programmablauf unterschiedliche Werte abspeichern kann.
Variablenzuweisung
Mit der Zuweisung
laenge = 5.3;
wird der Wert 5.3 in der Variablen laenge gespeichert. Zu einem späteren Zeitpunkt kann
mit
4
3 DATENTYPEN, VARIABLEN, KONSTANTEN
laenge = 12.6;
der Wert der Variablen auf 12.6 abgeändert werden.
Deklaration von Variablen
C++ ist eine typbasierte Sprache, d.h. jede Variable besitzt einen Datentyp, der vor
ihrer ersten Verwendung bestimmt werden muss. Mit der Deklaration einer Variablen
wird dem Compiler der Datentyp dieser Variablen mitgeteilt. Der Compiler kann nun
dafür sorgen, dass ein dem Datentyp entsprechender Speicherbereich für diese Variable
reserviert wird. Mit der Anweisung
float laenge;
wird die Variable laenge vom Fließkommadatentyp float deklariert. Bei der esten Zuweisung (Initialisierung der Variablen) wird deshalb ein Speicherbereich von 32 Bit für
das Datenobjekt laenge reserviert.
Es stehen in C++ eine Vielzahl von elementaren Datentypen zur Verfügung:
Datentyp
char
short int
int
unsigned int
long int
Bit
8
16
(16)32(64)
16
32(64)
Min.
-128
-32768
-2147483648
0
-2147483648
Max.
127
32767
2147483647
65535
2147483647
Tabelle 1: Ganzzahldatentypen
Die Datenbitbreite für die Typen int und long int hängt vom verwendeten Compiler
und Betriebssystem ab.
Datentyp
float
double
long double
Bit
32
64
80
Min.
ca. 3.4E-38
ca. 1.7E-308
ca. 1.2E-4932
Max.
ca. 3.4E38
ca. 1.7E308
ca. 1.2E4932
Tabelle 2: Fließkommadatentypen
Zusätzlich können auch eigene Datentypen entwickelt werden.
Mehrere Variablen vom gleichen Typ können durch Kommas (,) getrennt in einer Zeile
deklariert werden:
float laenge, breite, hoehe;
5
4 EIN- UND AUSGABEFUNKTIONEN
Initialisierung von Variablen
Die Initialisierung einer Variablen kann direkt bei ihrer Deklaration erfolgen
float durchmesser = 10.6;
oder sich erst zu einem späteren Programmzeitpunkt ergeben:
float durchmesser, radius;
...
durchmesser = 2.0 * radius;
...
// Deklaration
// Initialisierung (erste Zuweisung)
Der Gültigkeitsbereich einer Variablen reicht bis zum Ende des Blocks, in dem sie deklariert wurde. Sie gilt zusätzlich in allen Blöcken, die innerhalb dieses Blockes nach der
Deklaration der Variablen auftreten. Außerhalb dieses Blockes ist die Variable unbekannt.
Gültige Namen
Zur Konstruktion von Namen für Variablen, Konstanten oder Funktionen sind in C++ nur
alphanumerische Zeichen (A..Z, a..z, 0..9), sowie der Unterstrich (_) erlaubt. Zusätzlich
darf ein gültiger Name nicht mit einer Ziffer beginnen. Groß- und Kleinschreibung wird
unterschieden.
Konstanten
Werte, die sich während des Programmablaufes nicht mehr verändern sollen, können
in C++ als Konstanten definiert werden. Konstanten werden mit dem vorangestellten
Schlüsselwort const wie Variablen deklariert und müssen sofort initialisiert werden:
const float
PI = 3.14159;
Der dem Namen zugewiesene wert kann nach der Deklaration nirgendwo mehr im Programm verändert werden.
4
Ein- und Ausgabefunktionen
Unter C++ steht eine komfortabele Ein- und Ausgabe mittels Datenströmen (streams)
zur Verfügung.
6
4 EIN- UND AUSGABEFUNKTIONEN
Standardausgabe
Mit der Anweisung
cout << "Bitte eine Zahl eingeben: ";
folgt die Ausgabe
Bitte eine Zahl eingeben:
auf der Standardausgabe (d.h. i.a. auf dem Bildschirm). Der Einschiebeoperator (<<)
“schiebt” dabei den auszugebenden Text “Bitte eine Zahl eingeben: “ zum Standardausgabegerät.
Mit cout kann auch der Inhalt beliebiger Variablen auf der Standardausgabe abgebildet
werden. Die Anweisungen
float zahl = 45.323;
cout << zahl;
liefern
45.323
auf der Standardausgabe. Eine Formatangabe wird dabei nicht benötigt. Ebenfalls können
auch mehrere Datenströme hintereinander zur Standardausgabe “geschoben” werden. Die
Anweisungen
int ganze_zahl = 23;
float fliesskommazahl = 423.3423;
cout << "Zahlentypen in C++: \n" << endl
<< "Ganze Zahl: " << ganze_zahl << endl << "Fliesskommazahl: "
<< fliesskommazahl << endl;
führen zur Ausgabe:
Zahlentypen in C++ :
Ganze Zahl: 23
Fliesskommazahl: 423.3423
Mit dem Befehl endl oder mit der Zeichenkette "\n" kann Zeilenumbruch erzwungen
werden.
7
5 OPERATOREN UND AUSDRÜCKE
Standardeingabe
Bei dem Gegenstück zu cout, der Anweisung
cin >> zahl;
wird eine Eingabe vom Standardeingabegerät (i.a. der Tastatur) zur angegebenen Variablen zahl “geschoben”. Man beachte, dass der Einschiebeoperator (>>) hierbei in Richtung der Variablen zeigt. Ist die Tastatur als Standardeingabegerät definiert, so erwartet
das Programm an dieser Stelle eine Eingabe vom Anwender. Alle weiteren Anweisungen
werden vom Programm erst dann ausgeführt, wenn der Anwender seine Eingabe mit der
Return-Taste abgeschlossen hat.
Die Eingabe wird nicht automatisch auf ihre Gültigkeit hin untersucht, d.h. der Programmentwickler muss dafür sorgen, dass eventuell “unsinnige” Eingaben vom Programm
abgefangen werden.
Die Befehle für die Ein- und Ausgabe gehören nicht zum Grundwortschatz von C++ .
Daher muss am Anfang mit der Präprozessor-Anweisung
#include <iostream.h>
die Header-Datei iostream.h in die Quelldatei eingebunden werden. Nach dem Einbinden
der Header-Datei iomanip.h stehen zusätzlich noch einige Ausgabe-Manipulatoren zur
Verfügung. Die Anweisung
cout << setprecision(9);
etwa legt eine Ausgabe von insgesamt 9 Stellen für Fließkommazahlen fest. Mit
cout << setw(10) << zahl;
kann das Eingabefeld für zahl auf eine Breite von 10 Zeichen beschränkt werden.
5
Operatoren und Ausdrücke
In seiner einfachsten Form besteht ein Ausdruck aus einem Wert oder einer Variablen.
In dieser Form wird er auch Primärausdruck genannt.
Ausdrücke können durch Operatoren miteinander verknüpft werden. Ein in dieser Weise
entstandenes Konstrukt bildet wiederum einen Ausdruck. Ausdrücke, auf die ein Operator
angewendet wird, werden die Operanden des Operators genannt.
8
5 OPERATOREN UND AUSDRÜCKE
5.1 Arithmetische Operatoren
In C++ existieren eine Vielzahl von Operatoren mit denen Ausdrücke verknüpft oder manipuliert werden können. Manche Operatoren können allerdings nicht auf beliebige Ausdrücke angewendet werden, so sind für Operanden solcher Operatoren z.B. nur Variablen
von einem bestimmten Datentyp zugelassen.
Operatoren können nach verschiedenen Gesichtspunkten klassifiziert werden. Zum einen
besitzt jeder Operator eine Prioritätsstufe, die angibt, in welcher Reihenfolge Operatoren ausgewertet werden, wenn mehrere Operatoren in einem Ausdruck enthalten sind.
Operatoren mit einer höheren Priorität werden immer vor Operatoren mit einer niedrigeren Priorität abgearbeitet. Jede Prioritätsstufe besitzt zusätzlich eine festgelegte Abarbeitungsrichtung. Diese gibt an, in welcher Reihenfolge Operatoren gleicher Prioritätsstufe innerhalb eines Ausdrucks ausgewertet werden.
Ein weiteres Unterscheidungsmerkmal ist der Typ eines Operators. Der Typ eines Operator bezieht sich auf die Anzahl seiner Operanden. Unäre Operatoren wirken nur auf einen,
binäre wirken auf zwei und ternäre Operatoren wirken auf drei Operanden. Zusätzlich
gibt es noch primäre Operatoren, welche über die höchste Prioritätsstufe definiert sind.
Im folgenden sind einige wichtige Operatoren nach Funktionsgruppen geordnet aufgeführt.
5.1
Arithmetische Operatoren
Operator
+
*
/
%
Bedeutung
Addition
Subtraktion
Multiplikation
Division
Modulo
Priorität
5
5
4
4
4
Typ
binär
binär
binär
binär
binär
Richtung
>
>
>
>
>
Tabelle 3: Arithmetische Operatoren
Der Multiplikationsoperator (*) und der Divisionsoperator (\) haben eine höhere Priorität (je niedriger die Zahl, desto höher ist die Prioritätsstufe) als Additions- (+) und
Subtraktionsoperator (-). Untereinander liegen sie jeweils auf der selben Prioritätsstufe
mit einer Abarbeitungsrichtung von “links nach rechts”. Deshalb ist der Ausdruck
a + 4 * 5 - b + 4
gleichbedeutend mit dem geklammerten Ausdruck
(((a + (4 * 5)) - b) + 4)
Der Modulo-Operator (%) darf nur auf ganzzahlige (Integer-) Datentypen angewendet
werden, alle andern arithmetischen Operatoren können für Ganz- und Fließkommazahlen
verwendet werden.
9
5 OPERATOREN UND AUSDRÜCKE
5.2
5.2 Relationale Operatoren
Relationale Operatoren
Operator
<
<=
>
>=
==
!=
Bedeutung
kleiner als
kleiner oder gleich
größer
größer oder gleich
gleich
ungleich
Priorität
7
7
7
7
8
8
Typ
binär
binär
binär
binär
binär
binär
Richtung
>
>
>
>
>
>
Tabelle 4: Relationale Operatoren
Das Ergebnis eines Vergleichs zweier Ausdrücke ist immer einer der beiden Integerwerte
0 (logisch unwahr) oder 1 (logisch wahr). Ein beliebiger Wert ungleich 0 wird ebenfalls
als logisch wahr angesehen. Die Anweisungen
int a = 2, b, c;
b = 3 > a;
c = 3 < a;
ordnen der Variablen b den Wert 1 (wahr) zu und der Variablen c den Wert 0 (unwahr).
Vergleichsausdrücke werden in der Regel in Verzweigungen oder in Schleifen verwendet
(vgl. Abschnitt 6).
Der Vergleichsoperator (==) darf nicht mit dem Zuweisungsoperator (=) verwechselt werden. Die Anweisung
if (x = 3) { Anweisungen; }
wird vom Compiler nicht als Fehler erkannt! Der Ausdruck x = 3 wird mit dem Wert von
x, also mit 3, einem Wert ungleich 0 bewertet. Daher ist dieser Ausdruck immer wahr,
wodurch die Anweisungen immer ausgeführt werden.
5.3
Logische Operatoren
Operator
&&
||
!
Bedeutung
logisches AND
logisches OR
logische Negation
Priorität
12
13
2
Typ
binär
binär
unär
Tabelle 5: Logische Operatoren
10
Richtung
>
>
<
5 OPERATOREN UND AUSDRÜCKE
5.4 Zuweisungsoperatoren
Die logischen Operatoren verknüpfen logische Werte (wahr (1) oder unwahr (0)) miteinander, wobei das Ergebnis aus der folgenden Tabelle herauszulesen ist.
a
0
0
1
1
b
0
1
0
1
a && b
0
0
0
1
a || b
0
1
1
1
!a
1
1
0
0
Tabelle 6: Wahrheitstabelle für logische Verknüpfungen
Die Anweisungen der Verzweigung
if ((x >= 3) && (x <= 5))
{
Anweisungen;
}
werden nur ausgeführt, wenn die Variable x einen Wert zwischen 3 und 5 besitzt.
5.4
Zuweisungsoperatoren
Operator
=
+=
-=
*=
/=
Bedeutung
Zuweisung
Zuw. nach Addition
Zuw. nach Subtraktion
Zuw. nach Multiplikation
Zuw. nach Division
Priorität
15
15
15
15
15
Typ
binär
binär
binär
binär
binär
Richtung
<
<
<
<
<
Tabelle 7: Zuweisungsoperatoren
Bei den letzten vier Zuweisungsoperatoren erfolgt die Zuweisung erst nach der Ausführung
des vorangestellten Operators. Dies ermöglicht eine verkürzte Schreibweise.
Der Additionsoperator (+) hat eine höhere Priorität als der Zuweisungsoperator (=). Daher
ist der Ausdruck
a = a + 5;
gleichbedeutend mit
a = (a + 5);
11
6 KONTROLLSTRUKTUREN
5.5 Post- und Prefix-Operatoren
Zuerst verknüpft der Additionsoperator (+) die Varable a und den Wert 5 zu dem Ausdruck a + 5. Danach wird der Variablen a dieser Ausdruck zugewiesen, d.h. die Variable
a enthält jetzt den alten Wert von a “und” 5. Somit ist der Wert von a wird um 5 erhöht
worden. Den gleichen Effekt erzielt man mit dem Ausdruck
a += 5;
Der Zuweisungsoperator (+=) erhöht zuerst den Wert der Variablen a auf der linken Seite
um 5 und weist diesen um 5 erhöhten Wert wiederum der Variablen a zu.
Auf der linken Seite einer Zuweisung muss immer eine Variable stehen.
5.5
Post- und Prefix-Operatoren
Operator
++
-()
[]
Bedeutung
Inkrement
Dekrement
Funktionsaufruf
Arrayelement
Priorität
1
1
1
1
Typ
unär
unär
primär
primär
Richtung
>
>
>
>
Tabelle 8: Post- und Prefix-Operatoren
Werterhöhungen um 1 können auch mit dem unären Inkrementoperator (++) durchgeführt
werden. Nach Abarbeitung der Ausdrücke
a += 1;
oder
a++;
besitzt die Variable a jeweils einen um 1 erhöhten Wert. Für Subtraktionen um 1 steht
der Dekrementoperator zur Verfügung.
6
Kontrollstrukturen
Normalerweise werden die Anweisungen in einem C++-Programm der Reihe nach abgearbeitet. Über die Kontrollstrukturen Schleifen und Verzweigungen kann dieser Programmfluss gezielt gesteuert werden.
Verzweigungen ermöglichen es, das Programm in Abhängigkeit von bestimmten Eingaben
oder Variablenbelegungen an vorbestimmten Stellen fortzusetzen. Mit Schleifen können
Anweisungen beliebig oft wiederholt ausgeführt werden.
12
6 KONTROLLSTRUKTUREN
6.1
6.1 Schleifenanweisungen
Schleifenanweisungen
Eine Schleife wiederholt einen Block von Anweisungen, solange ein Ausdruck als wahr
(d.h. ungleich 0) bewertet wird. Dieser Ausdruck wird auch Laufbedingung genannt.
Kopfgesteuerte Schleife
Die kopfgesteuerte Schleife überprüft die Laufbedingung zu Beginn eines jeden Schleifendurchlaufs.
while (ausdruck)
{
anweisung1;
anweisung2;
...
}
Die Anweisungen werden nicht mehr ausgeführt, sobald zu Beginn eines Schleifendurchlaufs der Ausdruck unwahr (d.h. gleich 0) ist.
Fussgesteuerte Schleife
Bei einer fussgesteuerte Schleife wird die Laufbedingung am Ende eines jeden Schleifendurchlaufs überprüft, d.h. die Schleife muss mindestens einmal durchlaufen werden.
do
{
anweisung1;
anweisung2;
...
} while (ausdruck);
Die Anweisungen werden nicht mehr ausgeführt, sobald am Ende eines Schleifendurchlaufs
der Ausdruck unwahr ist.
Zählschleife
Auch bei der Zählschleife (for-Schleife) wird die Laufbedingung zu Beginn eines jeden
Schleifendurchlaufs überprüft.
13
6 KONTROLLSTRUKTUREN
6.2 Verzweigungen
for (Initialisierungsausdruck; Laufbedingung; Inkrementausdruck)
{
anweisung1;
anweisung2;
...
}
Bei dieser Schleife wird zuerst einmalig der Initialisierungsausdruck ausgeführt. Falls die
Laufbedingung erfüllt ist, wird der Anweisungsblock abgearbeitet. Nach jedem Schleifendurchlauf wird der Inkrementausdruck ausgeführt und die Laufbedingung erneut überprüft. Solange die Laufbedingung erfüllt bleibt, wird die Schleife durchlaufen.
Zählschleifen können verwendet werden, wenn Anweisungen mit einer festen Anzahl von
Wiederholungen ausgeführt werden sollen.
int i;
for (i = 0; i < 5; i++)
{
cout << i << endl;
}
Diese Schleife gibt alle ganzen Zahlen von 0 bis 4 aus. Der Variablen i wird zu Beginn der
Wert 0 zugewiesen (Initialisierungsausdruck). Nach jedem Schleifendurchlauf (Ausgabe
des Wertes von i) wird der Wert von i um 1 erhöht (Inkrementausdruck). Sobald die
Variable i den Wert 5 erreicht (Laufbedingung ist verletzt), wird die Schleife abgebrochen
und der Anweisungsblock nicht mehr ausgeführt.
Eine Zählschleife kann auch eine kopfgesteuerte Schleife simulieren:
for (; ausdruck;)
{
anweisung1;
anweisung2;
...
}
Solange der Ausdruck wahr bleibt, werden die Anweisungen ausgeführt.
6.2
Verzweigungen
Mit selektiven Steurungsanweisungen kann man dafür sorgen, dass bestimmte Programmblöcke nur unter bestimmten Bedingungen abgearbeitet werden.
14
6 KONTROLLSTRUKTUREN
6.2 Verzweigungen
if-else-Verzweigung
if (ausdruck)
{
anweisungA_1;
anweisungA_2;
...
}
else
{
anweisungB_1;
anweisungB_2;
...
}
Falls der Ausdruck als wahr (d.h. ungleich 0) bewertet wird, so führt das Programm den
Anweisungsblock A aus. Andernfalls wird Anweisungsblock B abgearbeitet. Der alternative Anweisungsblock B ist optional, d.h. eine if-Anweisung kann ohne den else-Zweig
implementiert werden.
if (x > 3)
{
cout << x;
}
switch-Verzweigung
Im Gegensatz zur if-else-Verzweigung ist die switch-Verzweigung eine Auswahlanweisung.
switch(ausdruck)
{
case markeA: {
anweisungA_1;
anweisungA_2;
...
break;
}
case markeB: {
anweisungB_1;
anweisungB_2;
...
15
6 KONTROLLSTRUKTUREN
6.2 Verzweigungen
break;
}
...
default: {
anweisungd_1;
anweisungd_2;
...
}
}
Der Ausdruck wird nacheinander mit allen Marken auf Gleichheit verglichen. Es wird
derjenige Anweisungsblock ausgeführt, bei dessen Marke zum ersten Mal die Gleichheit zutrifft. Stimmt keine Marke mit dem Ausdruck überein, so wird der optionale
Default-Anweisungsblock abgearbeitet. Der break-Befehl sorgt dafür, dass die switchVerzweigung nach Ausführung des entsprechenden Blockes sofort verlassen wird. Fehlt
der break-Befehl, so werden auch die Anweisungen der folgenden Marken abgearbeitet.
Mit der switch-Verzweigung kann eine Fallunterscheidung implementiert werden.
int zeichen;
cin >> zeichen;
switch(zeichen)
{
case ’A’: {
functionA();
break;
}
case ’B’: {
functionB();
break;
}
default: {
cout << "Falsche Eingabe!\n";
}
}
Gibt ein Anwender das Zeichen A ein, so wird die Funktion functionA() ausgeführt. Im
Fall der Eingabe des Zeichens B wird die entsprechende Funktion functionB() aufgerufen.
Eine falsche Eingabe produziert eine Fehlermeldung als Ausgabe.
16
8 DATEIBEHANDLUNG
7
Felder
Wird in einem Programm eine Vielzahl von Daten vom gleichen Typ benötigt, so können
diese Daten in einem gemeinsamen Feld (Array) untergebracht werden.
Die Deklaration eines Feldes besteht aus einem Datentyp, dem die Elemente des Feldes
angehören sollen, dem Namen des Feldes und der Angabe der Größe des Feldes. Die
Angabe de Größe darf nur in positiven, konstanten Ausdrücken erfolgen. Durch
int vector[20];
wird etwa ein Integer-Feld der Größe 20 deklariert. Aus dem Datentyp und der Anzahl
der Feldelemente ergibt sich der Speicherplatzbedarf für das Array, d.h. in diesem Fall ist
Speicher für 20 Integer-Werte reserviert. Das Feld vector kann somit 20 Integer-Werte
aufnehmen.
Die einzelnen Elemente eines Feldes werden über ihren ganzzahligen Index angesprochen.
Das erste Element hat immer den Index 0, das letzte immer den Index Größe−1.
const int N = 20;
int vector[N];
int i;
for (i = 0; i < N; i++)
{
vector[i] = i+1;
}
for (i = 0; i < N; i++)
{
cout << vector[i] << endl;
}
// Deklaration
// Feld initialisieren
// Feld ausgeben
Als erstes wird ein Feld mit 20 Elementen vom Datentyp int unter dem Namen vector
deklariert. Die erste for-Schleife initialisiert das Feld mit den Werten 1 bis 20. In der
zweiten for-Schleife werden danach die Einträge der einzelnen Feldelemente ausgegeben.
Damit erscheinen in der Standardausgabe alle Ganzen Zahlen von 1 bis 20.
8
Dateibehandlung
Bei der Behandlung von Dateien wird in C++ ebenfalls das “Stream-Konzept” verwendet.
Hierfür muss die Header-Datei fstream.h eingebunden werden.
17
8 DATEIBEHANDLUNG
8.1
8.1 Lesen aus einer Datei
Lesen aus einer Datei
Das folgende Programm liest Fließkommazahlen aus einer Datei Daten.inp und gibt sie
auf dem Standardausgabegerät aus.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <fstream.h>
int main()
{
double x;
ifstream leseDatei;
leseDatei.open("Daten.inp", ios::in);
if (leseDatei.good())
{
leseDatei >> x;
while (!leseDatei.eof())
{
cout << x << endl;
leseDatei >> x;
}
leseDatei.close();
}
else
{
cout << "Fehler beim Oeffnen der Datei\n";
}
}
Zum Lesen aus einer Datei wird durch
ifstream leseDatei;
ein Objekt der Klasse ifstream kreiert. Damit sind mit dem Objekt leseDatei automatisch einige Methoden zur Dateibehandlung “verbunden”. Mit der Methode
leseDatei.open("Daten.inp", ios::in);
wird die Datei Daten.inp für einen Lesezugriff geöffnet. Tritt beim Öffnen der Datei kein
Fehler auf (d.h. die angegebene Datei existiert und ist lesbar), so liefert die Methode
leseDatei.good() einen positiven Wert. In diesem Fall führt das Programm einen Lesezugriff aus (Block 11-20), andernfalls wird eine Fehlermeldung ausgegeben (Block 22-24).
18
8 DATEIBEHANDLUNG
8.2 Schreiben in eine Datei
Der Lesezugriff wird mit dem Einschiebeoperator (>>) durchgeführt. Solange das Dateiende noch nicht erreicht wurde
while (!leseDatei.eof())
wird die eingelesene Fließkommazahl auf dem Standardausgabegerät ausgegeben und
durch
leseDatei >> x;
die nächste Fließkommazahl aus der Datei auf die Variable x “geschoben”. Der Anwender
hat darauf zu achten, dass die Lese-Datei Daten.inp ausschließlich Fließkommazahlen
enthält.
Nach erfolgtem Lesezugriff wird die Datei mittels der Methode
leseDatei.close();
wieder geschlossen.
8.2
Schreiben in eine Datei
Das folgende Programm erzeugt die Datei Daten.inp mit den 10 Fließkommazahlen 1.5,
2.5 bis 10.5.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <fstream.h>
int main()
{
double x;
ofstream schreibeDatei;
schreibeDatei.open("Daten.inp", ios::out);
if (schreibeDatei.good())
{
for (x = 1.5; x < 11.0; x=x+1.0)
{
schreibeDatei << x << endl;
}
schreibeDatei.close();
}
19
9 FUNKTIONEN
else
{
cout << "Fehler beim Erzeugen der Datei\n";
}
19
20
21
22
23
}
Zum Schreiben in eine Datei wird durch
ofstream schreibeDatei;
ein Objekt der Klasse ofstream kreiert. Wie beim Lesen enthält damit das Objekt
schreibeDatei automatisch einige Methoden zur Dateibehandlung. Die Methode
schreibeDatei.open("Daten.inp", ios::out);
erzeugt die Datei Daten.inp und hält sie für einen Schreibzugriff offen. Eine eventuell
schon vorhandene, lesbare Datei desselben Namens wird dabei gelöscht. Mit der Methode schreibeDatei.good() kann wie beim Lesen der Öffnungsvorgang auf Fehlerfreiheit
überprüft werden.
In die geöffnete Datei kann nun genauso wie auf die Standardausgabe mit Hilfe des Einschiebeoperators (<<) geschrieben werden
schreibeDatei << x << endl;
Nach erfolgtem Schreibzugriff muss die Datei wieder geschlossen werden
leseDatei.close();
9
Funktionen
Eine Funktion wird eingesetzt, wenn bestimmte Teilaufgaben öfters und an verschiedenen Stellen im Programm ausgeführt werden sollen. Dabei ist es unerheblich, ob diese
Teilaufgaben einfach oder sehr komplex sind.
9.1
Definition einer Funktion
Eine Funktion wird außerhalb des Hauptprogrammes definiert durch
20
9 FUNKTIONEN
9.1 Definition einer Funktion
Datentyp Funktionsname(Parameterliste mit Typangabe)
{
Anweisungen;
return Ausdruck;
}
Im Funktionskopf wird der Datentyp des Funktionswertes (Rückgabewert), sowie die
Datentypen der einzelnen Parameter (Argumente), die beim Funktionsaufruf übergeben
werden, spezifiziert. Zusätzlich wird dort der Funktionsname festgelegt, mit dem die
Funktion später aufgerufen werden kann.
Die Aufgabe die eine Funktion erfüllen soll, wird in ihrem Anweisungsblock formuliert.
Nach Abarbeitung aller Anweisungen liefert die Anweisung
return Ausdruck;
den Wert eines Ausdrucks an die Stelle des “Aufrufs” der Funktion zurück. Der Datentyp
dieses Rückgabewertes muss mit dem im Funktionskopf spezifizierten Rückgabedatentyp
übereinstimmen. Die Ausführung der return-Anweisung führt zum sofortigen Verlassen
der Funktion und Rücksprung an die Stelle im Programm, von der aus die Funktion aufgerufen wurde. Innerhalb des Anweisungsblocks muss mindestens eine return-Anweisung
vorkommen.
Eine Funktion quad, die als Rückgabewert das Quadrat
(a + b)2 .
liefert kann wie folgt definiert werden
int quad(int a, int b)
{
int c;
c = a + b;
return c*c;
}
Die formalen Parameter a und b stehen innerhalb der Funktion als lokale Datenobjekte zur Verfügung. Natürlich können im Anweisungsblock zusätzliche lokale Variablen
deklariert werden.
Liefert eine Funktion kein Ergebnis zurück, so verwendet man den Rückgabedatentyp
void. In diesem Fall wird auch keine return-Anweisung im Anweisungsblock benötigt.
Eine Funktion kann auch ohne eine Parameterliste definiert werden
21
9 FUNKTIONEN
9.2 Aufruf einer Funktion
void meldung(void)
{
cout << "Das Programm wird beendet." << endl;
}
Die Funktion meldung gibt einfach die Meldung
Das Programm wird beendet.
auf dem Standardausgabegerät aus.
9.2
Aufruf einer Funktion
Eine Funktion kann beliebig oft und an beliebiger Stelle, sofern sie dort “bekannt” ist, innerhalb anderer Funktionen aufgerufen werden. Dazu wird die Funktion mit ihrem Namen
und den entsprechend der Parameterliste gewählten Parametern aufgerufen
Funktionsname(Parameter);
Die Parameter müssen in Datentyp und Position mit der Paramterliste der Funktionsdefinition übereinstimmen, denn diese aktuellen Parameter ersetzen die formalen Parameter
in der Funktion.
Die Funktion quad etwa kann wie folgt aufgerufen werden:
int q, r;
q = quad(1,2);
r = quad(q,1);
cout << q << "und" << r << endl;
Diese Anweisungen erzeugen als Ausgabe 9 und 100.
Das Hauptprogramm main() selbst ist auch eine Funktion, so dass dort beliebige Funktionen aufgerufen werden können.
Parameterübergabe
Funktionsparameter können auf zwei verschiedene “Arten” übergeben werden. Bei Verwendung der oben beschriebenen “Art” wird beim Aufruf einer Funktion Speicher für
22
9 FUNKTIONEN
9.2 Aufruf einer Funktion
die formalen Parameter bereitgestellt und jeder formale Parameter mit dem entsprechenden aktuellen Parameter initialisiert. Alternativ dazu kann man auch nur eine Referenz
auf einen Parameter übergeben. Dabei arbeitet die Funktion direkt mit der übergebenen
Variablen.
Bei Aufruf der Funktion
void add(int val, int &ref)
{
val++;
ref++;
}
inkrementiert die Anweisung
val++;
eine lokale Kopie des ersten übergebenen Parameters. Der übergebene Parameter selbst
bleibt dabei unverändert. Daher dürfen feste Zahlwerte beim Aufruf der Funktion übergeben werden. Die Anweisung
ref++;
inkrementiert den zweiten übergebenen Parameter direkt, d.h. es wird keine Kopie angelegt. Alle Änderungen werden an dem übergebenen Parameter selbst vorgenommen.
In diesem Fall muss beim Aufruf der Funktion eine Variable übergeben werden. Die
Anweisungen
int i = 1;
int j = 1;
add(i,j);
cout << i << "und" << j << endl;
liefern 1 und 2 an das Standardausgabegerät. Der erste Parameter wird als Wert übergeben
(“call by value”), dabei bleibt die Variable i unverändert. Das zweite Argument wird
dagegen als Referenz (“call by reference”) übergeben und somit wird keine Kopie, sondern
die Variable j selbst inkrementiert.
Wird ein Objekt nur aus Effizienzgründen per Referenz übergeben, und nicht um es zu
verändern, so sollte der entsprechende Parameter als const deklariert sein
void f(const int &ref)
{
...
}
23
9 FUNKTIONEN
9.3
9.3 Deklaration einer Funktion
Deklaration einer Funktion
Damit der Compiler bei der Abarbeitung eines Programmes eine Funktion aufrufen kann,
muss sie ihm an der Aufrufstelle “bekannt” sein. Entweder muss sich der Funktionsaufruf
im Programmcode nach der Funktionsdefinition befinden, oder die Funktion muss vor
ihrem Aufruf deklariert werden.
In der Funktionsdeklaration wird eine Schnittstelle zu der Funktion festgelegt:
Datentyp Funktionsname(Parameterliste mit Typangabe);
Dabei müssen Rückgabedatentyp, Funktionsname und Parameterliste, inklusive der Parameterreihenfolge mit der Funktionsdefinition übereinstimmen. Mit der Anweisung
int quad(int a, int b);
wird etwa die Funktion quad deklariert.
Eine Funktionsdeklaration muss sich vor dem ersten Aufruf der Funktion und außerhalb
jeglicher anderer Funktionen befinden.
9.4
Mathematischen Funktionen
Um die in Tabelle 9 aufgeführten mathematischen Funktionen verwenden zu können,
muss die Header-Datei math.h eingebunden werden. Zusätzlich muss der Compiler mit
der Option -lm gestartet werden, wodurch eine Mathe-Bibliothek “hinzugelinkt” wird.
Deklaration
int abs(int x);
double fabs(double x);
double sqrt(double x);
double sin(double x);
double cos(double x);
double tan(double x);
double exp(double x);
double pow(double x, double y);
double log(double x);
double log10(double x);
double ceil(double x);
double floor(double x);
Bedeutung
Absolutbetrag von x
Absolutbetrag von x
Quadratwurzel von x
Sinus von x
Cosinus von x
Tangens von x
Exponentialfunktion
Potenzfunktion xy
Natürlicher Logarithmus von x
Dekadischer Logarithmus von x
Aufrunden auf Ganzzahl
Abrunden auf Ganzzahl
Tabelle 9: Mathematische Funktionen
24
LITERATUR
LITERATUR
Literatur
[KR90] B.W. Kernighan, D.M. Ritchie. Programmieren in C. 2. Ausg., Hanser,
München 1990.
[S01]
K. Schröder. Nitty Gritty C. Hanser, Addison-Wesley, München 2001.
[S00]
B. Stroustrup. Die C++ Programmiersprache. 4. Aufl., Addison-Wesley,
München; Boston 2000.
25
Zugehörige Unterlagen
Herunterladen