Theorievortrag
Peter Beran,
Kristof Bhmer,
Reinhold Dunkl,
Sonja Kabicher-Fuchs,
Georg Kaes,
Martin Polaschek,
Manfred Schttengruber,
Helmut Wanek (Vortragender)
Anhang 232-250
Thematische Inhaltsbersicht 251-255
2
Testplan
3
Literatur
H.M. Deitel und P.J. Deitel: C++ How to Program. Prentice Hall
H.M. Deitel & P.J. Deitel & T.R. Nieto: C++ in the Lab, Prentice Hall (bungsbuch zu
C++ How to Program)
Bjarne Stroustrup: Einfhrung in die Programmierung mit C++, Pearson Studium.
Bjarne Stroustrup: Die C++ Programmiersprache. Addison Wesley.
Bjarne Stroustrup: Programming Principles and Practice Using C++, Addison Wesley.
4
Folien des Vortrags
5
1. Grundlagen
Ein erstes Programm
Was ist Informatik
Input Output
Informatik
7
Modelle der Informatik
Modelle Computerprogramm
der ...
Spielregeln
Input Bedienungsanleitung Output
Kochrezept
Informatik
8
Beispiele fr Modelle
Kochrezept als Folge von Anweisungen
Amerikanischer Wildreis
1. 1 Tasse ergibt 3 Portionen
2. Reis grndlich waschen
3. 1 Tasse Reis in 3 Tassen kochendes Wasser geben
4. kurz aufkochen lassen
5. bei schwacher Hitze 25 min bedeckt dnsten
6. Reis abdecken, salzen, mit Gabel auflockern
7. restliche Flssigkeit verdampfen
9
Algorithmus
Ursprung
Algorithmus Berechnungsvorschrift
Ben Musa Al-Chwarizmi (usbekischer Mathematiker um 825), erstes
Buch ber Algebra
arithmos ... griechisches Wort fr Zahl
10
Algorithmus - Eigenschaften
Eingangswerte/Ausgabewerte
EW sind vor, AW nach der Ausfhrung bekannt
Eindeutigkeit
Jeder Schritt der Ausfhrung muss eindeutig sein, keine Mehrdeutigkeiten
mglich
Endlichkeit
Statisch: mit endlich vielen Zeichen formulierbar
Dynamisch: in endlich vielen Schritten beendbar
Vollstndigkeit
sollte vollstndig sein, sollte alle mglichen Flle behandeln
Korrektheit
sollte das gewnschte Ergebnis liefern
Granularitt der Operationen
Spezifikationsgenauigkeit der einzelnen Beschreibungselemente
11
Paradigmen und Darstellung
Paradigma
... Das, was den Mitgliedern einer wissenschaftlichen Gemeinschaft gemeinsam
ist ... eine Konstellation von Meinungen, Wertungen und Methoden... (Thomas
Kuhn 1976)
12
Das logikbasierte Paradigma
ein Programm besteht aus Regeln und Fakten
Wenn es regnet, nehme ich den Schirm.
Wenn ich zerstreut bin, vergesse ich den Schirm unterwegs.
Regeln Wenn ich zerstreut bin, gre ich Bekannte nicht.
Wenn ich nicht zerstreut bin, gre ich Bekannte.
Wenn es regnet und ich meinen Schirm unterwegs vergesse, werde ich nass.
Wenn es schwl ist, bin ich zerstreut.
Fakten
Es ist schwl.
Es regnet.
Anfrage: werde ich nass? Antwort: ja.
Ableitbare Fakten: Ich nehme den Schirm. Ich bin zerstreut. Ich vergesse den Schirm
unterwegs. Ich werde nass. Ich gre Bekannte nicht.
ein Problem: Widersprche zwischen den Regeln
Beispiel (nicht ganz ernst zu nehmen, aber illustrativ): Meta-Regel
Regel 1: Der Chef hat immer recht.
Regel 2: Stimmt dies ausnahmsweise nicht, so findet Regel 1 Anwendung.
13
Das funktionale Paradigma
Beispiel: Csar-Verschlsselung
Nimm jeweils den im Alphabet drittfolgenden Buchstaben
CAESAR
jedes Vorkommen von 'A' kann
FDHVDU durch 'D' ersetzt werden
Das Programm ist nur aus Funktionen (Abbildungen) im mathematischen
Sinn aufgebaut. Jedes Vorkommen eines Funktionsaufrufes kann durch
das Funktionsergebnis ersetzt werden. Funktionen haben keine
Seiteneffekte; es gibt keine Variablen.
n=0 1
Beispiel: Fakulttsfunktion n! = n>0 n(n-1)!
14
Das prozedurale Paradigma
imperatives Paradigma
Pfadfinder-Gelndespiel:
Geh zur alten Hhle
Merk dir, wieviele Fichten davor stehen
Geh den Weg weiter bis zur Gabelung
Zhle die Fichten, die du hier siehst, dazu
Wenn du insgesamt fnf Fichten gezhlt hast, geh den linken Weg weiter;
wenn sieben, dann den rechten
15
Algorithmendarstellung
Graphisch
Ablaufdiagramme
Struktogramme
Pseudocode
Knstliche Programmiersprachenderivate
Stilisierte Prosa
Programmiersprachen
16
Darstellung graphisch
Ablaufdiagramm = Flussdiagramm Struktogramm
Anweisungen stehen in Knoten Anweisungen stehen in Blcken
Kontrollfluss: gerichtete Kanten Kontrollfluss: Form, Struktur der Blcke
Start summe = 0
summe = 0
n>0
n>0?
JA NEIN summe = summe + n
summe =
summe + n n=n1
drucke drucke summe
summe
n=n1
Ende
int summe = 0;
while(n > 0) {
summe += n;
n--;
}
cout << summe;
Damit ein Computer ein Problem lsen kann, mssen wir ihm den
Algorithmus in Form eines Programms, das in einer
Programmiersprache geschrieben ist, prsentieren
20
Programmiersprachen-Gruppen
Generation Typ der Programmiersprache Vertreter
erste Maschinensprachen Binr- und Hexadezimal-Programmierung
zweite Maschinenorientierte Sprachen OS/370 Assembler, 2650 Assembler,
(Assembler) 8080 Assembler
23
Variablen im berblick
Ein Datentyp <W,O> ist eine Menge von Werten (Wertebereich W) mit
einer Menge von Operationen (O), die auf diesen Werten definiert sind.
Beispiele: <R, {+,-,*,/}>
Ausprgungen: 3 + 4, 3.5 * 6 / 10, ...
<{wahr, falsch}, {, , }>
Ausprgungen: wahr falsch, wahr falsch,
...
Werte nennen wir auch Objekte, Exemplare oder Instanzen eines
Datentyps.
25
Datentypen und Speicherplatz
26
Variablen im Detail
Situation im Hauptspeicher:
1048575 ? 27
Wertzuweisung
Wir unterscheiden zunchst zwei Verwendungsarten fr Variablen:
Lesen einer Variable (Zugriff auf ihren aktuellen Inhalt)
Wertzuweisung auf eine Variable (ndern ihres Inhalts,
Schreiben der Variable, Belegen der Variable mit einem Wert)
V = Ausdruck
linke Seite rechte Seite
Vorgangsweise bei der Wertzuweisung:
1. Berechne den Wert des Ausdrucks auf der rechten Seite
2. Weise den berechneten Wert der Variablen auf der linken Seite zu
ndern
X = 3 Lesen
X = X*7
ndern
28
bersetzung und Verarbeitung
bersetzung Verarbeitung
Programmtext - Tastatur
Peripherie - Platten
- Bildschirm
Compiler
Maschinencode Maschinencode
- Programm
Arbeitsspeicher - Daten
jeweilige Vorteile: - Betriebs-
o Programmtext: lesbar system
o Maschinencode:
vom Rechner ausfhrbar Prozessor
Bus 29
Ein erstes Programm: Kreisflche I (1)
gegeben: Radius; gesucht: Kreisflche
#include<iostream>
using namespace std;
int main() {
double r,a;
cout << "Radius? ";
cin >> r;
a = 3.1415*r*r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
30
Ein erstes Programm: Kreisflche I (2)
#include<iostream>
using namespace std; Schnittstelle BS
int main() {
double r,a;
cout << "Radius? ";
cin >> r;
a = 3.1415*r*r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
31
Ein erstes Programm: Kreisflche I (3)
#include<iostream>
using namespace std;
int main() {
double r,a;
cout << "Radius? ";
cin >> r; Funktion
a = 3.1415*r*r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
32
Ein erstes Programm: Kreisflche I (4)
#include<iostream> Variablen
using namespace std;
int main() {
double r,a;
cout << "Radius? ";
cin >> r;
a = 3.1415*r*r; Anweisung (Wertzuweisung)
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
33
Ein erstes Programm: Kreisflche II
zwei Wertzuweisungen auf die selbe Variable
#include<iostream>
using namespace std;
int main() {
double r,a;
cout << "Radius? ";
cin >> r;
a = r*r; // Wertzuweisung 1
a = 3.1415*a; // Wertzuweisung 2
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
34
Ein erstes Programm: Kreisflche III
Variablendefinition unmittelbar vor der ersten Verwendung
#include<iostream>
using namespace std;
int main() {
double r;
cout << "Radius? ";
cin >> r;
double a;
a = 3.1415*r*r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
35
Ein erstes Programm: Kreisflche IV
Variablendefinition mit Initialisierung
#include<iostream>
using namespace std;
int main() {
double r;
cout << "Radius? ";
cin >> r;
double a = 3.1415*r*r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
36
Ein erstes Programm: Kreisflche V
#include<iostream>
using namespace std; Problem?
int main() {
double r, a = 3.1415*r*r;
cout << "Radius? ";
cin >> r;
cout << "Kreisflche fr Radius " <<
r << " ist " << a;
return 0;
}
37
Ein erstes Programm: Kreisflche VI
#include<iostream>
using namespace std;
int main() {
double r;
cout << "Radius? ";
cin >> r;
cout << "Kreisflche fr Radius " <<
r << " ist " << 3.1415*r*r;
return 0;
}
38
Wiederholung
39
2. Datentypen, Variable und Ausdrcke
Datentypen und Speicherplatz
Beispiele:
double X; int a, b, c; short int s;
44
Initialisierung
Beispiel: int X = 3, Y = X * 7;
45
Ausdrcke
Auswertung: 3 3 4 4
(double) 9 (double) 16
(double) 25
(double) 5
(double) 5 46
Zuweisungsoperator
In C++ sind Zuweisungen auch Ausdrcke und liefern den Wert der
linken Seite der Zuweisung nach erfolgter Wertbertragung. Die
Wertbertragung gilt als Seiteneffekt des Zuweisungsoperators =.
Beispiel: double a = 3, b = 4, c;
c = sqrt( a*a + b*b );
Auswertung: 3 3 4 4
(double) 9 (double) 16
(double) 25
(double) 5
(double) 5
47
Ausdruckauswertung (1)
Negativbeispiel:
int x, y, z;
(x * y) + (5 * z) // welche Multiplikation zuerst?
x = (y = 3) + (y = 4); // hat y den Wert 3 oder 4?
50
Typkonversionen
51
Typkonversionen Variante 1
double x = 12.5;
cout << 3 / 2 + x;
ostream int int double 12.5
double 13.5
double x = 12.5;
cout << 3. / 2 + x;
ostream double int double 12.5
double 2.0
double 1.5
double 14.0
54
Typkonversionen - automatisch
55
Beispiel: Datentypen (1)
#include<iostream>
using namespace std;
int main() {
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
56
Beispiel: Datentypen (2)
Ausgabe:
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
0.5 ist auch nicht viel, aber immerhin
1 ist auch ohne Komma eine reelle Zahl
i ist gleich 1 und ist ungerade
Viel Spass mit C++
57
Wiederholung
58
3. Anweisungen und Fehlerbehandlung
Sequenz
Anweisung 1
Anweisung 1
Anweisung 2
Anweisung 2
Anweisung 3
Anweisung 3
60
Anweisungsblock
61
C++ Programm
Jedes ausfhrbare C++ Programm muss genau einen Anweisungsblock
enthalten, der mit dem Namen main() vereinbart wurde.
Dieser Block wird auch als Hauptprogramm bezeichnet. Das Programm endet
blicherweise nach der letzten Anweisung in main. Mit dieser sollte ein Fehlercode
an das Betriebssystem bergeben werden (return 0).
int main()
{
float a;
cout << "A eingeben";
cin >> a;
return 0; Rckgabewert an das Betriebssystem (kann
} bei der Funktion main auch weggelassen
werden)
Genaugenommen handelt es sich um eine Funktion mit dem Namen main() (der
Begriff Funktion wird spter genauer erklrt).
62
Entscheidung oder Verzweigung
abhngig davon, ob eine Bedingung erfllt ist, wird eine von zwei (oder
mehreren) Anweisungen ausgefhrt:
Bedingung
JA Bedingung NEIN wahr falsch
erfllt? Anweisung 1 Anweisung 2
63
Bedingte Anweisung
logischer Ausdruck
if ( logischer-Ausdruck ) wahr falsch
Anweisung1
[ else Anweisung2 ] Anhang: Syntaxbeschreibung Anweisung 1 Anweisung 2
Beispiel
if (j > n) // Bedingung / logischer Ausdruck: j > n
j = 0; // Anweisung 1
else
j = i; // Anweisung 2
Leeranweisung
erlaubt z. B.: if(2<3) i = 0; oder if(1) ; else j = 0;
nicht erlaubt: if(1) else j = 0;
Die beiden Anweisungen knnen beliebig gewhlt werden; insbesondere sind auch
Anweisungsblcke und (weitere) bedingte Anweisungen erlaubt.
logischer-Ausdruck ist ein Ausdruck, der ein logisches (boolesches) Ergebnis
liefert (true, false). In C++ wird 0 als false und jeder andere Wert als true
interpretiert (implizite Typumwandlung zwischen bool und int).
D.h. 0 false, 1 true, -1 true, 47.11 true,...
64
Beispiel: Maximum zweier Zahlen (1)
#include<iostream>
using namespace std;
int main() {
double a, b;
cout << "Geben Sie die 2 Zahlen ein: ";
cin >> a >> b;
if(a > b)
cout << a << " groesser als " << b;
else
cout << b << " groesser als " << a;
}
65
Beispiel: Maximum zweier Zahlen (2)
Korrigierte Version
#include<iostream>
using namespace std;
int main() {
double a, b;
cout << "Geben Sie die 2 Zahlen ein: ";
cin >> a >> b;
if(a == b)
cout << a << " gleich " << b;
else
if(a > b)
cout << a << " groesser als " << b;
else
cout << b << " groesser als " << a;
}
66
Logische Ausdrcke (1)
67
Logische Ausdrcke (2)
Wahrheitstafeln:
&& false true || false true !
false false false false false true false true
true false true true true true true false
69
Beispiel: Fakultt
int main() {
int n;
cin >> n; Idee: n! hat einen
if (n == 0 || n == 1) cout << 1;
else if (n == 2) cout << 2;
sehr kleinen
else if (n == 3) cout << 6; Definitionsbereich
else if (n == 4) cout << 24;
else if (n == 5) cout << 120;
(0..12) - Ergebnisse
else if (n == 6) cout << 720; knnten direkt
else if (n == 7) cout << 5040; angegeben werden
else if (n == 8) cout << 40320;
else if (n == 9) cout << 362880;
else if (n == 10) cout << 3628800;
else if (n == 11) cout << 39916800;
else if (n == 12) cout << 479001600;
else cout << "Fehler!";
}
Ist leider etwas umstndlich!
70
Verschachtelte bedingte Anweisung
71
Mehrwegverzweigung (1)
Trotz Klammern und Einrcken sind tiefe Schachtelungen oft
unbersichtlich Abhilfe schafft mitunter die Mehrwegverzweigung:
switch ( Ausdruck ) {
case konstanter-Ausdruck1 : Anweisungsliste1
case konstanter-Ausdruck2 : Anweisungsliste2
...
case konstanter-Ausdruckn : Anweisungslisten
[ default : Anweisungslisten+1 ]
}
Der Ablauf verzweigt an die dem switch-Ausdruck entsprechende Stelle und setzt
von dort sequentiell fort.
Die einzelnen Anweisungslisten enthalten daher i.a. break oder return, um den
jeweiligen Abschnitt zu verlassen. break verlsst die unmittelbar umgebende
switch-Anweisung oder Schleife, return verlsst die umgebende Funktion.
72
Mehrwegverzweigung (2)
Entspricht keiner der konstanten Ausdrcke dem switch-Ausdruck, so
wird zur default-Marke verzweigt. Fehlt diese, so wird nach der switch-
Anweisung fortgesetzt.
int main() {
int n; cin >> n;
switch (n) {
case 0: case 1: return 1;
case 2: return 2;
case 3: return 6;
case 4: return 24;
case 5: return 120;
...
Faustregel:
case 12: return 479001600; default immer
}
default: return -1;
(evt. leer) angeben.
}
73
Iteration oder Schleife
Anweisung wird abhngig von einer Bedingung wiederholt ausgefhrt
Verschiedene Schleifenformen in Gebrauch
Beispiel: while-Form
Anweisung wird solange wiederholt, wie die Bedingung erfllt ist (Eintrittsbedingung)
Bedingung NEIN
erfllt?
Eintrittsbedingung
JA
Anweisung
Anweisung
74
Wiederholungsanweisung (1)
Aufgabenstellung: Eine Zahl n soll eingelesen und die Summe der Zahlen
1 bis n ausgegeben werden.
Versuch:
#include<iostream>
using namespace std;
int main() {
int n;
cin >> n;
cout << 1 + 2 + ??? + n;
}
Bei dieser Variante muss das Programm jedes Mal der Problemstellung
angepasst werden - erscheint ungnstig.
75
Wiederholungsanweisung (2)
76
Wiederholungsanweisung (3)
77
Wiederholungsanweisung (4)
78
Wiederholungsanweisung (5)
79
Wiederholungsanweisung (6) Jeder der drei Teile in den Klammern
darf auch leer bleiben. Die beiden
#include<iostream> Strichpunkte sind aber unbedingt
erforderlich.
using namespace std; Ein leerer Test wird als true bewertet.
int main () { for (;;); //Endlosschleife
int n, s = 0; Initialisierung
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
} Test Re-Initialisierung
cout << s;
}
80
Beispiel: Multiplikationstabelle
#include<iostream>
using namespace std;
int main() {
int i = 1, j = 1, n, m;
cin >> n >> m;
while(i<=n) {
while(j<=m) {
cout << i * j << " "; Input: 5 10
j = j + 1;
}
Ausgabe:
j = 1; 1 2 3 4 5 6 7 8 9 10
cout << endl; 2 4 6 8 10 12 14 16 18 20
i = i + 1; 3 6 9 12 15 18 21 24 27 30
} 4 8 12 16 20 24 28 32 36 40
} 5 10 15 20 25 30 35 40 45 50
81
Fehler und Fehlerbehandlung
Fehler sind leider unvermeidlich. Je nach Art der Fehler gibt es aber unterschiedliche
Hilfsmittel und Strategien zu deren Behebung bzw. Vermeidung .
bersetzungsfehler (werden vom Compiler angezeigt):
Warning
Das Programm ist korrekt, aber es ist wahrscheinlich, dass die Bedeutung des Programms so nicht
beabsichtigt ist (z.B. Verwendung von = in einer Bedingung statt ==).
Error
Syntaxfehler verhindern, dass ein lauffhiges Programm erstellt werden kann. Es empfiehlt sich, die
Fehlermeldungen des Compilers genau zu lesen. Bei vielen Fehlermeldungen ist in der Regel vor
allem die erste die wichtigste (Folgefehler).
Laufzeitfehler (treten whrend der Programmausfhrung auf):
Fehler in der Programmlogik
Das Programm liefert nicht die gewnschten Ergebnisse. Der Programmablauf kann durch
zustzliche Ausgaben (Logging), Zustandsberprfungen (assert) oder die Verwendung von
Debuggern getestet werden.
Bedienungsfehler
Das Programm wird durch unvorhergesehene Ereignisse (z.B. falsche Eingaben, Ausfall der
Internetverbindung) in einen unerwarteten bzw. undefinierten Zustand versetzt. Defensive
Programmierung und geeignete Behandlung von Ausnahmezustnden helfen, Programme gegen
diese Art von Fehlern robust zu machen.
82
assert (1)
assert hilft, Denkfehler des Programmierers zu finden
#include<cassert>
if (B1) if (B1)
if (B2) if (B2) Korrektheitsbedingung
A1; A1;
else else {
// B1 && !B2 assert(B1 && !B2);
A2; A2;
} Bedingung false
Programmabbruch
#include<cassert> #include<cassert>
if (B1) if (B1) if (B1) { if (B1) {
if (B2) if (B2) if (B2) if (B2)
A1; A1; A1; A1;
else ; else ;
else else { } else } else {
// !B1 assert(!B1); // !B1 assert(!B1);
A2; A2; A2; A2;
} }
83
assert (2)
#include<cassert>
#include<iostream>
using namespace std;
int main() {
double n;
cout << "Geben Sie eine Zahl ein: ";
cin >> n;
if (n < 0) {
assert(n < 0); // Ziemlich berflssig
n = -n;
}
assert(n > 0); // Nicht berflssig!
// Deckt einen Denkfehler auf
} Programmaufruf:
Geben Sie eine Zahl ein: 0
beispiel.C:12: failed assertion `n > 0'
Abort 84
Defensive Programmierung
for (int i=n; i!=0; i=i-1) for (int i=n; i>0; i=i-1)
Endlosschleife fr n<0 hlt auch fr n<0
85
Behandlung von Ausnahmen exception handling (1)
Traditionell: bool error = false;
if
bool error = false; if
if
//Fehler passiert //Fehler passiert
error = true; error = true;
if (error) //Fehlerbehandlung if (!error)
if (!error)
Fehleranfllig, da Abfrage nie
if (!error)
vergessen werden darf und
mhsam in verschachtelten
Programmteilen oder bei if (error) //Fehlerbehandlung
Funktionsaufrufen
86
Behandlung von Ausnahmen exception handling (2)
Programmfluss
Modern mit Exception: verzweigt zu einem try {
try { passenden catch
int i=7; if
Statement am Ende
eines (dynamisch) if
//Fehler umschlieenden if
throw 3; try Blocks.
//Fehler passiert
Falls kein
//anderer Fehler passendes catch throw 1;
throw i; Statement
vorhanden ist, wird
//noch ein Fehler das Programm
throw 1.2; abgebrochen.
}
catch (int err) {
Datentyp muss genau }
} passen. Keine der
bisher bekannten catch (int) //Fehlerbehandlung
catch (double d) {
Typumwandlungen.
} Variablenname darf auch weggelassen
catch (...) { Alle Datentypen werden, wenn der Wert zur
(Reihenfolge der catch Fehlerbehandlung nicht bentigt wird.
} Statements wichtig). 87
Wiederholung
Anweisungsblock { }
Hauptprogramm int main() { return 0; }
logischer Ausdruck 3 < 4 || 2 > 3 && 5 == 5
if-Anweisung if (a > b) ; else ;
switch-Anweisung switch(a){case 2: break; default: }
while-Schleife while (a > 4) a = a - 3;
do/while-Schleife do a = a - 3; while (a > 4);
for-Schleife for (int i=0; i<n; i=i+1) ;
assert #include<cassert>
if(a<0) a = -a; assert(a>=0);
try/catch try { throw x; } catch() {}
88
4. Felder, Zeiger und Referenzen
Felder (1)
90
Felder (2)
Zugriff:
Name [ Index ] { [ Index ] }
const int n = 1; NUR weil n
double wert[ 2*n ]; konstant ist
wert[0] = 1.0;
wert[n] = 2.0;
Initialisierung von Feldern durch Angabe einer Werteliste in
geschwungenen Klammern
double ex[3] = { 1, 0, 0 };
double ey[3] = { 0, 1 }; // drittes Element implizit mit 0 belegt
double ez[] = { 0, 0, 1 }; // Lnge des Feldes wird aus der
// Initialisierungsliste ermittelt
91
Felder (3)
int main () {
int fak[13] = {1,1,2,6,24,120,720,5040,40320,
362880,3628800,39916800,479001600};
cin >> n;
if (n >= 0 && n <= 12)
cout << fak[n];
else fak[0] 1
cout << "Fehler!";
} [1] 1
[2] 2
sizeof (fak) =
13 * sizeof (int) = [3] 6
13 * sizeof (fak[0])
... ...
[12] 479001600 92
Felder (4) Zeichenketten
Zeichenketten (Strings) sind spezielle Felder:
"C++" ist ein char-Feld der Lnge 4. "C++"
Zeichenketten werden durch ein Nullbyte ('\0')
terminiert, d.h. die physische Ausdehnung ist um [0] 'C'
eins grer als die logische
Initialisierung von Zeichenketten durch [1] '+'
Stringkonstanten mglich
char s[] = "C++";
[2] '+'
Die folgende Anweisung ermittelt die (logische) [3] '\0'
Lnge von Zeichenketten:
for (i=0;s[i]!='\0';i=i+1);
Leeranweisung als Das Nullzeichen
Schleifenrumpf markiert das Ende
Direkte Ein- und Ausgabe wird fr
Zeichenketten (nicht fr andere Felder) untersttzt: der Zeichenkette
cout << s << "abc";
cin >> s;
93
Felder (5) Zeichenketten
Textzeile einlesen und verkehrt wieder ausgeben:
#include<iostream>
using namespace std;
int main() {
char zeile[80];
int i, n;
do {
cin >> zeile;
for (n=0; zeile[n]!='\0'; n=n+1);
for (i=n-1; i>=0; i=i-1) {
cout << zeile[i];
}
cout << endl;
} while (1); Achtung: Endlosschleife,
} Abbruch durch ^C (Ctrl-C)
94
Zeiger (1)
..... .....
i 12300 47 int i 47
? ?
j 12308 11 int schematisch: j 11
? ?
z 12316 12308 Zeiger auf int z
..... .....
Lesen/Zuweisen eines Wertes mittels eines Zeigers ergibt/verndert den
Wert der Variable, auf die gezeigt wird.
95
Zeiger (2)
..... .....
i 47 i 47
? ?
j 123 j 123
? ?
z z
..... .....
Verndern/Lesen des Werts, Verndern(Verbiegen)/Lesen
auf den gezeigt wird. des Zeigers (der Adresse)
selbst.
96
Falle: * gehrt eigentlich zum
Datentyp, bindet aber
Zeiger (3) syntaktisch mit dem Namen
Der Name des Zeigers steht fr den Zeiger (die Adresse) selbst.
Um auf den Wert zuzugreifen, muss die Adresse dereferenziert werden. Dazu
wird dem Namen des Zeigers (oder einem Ausdruck, der einen Zeiger liefert) ein *
vorangestellt.
x=y; //x zeigt nun auch dorthin, wo y hinzeigt
*x=5; //5 wird dort als Wert gespeichert, wo x hinzeigt
z=*y; //z bekommt den (int) Wert, auf den y zeigt (5)
x=5; x=z; //Indirektionslevel verschieden, keine implizite Typumwandlung
double *dp = x //Datentyp, auf den gezeigt wird, verschieden; keine implizite
//Typumwandlung
double d = *x; //OK (implizite Typumwandlung) 97
Zeiger (4)
int * x, z = 3;
x = &z; //OK, &z ergibt eine Adresse; x zeigt nun auf z
*x = 7; //nderung des Werts, auf den x zeigt
cout << z; //Ausgabe: 7
cout << *x+1; //Ausgabe: 8
cout << x; //Ausgabe der (in x enthaltenen) Adresse von z
cout << &z; //Ausgabe der gleichen Adresse wie oben
cout << &x; //Ausgabe der Adresse von x (nicht wie oben)
cout << sizeof(x) //Ausgabe: 8 (auf 64bit Rechnern)
98
Zeiger (5)
Prinzipiell kann ein Zeiger auf jeden beliebigen Datentyp zeigen. Bei
Zuweisungen zwischen Zeigern ist auf gleichen Datentyp zu achten, es
werden keine impliziten Typumwandlungen durchgefhrt.
99
Zeiger (6)
Der Indirektionslevel auf beiden Seiten der Zuweisung muss gleich sein.
Dabei gilt: Der Adressoperator & (der nur einmal auf eine Variable
angewendet werden kann) erhht den Level um eins und der Dereferenz-
operator * (der auch mehrfach angewendet werden darf) vermindert den
Level um eins.
100
Zeiger sind gefhrlich
int *ip;
??? 42
*ip = 42;
ip ???
Es gilt: x[i] entspricht *(x+i), falls x ein Zeiger oder ein Array ist.
Da ein Array technisch als Zeiger realisiert ist, ist es nicht notwendig (aber auch nicht
verboten) den Adressoperator zu verwenden, um die Adresse des ersten Feldelements zu
erhalten:
103
Zeiger und Felder (3)
Zeiger und Felder sind in C++ sehr hnlich. Entscheidende Unterschiede sind:
Fr Feldelemente wird automatisch Speicherplatz reserviert ein Zeiger muss aber geeignet
initialisiert werden:
int ia[] = {1,3,5}; int *ip;
ia[1] = 10; ip[1] = 10;
*ia = 0; *ip = 0;
Das Feld ist ein konstanter Zeiger
ia = ip; ip = ia;
Der Adressoperator liefert beim Feld einen Zeiger auf das erste Element, beim Zeiger wird
der Indirektionslevel erhht.
ia 1
&ia 3
5 &ip ip
sizeof liefert bei einem Feld die Feldgre, bei einem Zeiger die Gre eines Zeigers
104
Referenzen
Referenzen ermglichen es, einer Variablen einen zustzlichen Namen zu geben.
Eine Referenz wird mittels des Zeichens & definiert und muss initialisiert werden:
int i;
int &ir = i;
ir = 7;
cout << i; //Ausgabe: 7 ir, i 7
Technisch ist eine Referenz ein Zeiger, der automatisch dereferenziert wird
Die Adresse einer Referenz entspricht der Adresse der referenzierten Variable:
&i == &ir; //true
105
Kontextabhngige Interpretation der Operatoren * und &
Die Operatoren * und & haben in C++ drei unterschiedliche Bedeutungen
Die genaue Bedeutung ist jeweils auf Grund des Kontexts ersichtlich.
int &ir1 = i = 3 * *(&i+2*i);
106
Dynamischer Speicher
Wird whrend des Programmlaufs zustzlich zu den vorhandenen Variablen Speicherplatz bentigt, so
kann dieser vom Betriebssystem angefordert werden. Der Operator new liefert einen Zeiger auf ein
Element vom gewnschten Typ:
int *ip = new int;
Ist kein Speicherplatz mehr vorhanden, dann lst
*ip = 5;
new eine Exception vom Typ std::bad_alloc aus.
int &ir = *ip;
cout << ir; //Ausgabe: 5
(Wahlweise kann auch der Nullzeiger
zurckgeliefert werden.)
Der allozierte Speicher bleibt so lange reserviert, bis er mit dem Operator delete wieder freigegeben (oder
das Programm beendet) wird.
delete ip;
*ip = 5; //Effekt nicht definiert, da Speicher nicht mehr reserviert
ip = 0; //Nach delete Zeiger auf 0 setzen (defensive Programmierung)
Mit dem Operator new[ ] kann ein ganzes Feld alloziert werden. Dieses muss dann mittels delete[ ] wieder
freigegeben werden.
ip = new int[10];
ip[5] = 5;
delete[] ip; Position der
Klammern beachten!
107
Initialisierung von dynamisch alloziertem Speicher
C++ hat lange kein syntaktische Mglichkeit angeboten, dynamisch allozierten Speicher direkt zu
initialisieren.
Seit C++98 Mglichkeit der zero initialization: new int[10]()
Mit C++03 uminterpretiert zu value initialization: Konstruktoraufruf, falls Konstruktor definiert, sonst mit 0
Seit C++11 neue durchgngige Initialisierungssyntax (Universal Initalizer): new int[]{1,2}
Kleinere nderungen noch mit C++14
Verfgbarkeit abhngig von der verwendeten
Compilerversion. Eventuell mssen Optionen beim
Start des Compilers gesetzt werden.
int i{42};
int i[]{0,0,7}; Beachte fehlendes = Zeichen!
char c[]{"abc"};
108
Beispiel: Zeigerverbiegen (1)
#include<iostream>
using namespace std;
int main() {
cout << "Zeigerverbiegungen" << endl << endl;
int *ip;
{ // warum wohl diese Klammern ??
cout << "Der Tragoedie erster Teil: Datentyp int" << endl;
int v = 4711;
int *pv = &v;
int **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
int f[10] = {0,1,2,3,4,5,6,7,8,9};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
ip = new int; // warum nicht ip = &v?
*ip = v;
}
{
cout << endl << "Funktioniert aber auch mit double" << endl;
double v = 47.11;
double *pv = &v;
double **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
double f[10] = {0,0.5,1,1.5,2,2.5,3,3.5,4,4.5};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
cout << "Dynamisches Element: " << *ip << endl;
}
109
Beispiel: Zeigerverbiegen (2)
{
cout << endl << "Und erst recht mit char" << endl;
char v = 'x';
char *pv = &v;
char **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
char f[10] = {'z','a','y','b','x','c','w','d','v','e'};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
delete ip;
}
cout << endl << endl;
return(0);
}
110
Beispiel: Zeigerverbiegen (Ausgabe)
Zeigerverbiegungen
Und erst recht mit char
Der Tragoedie erster Teil: Datentyp int Die Werte sind: x, 0x7fff232dbdbc und 0x7fff232dbda8
Die Werte sind: 4711, 0x7fff232dbdbc und 0x7fff232dbda8 v = x = x = x
v = 4711 = 4711 = 4711 Index: 0 Wert: z Adresse: 0x7fff232dbdc0
Index: 0 Wert: 0 Adresse: 0x7fff232dbd80 Index: 1 Wert: a Adresse: 0x7fff232dbdc1
Index: 1 Wert: 1 Adresse: 0x7fff232dbd84 Index: 2 Wert: y Adresse: 0x7fff232dbdc2
Index: 2 Wert: 2 Adresse: 0x7fff232dbd88 Index: 3 Wert: b Adresse: 0x7fff232dbdc3
Index: 3 Wert: 3 Adresse: 0x7fff232dbd8c Index: 4 Wert: x Adresse: 0x7fff232dbdc4
Index: 4 Wert: 4 Adresse: 0x7fff232dbd90 Index: 5 Wert: c Adresse: 0x7fff232dbdc5
Index: 5 Wert: 5 Adresse: 0x7fff232dbd94 Index: 6 Wert: w Adresse: 0x7fff232dbdc6
Index: 6 Wert: 6 Adresse: 0x7fff232dbd98 Index: 7 Wert: d Adresse: 0x7fff232dbdc7
Index: 7 Wert: 7 Adresse: 0x7fff232dbd9c Index: 8 Wert: v Adresse: 0x7fff232dbdc8
Index: 8 Wert: 8 Adresse: 0x7fff232dbda0 Index: 9 Wert: e Adresse: 0x7fff232dbdc9
Index: 9 Wert: 9 Adresse: 0x7fff232dbda4 Letzter Wert indirekt: e
Letzter Wert indirekt: 9
112
5. Geltungsbereich und Funktionen
Geltungsbereich
114
Lokaler Geltungsbereich
115
Globaler Geltungsbereich
Ausgabe: 12 116
Funktionen (1)
117
Funktionsdefinition
Ergebnistyp FName ( [ Parameterdef { , Parameterdef } ] )
{ { Anweisung; } }
muss i.a. vor (im Quelltext oberhalb) dem Aufruf stehen
Ergebnistyp kann auch void sein (Prozedur)
Parameterdefinition entspricht (lokaler) Variablenvereinbarung
formale Parameter werden beim Aufruf durch aktuelle Parameter initialisiert
Ergebnis muss durch
return Ausdruck;
zurckgegeben werden
double sqr(double a) {
return; (bzw. Erreichen
double b = a*a;
der uersten }) verlsst die return b;
Funktion (Prozedur) ohne }
Rckgabe eines Ergebnisses
118
Ablaufsteuerung
Funktionsaufruf
FName ( [ Argument { , Argument } ] )
verzweigt zum Unterprogramm FName
initialisiert die formalen Parameter von FName mit den aktuellen Parametern
(Argument)
setzt nach Beendigung von FName das Programm unmittelbar hinter der Stelle
des Aufrufs fort
kann einen Wert zurckliefern
...
cout << sqrt(a*a+b*b);
...
119
Funktionen (2)
#include<iostream>
using namespace std;
void B() {
cout << "1 "; Ausgabe:
}
void A() {
42135
cout << "2 ";
B();
cout << "3 ";
}
Programmablauf
int main() {
cout << "4 "; beginnt hier
A();
cout << "5 ";
return 0; Rckgabe
}
120
Funktionen (3)
#include<iostream> sqr() stellt wie main() eine
using namespace std; Funktion dar. Ihr Ergebnistyp ist
#include <cmath> double, der Ergebnistyp von main()
ist int.
double sqr(double a) {
double b = a*a; Die Variablen a und b in sqr()
return b; sind von den Variablen a und b in
} main() total unabhngig, alle vier
sind jeweils nur innerhalb ihres
int main() { Blocks bekannt.
cout << "A, B: ";
double a, b;
cin >> a >> b;
cout << sqrt(sqr(a)+sqr(b));
return 0;
}
121
Funktionen (4)
#include<iostream> sqr() ist parametrisiert.
using namespace std; Ein Parameter ist ein Vehikel zur
#include <cmath> Kommunikation mit Unter-
double sqr(double a) {
programmen. Im Funktionskopf
return a*a; werden formale Parameter definiert,
} die innerhalb der Funktion wie lokale
Variablen wirken, jedoch beim Aufruf
der Funktion durch je einen
int main() { aktuellen Parameter initialisiert
cout << "A, B: "; werden.
double a, b;
cin >> a >> b;
cout << sqrt(sqr(a)+sqr(b));
return 0;
}
122
Funktionen (5)
Hier meldet uns der Compiler einen Fehler:
#include<iostream>
Beim Versuch, main zu kompilieren, stt er
using namespace std; auf den Funktionsaufruf sqr diese Funktion
#include <cmath>
kennt er aber noch nicht, sie wird ja erst weiter
unten definiert.
int main() {
cout << "A, B: ";
double a, b;
cin >> a >> b;
cout << sqrt(sqr(a)+sqr(b)); Lsung: entweder sqr vor main setzen
}
return 0;
unbekannt! oder
double sqr(double a) {
return a*a;
}
123
Funktionsdeklaration
#include<iostream> wir deklarieren die Funktion sqr (und
using namespace std; definieren sie spter).
#include <cmath> Eine Definition stellt ein Konstrukt (Variable,
Funktion, etc.) tatschlich zur Verfgung,
double sqr(double a); whrend eine Deklaration den bersetzer
lediglich ber diverse Eigenschaften eines
int main() {
(anderswo definierten) Konstruktes informiert.
cout << "A, B: ";
double a, b; Eine Funktionsdeklaration (ein Prototyp)
cin >> a >> b; entsteht aus einer Funktionsdefinition, indem
cout << sqrt(sqr(a)+sqr(b)); der Funktionsrumpf durch ; ersetzt wird.
return 0;
} Die Namen der formalen Parameter drfen
auch entfallen: double sqr(double);
double sqr(double a) {
return a*a; Die Kombination der Parametertypen
} (double) einer Funktion wird Signatur der
Funktion genannt.
124
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a 12300 999
} #1
void f (int x) {
999
int a = x; Hauptspeicher
g(a+2); 100 (Stack)
cout << a;
} #2
int main () { 14
int a = 999;
f(12); 100
g(a); ?
return 0;
} ?
.....
114121099 125
Rekursion
Ein Objekt heit rekursiv, wenn es durch sich selbst definiert ist, oder sich
selbst (teilweise) enthlt.
Beispiele:
Fernseher Mathematik
(Definition der Fakultt)
n 0 1
n!
n 0 n ( n 1) !
126
Rekursive Programmteile
Eine Prozedur/Funktion heit rekursiv, wenn sie sich in der Folge ihrer
Abarbeitung selbst referenziert.
127
Direkte Rekursion: Fakultt int fkt(int n) {
if(n == 0)
Berechnung von 3! return(1);
else
Aufruf: fkt(3) 6 return(n*fkt(n-1));
}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
128
Direkte Rekursion: Fibonacci Zahlen (1)
129
Direkte Rekursion: Fibonacci Zahlen (2)
130
Wiederholung: Programmelemente
#include<iostream> Programmelemente:
using namespace std; Prprozessoranweisung
#include <cmath>
Funktionsdefinition fr main
/*Hauptprogramm*/ Anweisung (Verbund/Block,
int main() { Verzweigung, Schleife)
float a, b, c;
cout << "A, B: "; Variablenvereinbarung
cin >> a; cin >> b; Zuweisung
try {
c = sqrt(a*a+b*b); Ausdruck
} Funktionsaufruf (sqrt())
catch (){
cout << "Fehler bei sqrt"; Exception Handling (try, throw, catch)
} Dynamischer Speicher (new, delete)
cout << c;
return 0; Kommentar (// /**/)
} // Ende Mit /* wird ein Kommentarblock begonnen,
der (auch ber mehrere Zeilen) bis zu einem
abschlieenden */ reicht
131
Wiederholung
132
6. Funktionen:
Parameterbergabe, berladen und
Exceptions
Wertparameter call by value
Funktionsparameter werden in C++ prinzipiell als Wertparameter bergeben. Das
heit, dass beim Aufruf eine Kopie des aktuellen Parameterwertes im formalen
Parameter gespeichert wird. Variable im aufrufenden Programm bleiben somit
unverndert (keine Seiteneffekte beim Aufruf einer Funktion):
Statt Zeigern knnen fr den gleichen Zweck auch Referenzen verwendet werden.
138
const - Parameter
139
Datenaustausch mit Unterprogrammen
Arrays werden immer als Zeiger bergeben und niemals kopiert. Daher
sind folgende Funktionsdeklarationen quivalent:
void foo(int par1[5])
void foo(int par1[])
void foo(int *par1)
141
Achtung Seiteneffekte
Die Verwendung von Funktionen mit Referenzparametern in Ausdrcken kann
wieder zu Seiteneffekten fhren. Es ist daher darauf zu achten, dass das Ergebnis
nicht von der Auswertungsreihenfolge abhngt.
int main() {
int a = 1, b;
b = f(&a); // a=2, b=1
b = a + f(&a); // a=3, b=4??
}
142
berladen von Funktionen
Beispiel:
int max (int a, int b);
double max (double a, double b); T und const T knnen
double max (double a, int b); beim Aufruf nicht
unterschieden werden.
int max (const int a, const int b); T*/T& und const T*/T&
aber schon.
char max (int i, int j);
Unterschied nur im
Returntyp reicht nicht
aus!
max(3,7.5); //ambigous call
143
Defaultargumente - Vorgaben fr formale Parameter (1)
Argumente, die zumeist mit immer dem gleichen Wert belegt werden,
knnen mit Vorgabewerten (Defaults, Standardwerten) belegt werden.
Die Vereinbarung von Default-Parametern kann entweder bei der
Deklaration oder der Definition erfolgen (ABER nicht gleichzeitig).
Deklaration:
int binomial(int n = 45, int k = 6); //Parameternamen optional
Definition:
int binomial(int n = 45, int k = 6) {}
Aufruf:
binomial(); //45 ber 6
binomial(10); //10 ber 6
binomial(11,7); //11 ber 7
binomial(,7); //ist falsch (leider)
144
Defaultargumente - Vorgaben fr formale Parameter (2)
int i;
int f(int i, int j=2*i); //Parameter i zur default Berechnung
145
Defaultargumente und berladen
Wird fr die Funktion auch eine Deklaration angegeben, dann ist das Schlsselwort inline
sowohl bei der Deklaration, als auch bei der Definition zu verwenden. 147
Funktionen und Exceptions (1)
Als Bild fr die Funktionsweise von Exceptions kann man sich eine
Befehlskette vorstellen (Funktion Person; Funktionsaufruf Befehl;
Exception Rckmeldung, falls Befehl nicht ausgefhrt werden kann).
148
Funktionen und Exceptions (2)
void g() { Es wird eine Kopie
int i = 3; von i angelegt und
als Exception main1 f1 g1 main2
cout << "g1 ";
throw i; gesendet. Warum?
cout << "g2 ";
int *ip = new int;
}
Dieser int Wert wird nicht
void f() {
freigegeben (der Zeiger ip
int i;
sehr wohl)
cout << "f1 ";
g();
cout << "f2 ";
}
int main() {
int i; Fangen der Exception mittels
cout << "main1 "; Referenz, vermeidet
try { neuerliche Kopie und
f(); ermglicht nderungen an
} der Exception (z.B. um sie
catch(int &err) {} dann weiterzureichen)
cout << "main2 ";
} 149
Rethrow
void f() {
int i = 3; 4
try {
int *ip = new int;
throw i;
}
catch(int &err) {
err = err + 1;
throw;
}
} Rethrow: Die
int main() { ursprnglich
int i; empfangene Exception
try { wird neuerlich geworfen
f();
}
catch(int &err) {
cout << err;
}
}
150
Exception - Spezifikation
Wirft die Funktion eine Exception von einem nicht spezifizierten Typ
(eventuell auch durch Aufruf einer anderen Funktion), so wird das
Programm abgebrochen
151
Wiederholung
Referenzparameter void foo(int &r, int *p);
Seiteneffekte int par=1; foo(par); cout<<par;
const-Parameter void foo(const int &r);
Default-Parameter inline void f(int=0);
inline-Funktionen
Funktionen berladen char f(double);
Exceptions try catch throw
Rethrow throw;
Catch by reference catch (int &err) {}
Exception-Spezifikation void foo() throw (Typ1,);
152
7. Aufzhltyp, logischer Datentyp,
spezielle Operatoren
Aufzhltyp (1)
154
Aufzhltyp (2)
155
Logischer Datentyp (1)
Der Datentyp bool umfasst die Wahrheitswerte false und true:
int main () {
int vorige, naechste;
bool sortiert = true;
cin >> vorige;
cin >> naechste;
while (naechste != 0) {
if (naechste < vorige)
sortiert = false;
vorige = naechste;
cin >> naechste;
}
cout << "Die Zahlenfolge war ";
if (!sortiert) // es sind alle logischen Operatoren erlaubt
cout << "nicht ";
cout << "aufsteigend sortiert.";
}
156
Logischer Datentyp (2)
bool gerade (int x) {
return x % 2 == 0;
}
Logischer Ausdruck
if (gerade(x))
Bitte nicht:
bool gerade (int x) {
if (x % 2 == 0)
return true;
else return false;
}
157
Logischer Datentyp (3)
Technisch entspricht false = 0, true = 1.
Aus historischen Grnden (C) kommt man auch ohne bool aus:
int gerade (int x) { // Bedeutung schlechter ersichtlich
return !(x % 2);
}
if (gerade(x))
158
Spezielle Operatoren (1)
Inkrement (++) und Dekrement (--)
Prefix: ++x (--x) erhht (vermindert) zunchst den Wert von x um 1 und liefert
dann den neuen Wert von x als Resultat
Suffix: x++ (x--) liefert zunchst den alten Wert von x als Resultat und erhht
(vermindert) dann den Wert von x um 1
x muss ein lvalue (left hand side value; Objekt, dem etwas zugewiesen werden kann) sein. So dass z.B.: 1++ und
(x+y)++ keine gltigen Ausdrcke sind. Da nur der Prefix Operator wieder einen lvalue liefert, ist zwar ++ ++x erlaubt,
nicht aber x++ ++
int x = 0, y;
y = x++;
cout << x << ',' << y; // Ausgabe: 1,0
x = --y;
cout << x << ',' << y; // Ausgabe: -1,-1
159
Spezielle Operatoren (2)
Sequenzoperator (,)
Wertet mehrere Ausdrcke in fest definierter Reihenfolge (von links nach rechts)
aus. Der Wert des gesamten Ausdrucks ist gleich dem zuletzt ausgewerteten
Ausdruck.
Wird meist in for-Konstrukten verwendet, wenn zur Initialisierung bzw.
Reinitialisierung mehrere Ausdrcke erforderlich sind.
Zu unterscheiden von Beistrich bei Variablendefinition und Parameterliste.
Das Ergebnis ist genau dann ein lvalue, wenn der uerst rechte Ausdruck ein lvalue ist. Der Typ des
Ergebnisses ist der Typ des uerst rechten Ausdrucks.
int i = 0, j = 3; // Keine Sequenz!
f(1,(j = 2, j), 3); // Drei Parameter (1,2,3)
Zuweisungsoperatoren
(*= /= %= += -= >>= <<= &= ^= |=)
stellen abgekrzte Schreibweisen fr hufig bentigte Zuweisungen dar
x <op>= y ist gleichbedeutend mit x = x <op> y, auer dass im ersten Fall der
Ausdruck x nur einmal ausgewertet wird
sind rechtsassoziativ wie normale Zuweisung
x muss ein lvalue sein. Das Resultat ist wieder ein lvalue und hat den Typ des linken Operanden
int x = 0, y = 1;
y += x += 1;
cout << x << ',' << y; // Ausgabe: 1,2
x *= 4;
cout << x << ',' << y; // Ausgabe: 4,2
(x += y++) += 3;
cout << x << ',' << y; // Ausgabe: 9,3 161
Spezielle Operatoren (4)
Bedingte Auswertung (? :)
Form: Ausdruck1 ? Ausdruck2 : Ausdruck3
rechtsassoziativ, d.h. x>0 ? 1 : x<0 ? -1 : 0
x>0 ? 1 : (x<0 ? -1 : 0)
Wertet zunchst den booleschen Ausdruck Ausdruck1 aus und falls das Ergebnis true ist,
wird Ausdruck2 ausgewertet, sonst Ausdruck3
Nur eine Alternative wird ausgewertet
Der Wert des gesamten Ausdrucks ist der Wert der ausgewerteten Alternative
Das Resultat ist nur dann ein lvalue, wenn beide Alternativen den gleichen Typ haben und lvalues sind. Der Typ des
Ergebnisses wird aus den Typen der Alternativen nach den blichen C++ Konversionsregeln bestimmt.
Beispiel:
short a = 11; // bei 2 Bytes pro short: 0000000000001011
short b = 12; // bei 2 Bytes pro short: 0000000000001100
cout << (a | b); // ODER; 15 = 0000000000001111
cout << (a & b); // UND; 8 = 0000000000001000
cout << (a ^ b); // XOR; 7 = 0000000000000111
cout << (~a); // NEGATION; -12 = 1111111111110100
cout << (a << 1); // L-SHIFT; 22 = 0000000000010110
cout << (a >> 1); // R-SHIFT; 5 = 0000000000000101
int arr[]={1,3,5};
int *ip = arr;
cout << *ip++; //Ausgabe: 1
cout << (*ip)++; //Ausgabe: 3
cout << *ip; //Ausgabe: 4
cout << arr[1]; //Ausgabe: 4
Achtung: Beziehungen wie x[y] entspricht *(x+y) und x++ entspricht x+=1
entspricht x=x+1, etc. gelten nur fr die C++ Standardoperatoren, nicht aber fr
berladene Operatoren.
Muss bei Bedarf extra programmiert werden!
165
Zeigerverbiegen die zweite
#include<iostream>
using namespace std;
void f (int i1, int *pi2, int &ri3) {
i1--;
*pi2++; Der Tragoedie zweiter Teil
ri3 += 5;
} 4711, 815, 671
void f1 (int i1, int *pi2, int &ri3){
i1--; 4711, 815, 676
(*pi2)++; // Auf die Klammer kommt es an 4711, 816, 681
ri3 += 5;
} 816, 681
void f2 (int **pp, int *p) {
int local = *p;
*pp = p;
// Was wuerde passieren, wenn man
// stattdessen *pp = &local; verwendete
}
int main() {
cout << "Der Tragoedie zweiter Teil" << endl;
int v1 = 4711, v2 = 815, v3 = 666;
int *pv2 = &v2;
int *pv3 = &v3;
int &rv2 = v2;
int **ppv2 = &pv2;
f(v1, &rv2, v3);
cout << v1 << ", " << v2 << ", " << v3 << endl;
f(v1, *ppv2, *pv3);
cout << v1 << ", " << v2 << ", " << v3 << endl;
f1(v1, *ppv2, *pv3);
cout << v1 << ", " << v2 << ", " << v3 << endl;
f2(ppv2, pv3);
cout << v2 << ", " << **ppv2 << endl;
cout << endl << endl;
return(0);
} 166
Wiederholung
167
8. Klassen Einfhrung
Klassen (1)
Klassen stellen benutzerdefinierte Datentypen dar, die analog zu den eingebauten
Datentypen verwendet werden knnen.
Beispiel:
int main () {
char str1[]="Ein";
//str1 += "s"; -> C++ Strings zu limitiert
cout << str1; // sollte Eins ausgeben
}
Wunsch:
int main () {
String str1 = "Ein";
str1 += "s";
cout << str1; // liefert Eins
}
Kann erreicht werden
169
Klassen (2)
Idee: Ein String
besteht aus
einer int-Zahl fr die maximale Lnge und
einer int-Zahl fr die aktuelle Lnge und
einem char-Array fr den Inhalt (die gespeicherten Zeichen)
maxlen (int) 5
len (int) 3 String
buf (char *)
und man kann mit ihm einiges machen:
einen anderen String anhngen,
einen Teilstring suchen/ersetzen, C
Zugriff auf eine bestimmte Zeichenposition +
Ausgeben/Einlesen +
???
???
170
Klassen (3)
class String {
int maxlen, len; Versteckte Daten (automatisch private:)
char *buf;
public:
void set (const char* source); ffentliche Operationen
int find(const String& substr);
String operator+ (const String& rightop);
String operator- (const String& rightop);
char& operator[](int index);
void print ();
};
String fill (char c, int count); Externe Operationen
171
Klassen (4)
bestehen aus (im Allgemeinen versteckten) Daten, die den Zustand (Inhalt)
ihrer Objekte (Instanzen) beschreiben
Bsp.: maxlen, len und buf
heien Instanzvariablen
Zugriff: Objekt.VName - knnen aber, wenn versteckt, von auen (= von
anderen Programmteilen) nicht direkt angesprochen werden
bieten (im Allgemeinen ffentliche) Funktionen an, die auf den Objekten
operieren
Bsp.: set (fllt Puffer und setzt aktuelle Lnge), print (Ausgabe)
heien Methoden
knnen, wenn ffentlich, von anderen Programmteilen (Klienten) aufgerufen
werden
drfen auch versteckte Instanzvariablen/Methoden (der Klasse, der sie
angehren) direkt manipulieren/aufrufen
Aufrufsyntax: Objekt.MName([Argument {, Argument}])
knnen auch als Operatoren (+, -, *...) formuliert werden 172
Klassen (5)
Beispiel zur Verwendung der Klasse:
int main () {
String s1, s2;
s1.set("Ein "); // len = 4, buf = "Ein ", maxlen = ?
s1.print(); // Ausgabe: Ein
s2 = fill('+', 2); // len = 2, buf = "++", maxlen = 2
s2.print(); // Ausgabe: ++
s2 = s1 + s2; // s2: len=6, buf="Ein ++", maxlen=6
s2.print(); // Ausgabe: Ein ++
(s1+s2).print(); // Ausgabe: Ein Ein ++
return(0);
} Operator + liefert ein (temporres) String
Objekt, das wie jedes andere verwendet
werden kann.
173
Klassen (6)
Implementation der Klasse = Definition ihrer Methoden
void String::set(const char* source) {
len = strlen(source); Methode set der
if (len > maxlen) {
if (buf) delete[] buf; Klasse String
maxlen = len;
buf = new char[maxlen];
}
for (int i=0; i<len; ++i) buf[i] = source[i];
}
Beim Aufruf einer Methode fr ein Objekt wird implizit ein zustzlicher Parameter
namens this bergeben. Dieser ist ein konstanter Zeiger auf das Objekt. Wird
innerhalb der Methode eine Instanzvariable oder Methode derselben Klasse ohne
Angabe eines Objektes verwendet, so wird automatisch das this-Objekt
verwendet. Statt len, maxlen und buf knnte somit auch (*this).len, (*this).maxlen
und (*this).buf verwendet werden.
Klammern unbedingt notwendig (. bindet
strker), -> Operator als syntaktisch schnere
Alternative:
this->len 174
Initialisierung von Objekten
void String::set(const char* source) {
Jede Methode darf davon ausgehen, dass das
len = strlen(source); Objekt, fr das sie aufgerufen wird, in einem
if (len > maxlen) { konsistenten Zustand ist. Sie muss am Ende das
if (buf) delete[] buf; Objekt auch wieder in einem konsistenten
maxlen = len; Zustand hinterlassen.
buf = new char[maxlen];
} Die Methode set funktioniert nur, wenn len,
for (int i=0; i<len; ++i) maxlen und buf sinnvolle Werte beinhalten.
buf[i] = source[i];
}
175
Konstruktoren (1)
Bei der Erzeugung von Instanzen (Objekten) einer Klasse wird eine
spezielle Methode (Konstruktor) aufgerufen, die die neue Instanz
initialisieren (d.h., ihre Instanzvariablen mit sinnvollen Werten belegen)
soll.
Diese Methode heit genauso wie die Klasse, hat keinen Rckgabewert
(auch nicht void) und beliebig viele Parameter, mit denen die
Initialisierungswerte festgelegt werden knnen.
Deklaration:
String(int maxlen = 0, const char* cstr = 0);
String(const char* cstr);
Implizite Deklaration von String() dem
Defaultkonstruktor zur Initialisierung von Default-(Standard-) Objekten. Wird bei Bedarf (und
wenn gar kein Konstruktor in der Klasse definiert ist) vom Compiler automatisch erzeugt.
(Dieser automatisch erzeugte Defaultkonstruktor fhrt keine speziellen Initialisierungen von
Instanzvariablen durch.) 176
Konstruktoren (2)
Implementierung
Implementierung
178
Konstruktoren (4)
Verwendung
int main() {
String s1; //Defaultkonstruktor
String s2(100); //String(int maxlen)
String s3("Test"); //String(const char* cstr)
String s4(100, "C++"); //String(int maxlen, const char* cstr)
String s5=50; //String(int maxlen) Zuweisungsnotation
String s6="Init"; //String(const char* cstr) fr Konstruktoren mit
String sa1[20]; genau einem
String *sa2 = new String[10]; Parameter
179
Konstruktoren und Typumwandlung
Konstruktoren mit genau einem Parameter knnen auch als Typumwandlung
angesehen werden und vom Compiler bei Bedarf zur impliziten Typumwandlung
verwendet werden.
String s("Test"); int find(const String& substr);
s.find("e");
Da es keine Funktion find mit genau einem Parameter vom Typ char* gibt, wird
implizit von char* auf String umgewandelt, mittels des Konstruktors
String(const char* cstr). Damit das nicht unbersichtlich wird, wendet der
Compiler pro Parameter maximal eine implizite Typumwandlung an.
180
Konversionsoperatoren
181
berladen von Operatoren (1)
Fast alle C++ Operatoren knnen berladen werden. Dabei bleiben Aritt,
Bindungsstrke und Assoziativitt unverndert, aber die Semantik der Operation
kann neu definiert werden.
Von den uns bekannten Operatoren knnen nur der Bereichsoperator (::) und die
bedingte Auswertung (?:) nicht berladen werden.
Missbrauch vermeiden!
Zum berladen eines Operators wird eine globale Funktion oder Methode erstellt,
deren Namen sich aus dem Wort operator und dem jeweiligen Operatorsymbol
zusammensetzt (z.B.: operator+)
Der erste (linke und fr unre Operatoren einzige) Operand wird im Falle einer
Methode immer als this-Objekt bergeben. Eine Operatormethode hat somit
immer einen Parameter weniger als der Operator Operanden hat. Eine globale
Operatorfunktion hat immer gleich viele Parameter wie der Operator Operanden
hat.
Manche Operatoren, wie z.B. die Zuweisung (=) knnen nur mit Methoden
berladen werden. Bei Postfix-Inkrement und -Dekrement wird zur Unterscheidung
von den analogen Prefix-Operatoren ein zustzlicher (ansonst redundanter)
Parameter vom Typ int verwendet.
182
berladen von Operatoren (2)
class String {
String operator+(String); //Methode fr binren Operator +
};
String operator-(String,String); //globale Funktion fr binres -
Aufruf:
main() {
String str1, str2;
str1+str2;
str1.operator+(str2); //mglich, aber eher ungewhnlich
str1-str2;
operator-(str1,str2); //mglich, aber eher ungewhnlich
str1+"mehr";
} Wegen impliziter Typumwandlung mglich. (Aber nicht "mehr"+str1, weil
keine implizite Typumwandlung beim this-Objekt durchgefhrt wird.)
Vorzugsweise werden Operatoren mit Methoden berladen. Globale Funktionen
nur wenn unbedingt ntig (wie z.B. String operator+(const char*,String)).
183
String::operator[ ]
Binrer Operator. Erster Operand ist das Feld (der Zeiger) und zweiter
Operand ist der Index. Durch Retournieren einer
Referenz kann diese Methode
auch zur nderung von Zeichen
im String verwendet werden.
char& String::operator[] (int index) {
if (!len) throw "Positionszugriff auf leeren String";
if (index<0 || index>=len) throw "Positionszugriff auf ungueltige Position";
return buf[index];
}
184
String::find
int String::find(const String& substr) {
for (int i=0; i<len; ++i) { this->len
int j;
for (j=0; j<substr.len && i+j<len && (*this)[i+j]==substr[j]; ++j);
if (j == substr.len) return i;
Aufruf von operator[]
}
Scheitert fr substr mit Fehlermeldung
return -1; error: passing <const String> as this argument of
} <char &operator[](int)> discards qualifiers
Mgliche Lsungen:
int String::find(String& substr) { //erlaubt nderungen am Parameter substr
int String::find(String substr) { //Wertparameter; weniger effizient
185
const Methoden
const ist ansteckend. Der Aufwand zahlt sich aber im Hinblick auf
korrekte und leichter wartbare Programme im Allgemeinen aus.
186
String::find effizientere Lsung
int String::find(const String& substr) {
for (int i=0; i<len; ++i) {
int j;
for (j=0; j<substr.len && i+j<len && buf[i+j]==substr.buf[j]; ++j);
if (j == substr.len) return i;
Direkter Feldzugriff mittels der Instanzvariablen buf
}
return -1;
}
187
String::operator+
188
String::operator-
189
String::print
void String::print() {
for (int i=0; i<len; ++i)
cout << buf[i];
}
190
inline Methoden
Einfache Methoden (Komponentenfunktionen) knnen auch als
inline-Funktionen formuliert werden:
191
Hochkommata
" " statt spitzen
Organisation der Programmteile (1) Klammern <>
g++
#include "string.h"
int main () {
String x; g++ stringtest.o string.o
} ld
Datei stringtest.C
stringtest 192
Organisation der Programmteile (2)
Alternative:
g++ -Wall string.C stringtest.C -o stringtest
(bersetzen & binden)
193
Funktionen vs. Methoden
(globale) Funktion Methode
Deklaration (Prototyp)
class C { /* ... */
C add (C op1, C op2); C add (C op2);
}; gehrt zu jedem C-Objekt
Definition (Implementation)
C add (C op1, C op2) { C C::add (C op2) {
/* ... op1.real + op2.real ... */ /* ... real + op2.real ... */
} }
Aufruf
z = add(x, y); z = x.add(y);
Operatorfunktion/-methode
C operator+ (C op1, C op2) {...} C C::operator+ (C op2) {...}
Aufruf
z = x + y; z = x + y;
z = operator+(x, y); z = x.operator+(y);
194
Erweiterungen der Klasse String
Viele Erweiterungen sind denkbar (Lnge des Strings, Ersetzen von Teilstrings,
Rechtschreibprfung, etc.).
Die meisten Klassen bieten sogenannte Accessoren (get-Methoden) und
Mutatoren (set-Methoden) zur Abfrage und Manipulation von Instanzvariablen. Im
Gegensatz zum direkten Zugriff auf die Variablen (die dann public definiert sein
mssten) knnen diese Methoden die Garantie aufrecht erhalten, dass Objekte
stets in einem konsistenten Zustand bleiben.
195
Wiederholung
Klassen class String {
Instanzvariablen int maxlen;
Zugriffserlaubnis public:
Methoden void print();
Konstruktoren String(int = 0, const char* = 0);
Typumwandlung explicit String(const char* cstr);
operator int ();
Operator berladen operator[](int index) const {
const-Methoden
inline-Methoden
this return this->buf[index];
}
};
196
9. Klassen Fortsetzung
Probleme mit der Klasse String
Die Klasse String ist so, wie sie im vorigen Kapitel implementiert wurde,
leider nicht verwendbar. Bei der praktischen Anwendung treten
unterschiedliche Probleme auf. Zum Beispiel:
for (int i=0; i>=0; ++i) {String s(100000); cout << i << endl;}
Ausgabe:
0
1
32040
32041
terminate called after throwing an instance of 'std::bad_alloc' Warum?
what(): St9bad_alloc
Aborted
198
Destruktoren (1)
Wird ein String-Objekt zerstrt, so geht der fr buf reservierte Speicher verloren,
da kein Aufruf des delete[ ]-Operators erfolgt.
Abhilfe: Destruktor-Methode
kann als Gegenstck zum Konstruktor aufgefasst werden
dient zum Aufrumen (statt zum Initialisieren)
heit wie die Klasse, allerdings mit vorangestelltem ~ (~String)
wird automatisch aufgerufen, wenn ihr Objekt zu existieren aufhrt
darf ber keine Parameter verfgen (daher nicht berladbar)
hat keinen Rckgabewert
class String {
//alles wie bisher Im Sinne einer defensiven
Programmierung knnten buf, len
public: und maxlen wieder auf 0 gesetzt
werden. Ist aber nicht notwendig
und ineffizient.
~String() {if (buf) delete[] buf;}
};
199
Destruktoren (2)
for (int i=0; i>=0; ++i) {String s(100000); cout << i << endl;}
Ausgabe:
0
1
2147483646
2147483647
Wird kein Destruktor definiert, so wird einer automatisch vom Compiler erzeugt.
Dieser kmmert sich nur um die Freigabe der automatisch erzeugten
Instanzvariablen.
Ein Destruktor sollte keine Exception werfen (wird whrend der Abarbeitung
einer Exception (stack unwinding) durch einen Destruktor eine weitere Exception
geworfen, so wird das Programm abgebrochen). Verwendet der Destruktor
Aufrufe, die eventuell eine Exception werfen knnten, so empfiehlt es sich, diese
zu fangen.
~Klasse() throw() {
try{}
catch(...){}
}
201
Destruktoren (4)
Fr automatische Objekte werden die Destruktoren in genau der umgekehrten
Reihenfolge aufgerufen, wie die Konstruktoren!
LIFO
int no=0;
Globale Variable kann
Warum?
class KDTest {
int val; vermieden werden K0
public: K1
KDTest() {cout<<"K"<<(val=no++)<<endl;} K2
~KDTest() {cout<<"D"<<val<<endl;}
K3
};
D3
class A {
KDTest kda; D2
}; D1
class B { K4
KDTest kd1; K5
KDTest kd2; K6
}; main() { D6
void f() { KDTest kd; K7
KDTest kd; f(); D7
B b;
B b; K8
}
for (int i=0;i<2;++i) KDTest kd; D8
A kd1; D5
} D4
D0 202
Weitere Probleme mit Klasse String
main() { void foo(String s) { main() {
String s = "Init"; } String s = "Init";
(s-"ni").print(); String s1 = s;
} main() { }
String s = "Init";
foo(s);
}
*** glibc detected *** double free or corruption (fasttop): 0x0804c120 ***
Aborted
203
Kopierkonstruktoren (1)
Problemlsung: Kopierkonstruktor-Methode
Konstruktor mit Argument vom selben Datentyp:
String (const String& original);
Argument muss (i.a. const-) Referenz sein! (Warum?)
wird aufgerufen, wenn ein Objekt mit einem anderen initialisiert wird
String t = s; // oder String t(s)
// auch bei call by value und bei temporren
// Variablen als Retourwert von Funktionen
204
Kopierkonstruktoren (2)
class Point {
String label;
double x;
double y;
Automatisch erstellter
Kopierkonstruktor und
}; Destruktor wrden
ausreichen.
205
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1;
s1 = s;
}
*** glibc detected *** double free or corruption (fasttop): 0x0804c120 ***
Aborted
206
Zuweisungsoperatoren (1)
Problemlsung: Zuweisungsoperator-Methode
String& operator= (const String& rightop);
Argument sollte (muss aber nicht) (wieder i.a. const-) Referenz sein
wird bei Zuweisungen mit passender rechter Seite aufgerufen
kann u.U. berladen sein Aufgrund der impliziten
String& operator= (const char* cstr); Typumwandlung nicht notwendig
String& operator= (const int val);
gibt konventionsgem (aber nicht unbedingt) eine Referenz auf die linke
Seite der Zuweisung zurck (das ermglicht Verkettungen von Zuweisungen in
der Art a = b = c)
207
Verhindert Fehler bei Zuweisung
einer Variable an sich selbst
Zuweisungsoperatoren (2)
Eventuell bereits reservierten
String& String::operator= (const String& rightop) { Puffer wieder freigeben
if (&rightop == this) return *this; (Wiederverwendung auch
mglich, aber verkompliziert
if (buf) delete[] buf; meist den Code)
maxlen = rightop.maxlen; len = rightop.len;
buf = maxlen ? new char[maxlen] : 0; Rest wie in
for (int i=0; i<len; ++i) buf[i] = rightop.buf[i]; Kopierkonstruktor
return *this; Retourwert fr verkettete
} Zuweisungen
208
Zuweisungsoperatoren (3)
class Point {
String label;
double x;
double y;
Automatisch erstellter
Zuweisungsoperator
wrde ausreichen.
};
209
Zuweisung versus Initialisierung (1)
210
Zuweisung versus Initialisierung (2)
211
Zuweisung versus Initialisierung (3)
class Point {
String label;
double x;
double y;
};
Point::Point(String label) { Point::Point(String label):label(label) {
this->label = label; }
}
Faustregel:
Instanzvariablen immer mit der Initialisierungssyntax initialisieren.
Wichtiges Detail:
String::String() : len(++n), maxlen(n){}
214
Ausgabe von Strings (2)
216
Ausgabe von Strings (4)
217
friend-Funktionen
Mittels des Schlsselworts friend kann eine Klasse globalen Funktionen
oder Methoden anderer Klassen den Zugriff auf private Instanzvariable
erlauben.
class String {
friend ostream& operator<< (ostream& o, const String &s);
Deklaration der
}; weiterhin globalen Funktion operator<<
als friend der Klasse String
220
Ausgabe von Strings (7)
void String::print(ostream &o) const {
for (int i=0; i<len; ++i)
o << buf[i]; Zustzlicher Parameter, der das Ziel
} der Ausgabeoperation festlegt
Aufruf in operator<<:
ostream& operator<< (ostream& o, const String &s) { //kein friend
s.print(o);
return o;
}
221
Eingabe von Strings
222
Klassenvariablen und Klassenmethoden
Fr Klassen, in denen dynamischer Speicher verwendet wird, mssen in aller Regel ein
Destruktor, ein Kopierkonstruktor und ein Zuweisungsoperator implementiert werden.
225
10. Rekursive Datenstrukturen
Listen und Bume
Rekursive Datenstruktur
Eine Datenstruktur heit zirkulr oder rekursiv, wenn sie sich in ihrer Definition
selbst referenziert.
Basismodell fr dynamisch erweiterbare Datenstrukturen
Liste
class List{
int value;
List *next; Liste Element mit angehngter Liste
};
Rekursion nur mit
Baum (binr)
Zeiger oder Referenz!
class Tree { Warum?
int value;
Tree *leftChild;
Tree *rightChild;
};
AC D E F GH I J K L MN OP QR C
B einfgen
228
Bearbeiten von rekursiven Datenstrukturen
230
Implementierung mittels zweier Klassen
Die bisher vorgestellte Implementierung hat den Nachteil, dass eine leere
Liste (ein leerer Baum) nicht durch ein normales Objekt darstellbar ist.
List l; Tree t; //in beiden Objekten bereits eine value Komponente vorhanden
Man kann das umgehen, indem man Zeiger verwendet, die fr leere
Objekte auf 0 gesetzt werden.
List *l = 0; Tree *t = 0;
Ist die Verwendung von Zeigern nicht erwnscht, so wird oft eine Lsung
mit zwei Klassen verwendet. Dabei ist eine Klasse fr die Elemente
zustndig, die andere fr die eigentliche Liste (den eigentlichen Baum).
class Element{ class Node {
int value; int value;
Element *next; Node *leftChild;
}; Node *rightChild;
class List { };
Element *head; class Tree {
} Node *root;
}
Werden jeweils im Konstruktor auf 0
gesetzt 231
Allgemeine Bume
Liste von
Zeigern auf
Kinder 232
Wiederholung
(binre) Bume
233
Anhang
Anhang: Escape-Sequenzen
\n Neue Zeile (new line)
\r Wagenrcklauf (carriage return)
\t Horizontaler Tabulator
\v Vertikaler Tabulator
\b Backspace
\f Neue Seite (formfeed)
\a Alarm
\\ Backslash
\' Apostroph
\" Anfhrungszeichen
\041 Zeichencode im Oktalsystem (Basis 8)
\x4F Zeichencode im Hexadezimalsystem (Basis 16) 235
Anhang: Wertebereich der Datentypen
Datentyp Speicherplatz in Byte Wertebereich (fr GNU C/C++ 32 bit)
Turbo C/C++ GNU C/C++ MCC68K Sharc C
C/C++
bool 1 true, false bzw. 0, 1 C++
signed char 1 1 1 4 -128 bis 127
unsigned char 1 1 1 4 0 bis 255
char 1 1 1 4 implementierungsabhngig
short, short int, 2 2 2 4 -32768 bis 32767 bzw.
signed short, signed -2147483648 bis 2147483647
short int
unsigned short, 2 2 2 4 0 bis 65535 bzw.
unsigned short int 0 bis 4294967295
int, signed int 2 4 4 4 -32768 bis 32767 bzw.
-2147483648 bis 2147483647
unsigned int, 2 4 4 4 0 bis 65535 bzw.
unsigned 0 bis 4294967295
long, long int, signed 4 4 4 4 -2147483648 bis 2147483647
long int
unsigned long, 4 4 4 4 0 bis 4294967295
unsigned long int
float 4 4 4 4 (1.18e-38 bis 3.4e38) / 7 Dezimalstellen genau
double 8 8 8 4 (2.3e-308 bis 1.7e308) bzw. (1.18e-38 bis 3.4e38) 15 (8)
Dezimalstellen genau
long double 10 10 8 8 (3.4e-4932 bis 1.1e4932) bzw. (2.3e-308 bis 1.7e308) 19 (15)
Dezimalstellen genau
enum 2 4 4 4 0 bis 65535 bzw. 0 bis 4294967295
238
Anhang: Operatoren bersicht (3)
operator name example
sizeof size of object sizeof expr
sizeof() size of type sizeof (type)
++ pre increment ++lvalue
-- pre decrement --lvalue
~ complement ~expr
! not !expr
- unary - -expr
+ unary + +expr right
& address of &lvalue
* dereference *expr
new create (allocate) new type
delete destroy (de-allocate) delete pointer
delete[ ] destroy array delete[ ] pointer
() cast (type) expr
239
Anhang: Operatoren bersicht (4)
operator name example
.* member selection object.*pointer_to_member
->* member selection pointer->*pointer_to_member left
* multiply expr*expr
/ divide expr/expr left
% modulo expr%expr
+ add expr+expr
left
- subtract expr-expr
== equal expr==expr
!= not equal expr!=expr left
242
Anhang: Operatoren bersicht (7)
244
Anhang: Syntaxbeschreibung
Achtung: Die Metasymbole [ ], / und { } drfen nicht mit den C++ Symbolen
[], | und {} verwechselt werden
245
Anhang: Schlsselwrter in C/C++
246
Anhang: Die Klasse String (1)
#include <cassert>
#include <cstring>
using namespace std;
class String {
int maxlen, len;
char *buf;
public:
String(int maxlen=0, const char* cstr=0);
String (const String& original);
String(const char* cstr);
~String() {if (buf) delete[] buf;}
249
Anhang: Die Klasse String (4)
int String::find(const String& substr) {
for (int i=0; i<len; ++i) {
int j;
for (j=0; j<substr.len && i+j<len && buf[i+j]==substr.buf[j]; ++j);
if (j == substr.len) return i;
}
return -1;
}
250
Anhang: Die Klasse String (5)
String String::operator+ (const String& rightop) {
int newmax = len + rightop.len;
if (newmax<maxlen) newmax = maxlen;
if (newmax<rightop.maxlen) newmax = rightop.maxlen;
String res(newmax);
res.len = len + rightop.len;
for (int i=0; i<len; ++i) res.buf[i] = buf[i];
for (int i=0; i<rightop.len; ++i) res.buf[i+len] = rightop.buf[i];
return res;
}
252
Thematische Inhaltsbersicht
Thematische Inhaltsbersicht (1)
Anweisungen 1,3
Entscheidung if, switch 3
Exceptions try, catch, [assert] 3
Funktionsaufruf 5
Iteration while, do, for 3
Sequenz (Block) 3
Wertzuweisung 1,2
Ausdrcke 1,2
Auswertung 2
Spezielle Operatoren 7
Typkonversion 2
Explizit 2
Implizit 2
255
Thematische Inhaltsbersicht (3)
Funktionen 5,6
Deklaration/Definition 5
inline 6
Methoden 8
const 8
Destruktor 9
inline 8
Konstruktor 8
Operatormethoden 8
Zuweisungsoperator 9
Parameter 6
Defaultparameter 6
Referenzparameter 6
Wertparameter 6
berladen 6
Rekursion 5,10
256
Thematische Inhaltsbersicht (4)
Variable 1,2
Definition 2
Deklaration 8
Dynamische Objekte 4
Geltungsbereich 5
257
Dynamische Folien
Beispiel: Datentypen (1)
#include<iostream>
using namespace std;
int main() {
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
259
Pi ist gleich 3.14159
260
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
261
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
262
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
263
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
0.5 ist auch nicht viel, aber immerhin
Beispiel: Datentypen (1)
#include<iostream>
using namespace std;
int main() {
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
264
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
0.5 ist auch nicht viel, aber immerhin
Beispiel: Datentypen (1)
1 ist auch ohne Komma eine reelle Zahl
#include<iostream>
using namespace std;
int main() {
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
265
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
0.5 ist auch nicht viel, aber immerhin
Beispiel: Datentypen (1)
1 ist auch ohne Komma eine reelle Zahl
#include<iostream> i ist gleich 1 und ist ungerade
using namespace std;
int main() {
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
266
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche
0 ist gar nichts
0.5 ist auch nicht viel, aber immerhin
Beispiel: Datentypen (1)
1 ist auch ohne Komma eine reelle Zahl
#include<iostream> i ist gleich 1 und ist ungerade
using namespace std;
int main() { Viel Spass mit C++
int i = 1, j = 2;
const double pi = 3.14159;
double r = 1.2, U;
// Allgemein wird der "maechtigere" Datentyp fuer das Ergebnis
// des Ausdrucks verwendet
cout << "Pi ist gleich " << i*pi << endl;
U = 2*r*pi;
cout << "Der Umfang des Kreises mit Radius " << r << " betraegt " << U << endl;
// Aber bei Zuweisung wird bei Bedarf abgeschnitten
j = U;
cout << j << " Tage hat die Woche" << endl;
// Die Division liefert ein ganzzahliges Ergebnis, wenn beide Operanden
// ganzzahlig sind.
cout << i/2 << " ist gar nichts" << endl;
r = i; // r bleibt trotzdem eine reelle Zahl!
cout << r/2 << " ist auch nicht viel, aber immerhin" << endl;
cout << r << " ist auch ohne Komma eine reelle Zahl" << endl;
// Der Operator % ist (in C++) nur fuer ganzzahlige Operanden definiert
cout << "i" << " ist gleich " << i << " und ";
if ((i % 2) == 0) cout << "ist gerade" << endl;
else cout << "ist ungerade" << endl;
// r % 2; ist verboten
char first = 'C';
char rest[3]= "++"; // unterschiedliche Hochkommata und Laenge 3 beachten
cout << "Viel Spass mit " << first << rest << endl;
return 0;
}
267
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n ???
s 0
268
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n ???
s 0
i 1
269
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 0
s 0
i 1
270
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i; false
i = i + 1;
}
cout << s;
}
n 0
s 0
i 1
271
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 0
s 0
i 1 Ausgabe: 0 (das ist korrekt)
272
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n ???
s 0
273
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n ???
s 0
i 1
274
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 0
i 1
275
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i; true
i = i + 1;
}
cout << s;
}
n 3
s 0
i 1
276
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 1
i 1
277
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 1
i 2
278
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i; true
i = i + 1;
}
cout << s;
}
n 3
s 1
i 2
279
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 3
i 2
280
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 3
i 3
281
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i; true
i = i + 1;
}
cout << s;
}
n 3
s 3
i 3
282
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 6
i 3
283
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 6
i 4
284
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i; false
i = i + 1;
}
cout << s;
}
n 3
s 6
i 4
285
Wiederholungsanweisung (3)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}
n 3
s 6
i 4 Ausgabe: 6
286
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n ???
s 0
287
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n ???
s 0
i 1
288
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n 0
s 0
i 1
289
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n 0
s 1
i 1
290
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n 0
s 1
i 2
291
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s; false
}
n 0
s 1
i 2
292
Wiederholungsanweisung (4)
#include<iostream>
using namespace std;
int main() {
int n, s = 0;
int i = 1;
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}
n 0
s 1
i 2 Ausgabe: 1 (das ist falsch!)
293
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n ???
s 0
294
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 0
295
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 0
i 1
296
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
} true
n 3
s 0
i 1
297
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 1
i 1
298
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 1
i 2
299
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
} true
n 3
s 1
i 2
300
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 3
i 2
301
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 3
i 3
302
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
} true
n 3
s 3
i 3
303
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 6
i 3
304
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 6
i 4
305
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
} false
n 3
s 6
i 4
306
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 6
4
307
Wiederholungsanweisung (6)
#include<iostream>
using namespace std;
int main () {
int n, s = 0;
cin >> n;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
cout << s;
}
n 3
s 6 Ausgabe: 6
4
308
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
cout << "1 ";
}
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
Programmablauf
cout << "4 "; beginnt hier
A();
cout << "5 ";
return 0;
}
309
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 4
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
310
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 4
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
311
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 42
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
312
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 42
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
313
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 421
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
314
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 421
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
315
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 4213
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
316
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 4213
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
317
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 42135
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0;
}
318
Funktionen (2)
#include<iostream>
using namespace std;
void B() { Ausgabe:
}
cout << "1 "; 42135
void A() {
cout << "2 ";
B();
cout << "3 ";
}
int main() {
cout << "4 ";
A();
cout << "5 ";
return 0; Rckgabe
}
319
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; 12300 ?
} ?
void f (int x) { ?
int a = x; Hauptspeicher
g(a+2); ? (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); ?
g(a); ?
}
return 0; .....
320
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} ?
void f (int x) { ?
int a = x; Hauptspeicher
g(a+2); ? (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); ?
g(a); ?
}
return 0; .....
321
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} ?
void f (int x) { ?
int a = x; Hauptspeicher
g(a+2); ? (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); ?
g(a); ?
}
return 0; .....
322
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); ? (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); #1 ?
g(a); ?
}
return 0; .....
323
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); a12312 12 (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); #1 ?
g(a); ?
}
return 0; .....
324
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); a12312 12 (Stack)
cout << a; ?
} ?
int main () {
int a = 999;
?
f(12); #1 ?
g(a); ?
}
return 0; .....
325
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); #2
a12312 12 (Stack)
cout << a; #2
} y12320 14
int main () {
int a = 999;
?
f(12); #1 ?
g(a); ?
}
return 0; .....
326
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); #2
a12312 12 (Stack)
cout << a; #2
} y12320 14
int main () {
int a = 999; a12324 100
f(12); #1 ?
g(a); ?
}
return 0; .....
327
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); #2
a12312 12 (Stack)
cout << a; #2
} y12320 14
int main () {
int a = 999; a12324 100
f(12); #1 ?
g(a); ?
}
return 0; .....
114 328
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); #2
a12312 12 (Stack)
cout << a; #2
} y12320 14
int main () {
int a = 999; a12324 100
f(12); #1 ?
g(a); ?
}
return 0; .....
114 329
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); a12312 12 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); #1 ?
g(a); ?
}
return 0; .....
11412 330
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { x12308 12 Hauptspeicher
int a = x;
g(a+2); a12312 12 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); #1 ?
g(a); ?
}
return 0; .....
11412 331
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #1
void f (int x) { 12 Hauptspeicher
int a = x;
g(a+2); 12 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); ?
}
return 0; .....
11412 332
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #3
void f (int x) { y12308 999 Hauptspeicher
int a = x;
g(a+2); 12 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); #3 ?
}
return 0; .....
11412 333
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #3
void f (int x) { y12308 999 Hauptspeicher
int a = x; a12312
g(a+2); 100 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); #3 ?
}
return 0; .....
11412 334
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #3
void f (int x) { y12308 999 Hauptspeicher
int a = x; a12312
g(a+2); 100 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); #3 ?
}
return 0; .....
114121099 335
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #3
void f (int x) { y12308 999 Hauptspeicher
int a = x; a12312
g(a+2); 100 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); #3 ?
}
return 0; .....
114121099 336
Funktionsaufrufmechanismus
void g (int y) { .....
int a = 100;
cout << y+a; a12300 999
} #3
void f (int x) { y12308 999 Hauptspeicher
int a = x; a12312
g(a+2); 100 (Stack)
cout << a; #2
} 14
int main () {
int a = 999;
100
f(12); ?
g(a); ?
}
return 0; .....
114121099 337
Direkte Rekursion: Fakultt int fkt(int n) {
if(n == 0)
Berechnung von 3! return(1);
else
Aufruf: fkt(3) return(n*fkt(n-1));
}
fkt(3)
338
Direkte Rekursion: Fakultt int fkt(int n) {
if(n == 0)
Berechnung von 3! return(1);
else
Aufruf: fkt(3) return(n*fkt(n-1));
}
fkt(3)
3*fkt(2)
339
Direkte Rekursion: Fakultt int
int fkt(int
fkt(int n) n) {{
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
else
else
Aufruf: fkt(3) return(n*fkt(n-1));
return(n*fkt(n-1));
}}
fkt(3) fkt(2)
3*fkt(2)
340
Direkte Rekursion: Fakultt int
int fkt(int
fkt(int n) n) {{
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
else
else
Aufruf: fkt(3) return(n*fkt(n-1));
return(n*fkt(n-1));
}}
fkt(3) fkt(2)
3*fkt(2) 2*fkt(1)
341
Direkte Rekursion: Fakultt int fkt(int n) {
int
int fkt(int
fkt(int n)n) {{
if(n
if(n == 0)
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else
Aufruf: fkt(3) else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}
3*fkt(2) 2*fkt(1)
342
Direkte Rekursion: Fakultt int fkt(int n) {
int
int fkt(int
fkt(int n)n) {{
if(n
if(n == 0)
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else
Aufruf: fkt(3) else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}
343
Direkte Rekursion: Fakultt int fkt(int n) {{
int
int fkt(int
fkt(int n)
n) {{
int
if(n
if(n fkt(int
==
== 0)
0) n)
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else return(1);
Aufruf: fkt(3) else
else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}}
344
Direkte Rekursion: Fakultt int fkt(int n) {{
int
int fkt(int
fkt(int n)
n) {{
int
if(n
if(n fkt(int
==
== 0)
0) n)
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else return(1);
Aufruf: fkt(3) else
else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
345
Direkte Rekursion: Fakultt int fkt(int n) {{
int
int fkt(int
fkt(int n)
n) {{
int
if(n
if(n fkt(int
==
== 0)
0) n)
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else return(1);
Aufruf: fkt(3) else
else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
return(1)
346
Direkte Rekursion: Fakultt int fkt(int n) {
int
int fkt(int
fkt(int n)n) {{
if(n
if(n == 0)
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
return(1);
else
else
Aufruf: fkt(3) else
return(n*fkt(n-1));
return(n*fkt(n-1));
return(n*fkt(n-1));
}}
}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
return return(1)
(1*1)
347
Direkte Rekursion: Fakultt int
int fkt(int
fkt(int n) n) {{
if(n
if(n ==
== 0)
0)
Berechnung von 3! return(1);
return(1);
else
else
Aufruf: fkt(3) return(n*fkt(n-1));
return(n*fkt(n-1));
}}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
348
Direkte Rekursion: Fakultt int fkt(int n) {
if(n == 0)
Berechnung von 3! return(1);
else
Aufruf: fkt(3) return(n*fkt(n-1));
}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
349
Direkte Rekursion: Fakultt int fkt(int n) {
if(n == 0)
Berechnung von 3! return(1);
else
Aufruf: fkt(3) return(n*fkt(n-1));
}
n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)
350
Direkte Rekursion: Fibonacci Zahlen (1)
351
Direkte Rekursion: Fibonacci Zahlen (1)
352
Direkte Rekursion: Fibonacci Zahlen (1)
353
Direkte Rekursion: Fibonacci Zahlen (1)
354
Direkte Rekursion: Fibonacci Zahlen (1)
356
Direkte Rekursion: Fibonacci Zahlen (1)
357
Direkte Rekursion: Fibonacci Zahlen (1)
358
Direkte Rekursion: Fibonacci Zahlen (1)
359
Direkte Rekursion: Fibonacci Zahlen (1)
360
Direkte Rekursion: Fibonacci Zahlen (1)
361
Direkte Rekursion: Fibonacci Zahlen (1)
362
Direkte Rekursion: Fibonacci Zahlen (1)
363
Direkte Rekursion: Fibonacci Zahlen (1)
364
Direkte Rekursion: Fibonacci Zahlen (1)
365
Direkte Rekursion: Fibonacci Zahlen (1)
366
Direkte Rekursion: Fibonacci Zahlen (1)
367
Direkte Rekursion: Fibonacci Zahlen (1)
368
Direkte Rekursion: Fibonacci Zahlen (1)
369
Direkte Rekursion: Fibonacci Zahlen (1)
370
Zeiger (5)
int i = 3;
double d = 1.5;
int *ip = &i;
double *dp = &d;
Compiler Warnung:
*ip = *ip + *dp; converting to <int>
ip = (int *)dp; from <double>
cout << *ip;
371
Zeiger (5)
int i = 3; .....
double d = 1.5; i 3
int *ip = &i; ?
double *dp = &d; ?
?
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
372
Zeiger (5)
int i = 3; .....
double d = 1.5; i 3
int *ip = &i; d 1.5
double *dp = &d; ?
?
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
373
Zeiger (5)
int i = 3; .....
double d = 1.5; i 3
int *ip = &i; d 1.5
double *dp = &d; ip
?
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
374
Zeiger (5)
int i = 3; .....
double d = 1.5; i 3
int *ip = &i; d 1.5
double *dp = &d; ip
dp
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
375
Zeiger (5)
int i = 3; .....
double d = 1.5; i 4
int *ip = &i; d 1.5
double *dp = &d; ip
dp
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
376
Zeiger (5)
int i = 3; .....
double d = 1.5; i 4
int *ip = &i; d 1.5
double *dp = &d; ip
dp
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip; ?
?
?
?
.....
377
Zeiger (5)
int i = 3; .....
double d = 1.5; i 4
int *ip = &i; d 1.5
double *dp = &d; ip
dp
*ip = *ip + *dp;
ip = (int *)dp;
?
cout << *ip;
Ausgabe: 0 ?
?
?
?
.....
378
Zeiger (5)
int i = 3; .....
double d = 1.5; ip i 4
int *ip = &i; d 1.5
dp 00000000
double *dp = &d; ip
00000000 dp
*ip = *ip + *dp; 00000000
?
ip = (int *)dp; 00000000
cout << *ip;
Ausgabe: 0 ?
00111111
?
11111000
?
00000000
?
00000000 .....
379
Zeiger (5)
int i = 3; .....
float d = 1.5; ip i 4
Verwendung
int *ip = &i; von float, damit d 1.5
dp 00111111
float *dp = &d; beide ip
Datentypen 11000000 dp
*ip = *ip + *dp; gleich lang sind. 00000000
?
ip = (int *)dp; 00000000
cout << *ip; ?
?
?
?
.....
380
Zeiger (5)
int i = 3; .....
float d = 1.5; ip i 4
Verwendung
int *ip = &i; von float, damit d 1.5
dp 00111111
float *dp = &d; beide ip
Datentypen 11000000 dp
*ip = *ip + *dp; gleich lang sind. 00000000
Hilft auch nicht! ?
ip = (int *)dp; 00000000
cout << *ip; ?
?
Ausgabe: ?
1069547520 ?
.....
381
int main() {
cout << "Zeigerverbiegungen" << endl << endl;
int *ip;
{ // warum wohl diese Klammern ??
cout << "Der Tragoedie erster Teil: Datentyp int" << endl;
int v = 4711;
int *pv = &v;
int **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
int f[10] = {0,1,2,3,4,5,6,7,8,9};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
ip = new int; // warum nicht ip = &v?
*ip = v; Zeigerverbiegungen
}
Der Tragoedie erster Teil: Datentyp int
Die Werte sind: 4711, 0x7fff232dbdbc und
0x7fff232dbda8
v = 4711 = 4711 = 4711
Index: 0 Wert: 0 Adresse: 0x7fff232dbd80
Index: 1 Wert: 1 Adresse: 0x7fff232dbd84
Index: 2 Wert: 2 Adresse: 0x7fff232dbd88
Index: 3 Wert: 3 Adresse: 0x7fff232dbd8c
Index: 4 Wert: 4 Adresse: 0x7fff232dbd90
Index: 5 Wert: 5 Adresse: 0x7fff232dbd94
Index: 6 Wert: 6 Adresse: 0x7fff232dbd98
Index: 7 Wert: 7 Adresse: 0x7fff232dbd9c
Index: 8 Wert: 8 Adresse: 0x7fff232dbda0
Index: 9 Wert: 9 Adresse: 0x7fff232dbda4
Letzter Wert indirekt: 9 382
{
cout << endl << "Funktioniert aber auch mit double" << endl;
double v = 47.11;
double *pv = &v;
double **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
double f[10] = {0,0.5,1,1.5,2,2.5,3,3.5,4,4.5};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
cout << "Dynamisches Element: " << *ip << endl;
}
383
{
cout << endl << "Und erst recht mit char" << endl;
char v = 'x';
char *pv = &v;
char **ppv = &pv;
cout << "Die Werte sind: " << v << ", " << (void *)pv << " und " << (void *)ppv << endl;
cout << "v = " << v << " = " << *pv << " = " << **ppv << endl;
char f[10] = {'z','a','y','b','x','c','w','d','v','e'};
for (int i=0; i < 10; i=i+1)
cout << "Index: " << i << " Wert: " << f[i] << " Adresse: " << (void *)(f + i) << endl;
pv = f + 9; // Aequivalent zu &f[9]
cout << "Letzter Wert indirekt: " << *pv << endl;
delete ip;
}
cout << endl << endl;
return(0);
}
Und erst recht mit char
Die Werte sind: x, 0x7fff232dbdbc und 0x7fff232dbda8
v = x = x = x
Index: 0 Wert: z Adresse: 0x7fff232dbdc0
Index: 1 Wert: a Adresse: 0x7fff232dbdc1
Index: 2 Wert: y Adresse: 0x7fff232dbdc2
Index: 3 Wert: b Adresse: 0x7fff232dbdc3
Index: 4 Wert: x Adresse: 0x7fff232dbdc4
Index: 5 Wert: c Adresse: 0x7fff232dbdc5
Index: 6 Wert: w Adresse: 0x7fff232dbdc6
Index: 7 Wert: d Adresse: 0x7fff232dbdc7
Index: 8 Wert: v Adresse: 0x7fff232dbdc8
Index: 9 Wert: e Adresse: 0x7fff232dbdc9
Letzter Wert indirekt: e 384
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a ?
int help = i; b ?
i = j;
j = help; ?
} ?
}
int main() { ?
int a,b; ?
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 385
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; ?
} ?
}
int main() { ?
int a,b; ?
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 386
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; ?
} ?
}
int main() { ?
int a,b; ?
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 387
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 7
}
int main() { j 5
int a,b; ?
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 388
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 7
}
int main() { j 5
int a,b; ?
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 389
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 7
}
int main() { j 5
int a,b; help 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 390
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 5
}
int main() { j 5
int a,b; help 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 391
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 5
}
int main() { j 7
int a,b; help 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 392
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} i 5
}
int main() { j 7
int a,b; 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 393
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} 5
}
int main() { 7
int a,b; 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 394
Referenzparameter call by reference (1)
Manchmal ist es aber gewnscht, dass eine Funktion die Werte der Variablen im
aufrufenden Programmteil verndern kann.
.....
void sort(int i, int j) {
if (i>j) { a 7
int help = i; b 5
i = j;
j = help; #1
} 5
}
int main() { 7
int a,b; 7
cin >> a >> b;
sort(a,b); ?
assert(a<=b); Scheitert, wenn fr
return 0; b ein kleinerer Wert
?
} eingegeben wurde, ?
als fr a ..... 395
Referenzparameter call by reference (2)
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte
407
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4
main() {
String s = "Init";
(s-"ni").print();
}
408
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4
main() {
String s = "Init";
(s-"ni").print();
}
Konstruktoraufruf
409
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4
main() { n
String s = "Init"; i
(s-"ni").print(); t
}
Erstes Argument
via this 410
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4
i
main() {
String s = "Init"; I
(s-"ni").print(); n
}
i
t
412
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4
i
main() {
String s = "Init"; I
(s-"ni").print(); n
}
i
t
413
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 4
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; n
}
i
t
main() {
String s = "Init";
(s-"ni").print(); ?
} ?
?
414
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; n
}
i
t
main() {
String s = "Init";
(s-"ni").print(); ?
} ?
?
415
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; n
}
i
t
main() {
String s = "Init";
(s-"ni").print(); ?
} ?
?
416
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; n
}
i
t
main() {
String s = "Init";
(s-"ni").print(); I
} ?
?
417
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; n
}
i
t
main() {
String s = "Init";
(s-"ni").print(); I
} t
?
418
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this;
Es muss eine
n
} (temporre) Kopie
i
erstellt werden
(Warum?). t
main() {
String s = "Init";
Merkregel: Niemals eine Referenz oder I
(s-"ni").print();
einen Zeiger auf eine lokale Variable
} t
retournieren! ?
?
419
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); res maxlen 4
res.len=len-rightop.len; len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
Komponentenweise
return *this; n
Kopie
}
i
t
main() {
String s = "Init";
(s-"ni").print(); I
maxlen 4
} t
len 2
?
buf
?
420
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { Lokale Variable pos 1
String res(maxlen); res wird res maxlen 4
res.len=len-rightop.len; zerstrt. len 2
assert(res.len>=0); buf
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} I
return *this; Speicher wird n
} im Destruktor
i
freigegeben!
t
main() {
String s = "Init";
(s-"ni").print(); I
maxlen 4
} t
len 2
?
buf
?
421
s maxlen 4
len 4
buf
Weitere Probleme mit Klasse String rightop maxlen 2
len 2
buf
String String::operator- (const String& rightop) {
int pos=this->find(rightop); this
if (pos>=0) { pos 1
String res(maxlen); 4
res.len=len-rightop.len; 2
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i];
for(int i=pos+rightop.len; i<len; ++i)
n
res.buf[i-rightop.len] = buf[i];
i
return res;
} Zugriff auf bereits
I
return *this; freigegebenen
n
Speicher!
}
i
t
main() {
String s = "Init";
(s-"ni").print(); I
maxlen 4
} t
len 2
Temporres Objekt ?
buf
?
422
Weitere Probleme mit Klasse String
void foo(String s) {
}
s maxlen 4
len 4
main() {
buf
String s = "Init";
foo(s);
I
}
n
423
Weitere Probleme mit Klasse String
void foo(String s) {
}
s maxlen 4
len 4
main() {
buf
String s = "Init";
foo(s);
I
}
n
Call by value kopiert
den aktuellen i
Parameter in den t
formalen
424
Weitere Probleme mit Klasse String
void foo(String s) {
}
s maxlen 4
len 4
main() {
buf
String s = "Init";
foo(s);
s maxlen 4
} len 4
buf
I
n
425
Weitere Probleme mit Klasse String
void foo(String s) {
}
Destruktoraufruf fr s maxlen 4
Parameter len 4
main() {
buf
String s = "Init";
foo(s);
s maxlen 4
} len 4
buf
I
n
426
Weitere Probleme mit Klasse String
void foo(String s) {
}
s maxlen 4
len 4
main() {
buf
String s = "Init";
foo(s);
4
} Speicher wird
4
im Destruktor
freigegeben!
I
n
427
Weitere Probleme mit Klasse String
void foo(String s) {
}
s maxlen 4
len 4
main() {
buf
String s = "Init";
foo(s);
4
}
4
Destruktoraufruf fr
lokale Variable
I
n
428
Weitere Probleme mit Klasse String
void foo(String s) {
}
4
4
main() {
String s = "Init"; Destruktor
foo(s); versucht, nicht 4
} mehr reservierten
4
Speicher neuerlich
freizugeben!
I
n
429
Weitere Probleme mit Klasse String
main() {
String s = "Init";
String s1 = s; s maxlen 4
len 4
}
buf
I
n
430
Weitere Probleme mit Klasse String
main() {
String s = "Init";
String s1 = s; s maxlen 4
431
Weitere Probleme mit Klasse String
main() {
String s = "Init";
String s1 = s; s maxlen 4
len 4
}
buf
432
Weitere Probleme mit Klasse String
main() {
String s = "Init";
String s1 = s; s maxlen 4
len 4
}
buf
4
Zerstrung der lokalen
Variablen Destruktor fr s1 gibt 4
Speicher frei
I
n
433
Weitere Probleme mit Klasse String
main() {
String s = "Init";
4
String s1 = s;
4
}
4
Destruktor fr s
versucht nicht mehr
reservierten Speicher
neuerlich freizugeben I
n
434
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1; s maxlen 4
len 4
s1 = s;
buf
}
I
n
435
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1; s maxlen 4
len 4
s1 = s;
buf
}
s1 maxlen 0
len 0
buf
I
n
i
436
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1; s maxlen 4
len 4
s1 = s;
buf
}
Keine Initialisierung!
s1 maxlen 4
len 4
Keine Kopie!
buf
Komponentenweise
Zuweisung
I
n
i
437
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1; s maxlen 4
len 4
s1 = s;
buf
}
4
Zerstrung der lokalen
Variablen 4
Destruktor fr s1 gibt
Speicher frei
I
n
i
438
Noch immer nicht alle Probleme der Klasse String gelst
main() {
String s = "Init";
String s1; s maxlen 4
len 4
s1 = s;
buf
}
4
4
Destruktor fr s
versucht nicht mehr
reservierten Speicher
neuerlich freizugeben I
n
i
439
Vergleich mit Array
B einfgen
440
Vergleich mit Array
AC D E F GH I J K L MN OP Q R
B einfgen
441
Vergleich mit Array
AC D E F GH I J K L MN OP QR
B einfgen
442
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
443
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
444
Vergleich mit Array
AC D E F GH I J K L M N O PQR
B einfgen
445
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
446
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
447
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
448
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
449
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
450
Vergleich mit Array
AC D E F G H I J K L MN O PQR
B einfgen
451
Vergleich mit Array
AC D E F GH I J K L MN O PQR
B einfgen
452
Vergleich mit Array
AC D E FGH I J K L MN O PQR
B einfgen
453
Vergleich mit Array
AC D E FGH I J K L MN O PQR
B einfgen
454
Vergleich mit Array
AC D E FGH I J K L MN O PQR
B einfgen
455
Vergleich mit Array
A C D E FGH I J K L MN O PQR
B einfgen
456
Vergleich mit Array
AB C D E FGH I J K L MN O PQR
457
Vergleich mit Array
B einfgen
458
Vergleich mit Array
B einfgen
459
Vergleich mit Array
B einfgen
B
R
Neues Element mit
gewnschtem Inhalt
erzeugen
460
Vergleich mit Array
B einfgen
B
R
Einhngen
461
Vergleich mit Array
462
Vergleich mit Array
D
Es wurden keine aufwndigen
Verschiebungen im Speicher
bentigt! B
463