Sie sind auf Seite 1von 463

Einfhrung in die Programmierung

Theorievortrag
Peter Beran,
Kristof Bhmer,
Reinhold Dunkl,
Sonja Kabicher-Fuchs,
Georg Kaes,
Martin Polaschek,
Manfred Schttengruber,
Helmut Wanek (Vortragender)

Mit Beitrgen von C. Bruckmann, M. Hitz, T. Mck und E. Schikuta


Inhalt und Semesterplan
Kapitel Folie Datum
1. Grundlagen; ein erstes Programm 6-39 04.03.
2. Datentypen, Variable und Ausdrcke 40-58 12.03.
3. Anweisungen und Fehlerbehandlung 59-88 18.03.
4. Felder, Zeiger und Referenzen 89-112 08.04.
Wiederholung (keine neuen Stoffinhalte) 15.04.
5. Geltungsbereich und Funktionen 113-132 22.04.
6. Funktionen: Parameterbergabe, berladen und Exceptions 133-152 29.04.
Wiederholung (keine neuen Stoffinhalte) 06.05.
7. Aufzhltyp, logischer Datentyp, spezielle Operatoren 153-167 13.05.
Kein Repetitorium; kein Vortrag 20.05.
8. Klassen Einfhrung 168-196 27.05.
Wiederholung (keine neuen Stoffinhalte) 03.06.
9. Klassen Fortsetzung 197-223 10.06.
Wiederholung (keine neuen Stoffinhalte) 17.06.
10. Rekursive Datenstrukturen Listen und Bume 226-233 24.06.

Anhang 232-250
Thematische Inhaltsbersicht 251-255
2
Testplan

Test Datum Stoffgebiet (Kapitel)


01 15.03. 1. Grundlagen; ein erstes Programm
02 05.04. 2. Datentypen, Variable und Ausdrcke
03 12.04. 3. Anweisungen und Fehlerbehandlung
04 19.04. 4. Felder, Zeiger und Referenzen
05 26.04. 4. Felder, Zeiger und Referenzen
06 03.05. 5. Geltungsbereich und Funktionen
07 10.05. 6. Funktionen: Parameterbergabe, berladen und Exceptions
08 24.05. 6. Funktionen: Parameterbergabe, berladen und Exceptions
09 31.05. 7. Aufzhltyp, logischer Datentyp, spezielle Operatoren
10 07.06. 8. Klassen Einfhrung
11 14.06. 8. Klassen Einfhrung
12 21.06. 9. Klassen Fortsetzung
13 28.06 Wiederholungstest

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

Online Prsentation im Web


erreichbar ber die Homepage
https://cewebs.cs.univie.ac.at/PRG/ss16
enthlt zustzlich alle dynamischen Folien (Symbol )

5
1. Grundlagen
Ein erstes Programm
Was ist Informatik

Die Wissenschaft der Informatik umfasst alle Modelle (Methoden,


Verfahren, Konzepte, etc.), die dazu dienen, eine gegebene Eingabe in
eine beliebige (gewnschte) Ausgabe zu verwandeln

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

Beschreibung von Ursache-Wirkungszusammenhngen,


von Verfahren mit bekannten Folgen
Weies Licht erhlt man,
wenn man rotes, grnes und blaues Licht mischt

9
Algorithmus

Unter Algorithmus versteht man die schrittweise Vorschrift zur


Berechnung gesuchter aus gegebenen Gren, in der jeder Schritt
aus einer Anzahl eindeutig ausfhrbarer Operationen und einer
Angabe ber den nchsten Schritt besteht.

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)

Algorithmen knnen auf ganz unterschiedliche Art konzipiert werden


(Paradigmen):
prozedural versus funktional versus logik-basiert
orthogonal dazu objektorientiert (OO)
auf eine bestimmte Art konzipierte Algorithmen knnen auf ganz
unterschiedliche Art ausgedrckt werden (Darstellung):
natrlichsprachlich, in einer Programmiersprache,

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)!

3! 3(3-1)! 32! 32(2-1)! 321! 321(1-1)! 3210! 3211 6


hier: kann ersetzt werden durch

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

Der Lsungsweg ist durch eine Folge von Anweisungen vorgegeben


Anweisungen knnen Werte in Variablen zwischenspeichern, lesen und verndern

15
Algorithmendarstellung

Wie man einen Algorithmus darstellen kann, hngt vom gewhlten


Paradigma ab!
Fr das prozedurale Paradigma bieten sich insbesondere an:

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

Pro: leicht erfassbar, Standardnotation, auch dem Anwender verstndlich (Diskussionsbasis)


Con: groe Programme unberschaubar, schwierig direkt in Programmiersprache umsetzbar, schwer editierbar, automatische Codegenerierung eher 17
beschrnkt (meist nur Prozedurkpfe)
Darstellung Pseudocode
Programmiersprachenderivate Stilisierte Prosa
Anlehnung an eine Programmiersprache (z. B. Beschreibung der schrittweisen Ausfhrung
Pascal, MODULA-2);
Ziel, eine der natrlichen Sprache mglichst
nahe Kunstsprache zu schaffen (standardisierte
Darstellung!)

begin comment Das ist ALGOL; Schritt 1:Initialisiere.


integer n,i,summe; Setze summe auf 0.
read(n); Schritt 2:Abarbeitung der Schleife.
summe := 0; Solange n grer als 0,
for i := n step -1 until 1 addiere n zu summe,
do summe := summe + i; ziehe 1 von n ab.
print(summe) Schritt 3:Ausgabe.
end Drucke summe.

Pro: Programmiersprache sehr hnlich, direkte Umsetzung einfach


Con: hnliche Komplexitt wie Programmiersprache, fr den Anwender oft schwer verstndlich
18
Darstellung Programmiersprache
Kenneth E. Iverson, 1979
Notation as a Tool of Thought
Inventor of APL
+/N Summe von 1 bis N
Sortieren eines Vektors

int summe = 0;
while(n > 0) {
summe += n;
n--;
}
cout << summe;

Pro: (fast) direkt ausfhrbar


Con: komplex, fr Entwurf nur sehr beschrnkt einsetzbar
19
Programmiersprachen Paradigmen

Damit ein Computer ein Problem lsen kann, mssen wir ihm den
Algorithmus in Form eines Programms, das in einer
Programmiersprache geschrieben ist, prsentieren

Klassifikation von Programmiersprachen: Programmiersprachen-


Paradigmen
prozedural versus funktional versus logik-basiert
sequentiell versus parallel
typisiert versus untypisiert (flieender bergang)

orthogonal dazu: objektorientiert

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

dritte Problemorientierte Sprachen FORTRAN, COBOL, Pascal, MODULA-2, C


vierte Datenbanksprachen SQL, Natural-2
fnfte Sprachen der KI Lisp, PROLOG
? Objektorientierte Sprachen Smalltalk
? Hybride Sprachen C++

prozedural versus funktional versus logik-basiert


sequentiell versus parallel
typisiert versus untypisiert (flieender bergang)

orthogonal dazu: objektorientiert


21
Kategorisierung

Kategorisierung der hier prsentierten Teilmenge von C++:


prozedural (Wertzuweisung an Variablen, beliebig viele Zuweisungen pro
Variable)
sequentiell (Anweisungen werden in der von der Programmiererin
vorgegebenen Reihenfolge ausgefhrt)
typisiert (Variablen haben vordefinierte Wertebereiche, die zunchst eng
mit den Operand-Typen der gngigen Prozessoren korrespondieren;
byte, int, long int, float ...)

Im Folgenden nur mehr:


Prozedurales Paradigma
Das prozedurale Paradigma: Anweisungen, Variablen
22
Anweisungen

Elemente einer prozeduralen Programmiersprache


Die Wertzuweisung ist eine Anweisung
Die Sequenz von Anweisungen ist eine Anweisung
Die Entscheidung ist eine Anweisung
Die Iteration ist eine Anweisung
Satz (Bhm/Jacopini 1966):
Jedes durch einen Computer ausfhrbare Programm kann durch eine
Kombination dieser vier Elemente berechnet werden

23
Variablen im berblick

Um in einem Programm mit Werten umgehen zu knnen, bentigen wir


Variablen.
Variablen enthalten einen Wert. Der Wert einer Variablen kann beliebig oft
gelesen und geschrieben werden.
nichtnegative ganze Zahl
anzahlFichten 12

Name sieben Ziffern Datentyp


meineMatrikelnummer 0 2 0 1 2 3 4
Zeichenkette
meinVorname H u g o
einAndererVorname M a x i m i l i a n 24
Datentypen

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

Wieviel Speicherplatz ein Objekt eines bestimmten Datentyps bentigt, hngt


vom Wertebereich des Datentyps ab
(exakt: von der Implementation des Datentyps in einer Programmiersprache)
Die Gre des Speicherplatzes wird in Bytes angegeben
1 Byte umfasst 8 Bits
Ein Bit kann den Wert 0 oder 1 annehmen
1 Byte kann daher 28 = 256 verschiedene Werte annehmen
Beispiele
Gre(<{0..255},{+,-,*,/}>) = 1 Byte
Gre(<{-32768..32767},{+,-,*,/}>) = 2 Byte
Gre(<{wahr, falsch},{,,}>) = 1 Byte (warum?)

26
Variablen im Detail

Eine Variable ist ein Speicherplatz bestimmter Gre fr Objekte eines


bestimmten Datentyps.
Falls die Variable benannt ist, kann sie durch ihren Namen (einen
eindeutigen Bezeichner) angesprochen werden.
Variablen knnen stets durch ihre Adresse im Hauptspeicher
angesprochen werden.
0 ?
Beispiel: 1 ?
Variable X fr <{0..255},{+,-,*,/}>
addr(X) = 2, Inhalt(X) = 14, Gre(X) = 1 2 14

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

Variablendefinition mit nicht korrekter Initialisierung

#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

Direkte Ausgabe des Ausdrucks

#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

Algorithmus eindeutige schrittweise Vorschrift


Sprachparadigmen prozedural versus funktional versus logik-basiert
Programm
Programmiersprache
prozedurales Paradigma Anweisungen, Variablen mit nderbarem Wert
Anweisungen Wertzuweisung, Sequenz, Entscheidung, Iteration
Datentypen <{0..255},{+,-,*,/}>
Werte 14, "Hugo"
Variablen(namen) anzahlFichten, meinVorname
Wertzuweisung anzahlFichten = 14

39
2. Datentypen, Variable und Ausdrcke
Datentypen und Speicherplatz

Einige in C++ vordefinierte Datentypen, deren Gren und Beispiele fr


Literale:
int 4 float 4
0
double 8
010 14.3
-4711 -3.1E2
0x7FFFFFFF 1E-1

char 1 char[n+1] n+1 Achtung: Strings


'A' "C++" (Zeichenketten)
Achtung: char mit doppelten
'\033'
Literale mit 'C' '+' '+' '\0' Hochkommata
'\n' einfachen
Hochkommata Anhang: Escape-Sequenzen
41
Fundamentale Datentypen
Auswahl, implementationsabhngig
Datentyp Beschreibung Gre Wertebereich Beispiele
bool Logischer Wert 1 true false
char Zeichen 1 0 bis 255 'a', '$', '4', '\n'...
short ganze Zahl 2 -32768 bis 32767 -10, 0, 12, 32767
int ganze Zahl 4 -2147483648 bis -10, 0, 12, 32767,
2147483647 2147483647
long ganze Zahl 4
float Gleitkommazahl 4 (1.18*10-38 bis -10.123, 0, 12, 12.0,
3.4*1038) ...
(7 Dezimalstellen genau)

double lange 8 (3.4*10-308 bis -10.123, 0, 12, 12.0,


Gleitkommazahl 1.1*10308) ...
(15 Dezimalstellen genau)

Anhang: Wertebereich der Datentypen


42
signed, unsigned und sizeof
Fr alle (integralen) Datentypen (char, int, short, long) gibt
es die Attribute signed und unsigned, die die Bedeutung des
fhrenden Bits ndern, unsigned definiert Variable ohne
Vorzeichenbit, vergrert den positiven Wertebereich (z.B.
unsigned short, Zahlen 0 bis 65535)

sizeof() liefert die Gre eines Datenobjekts oder Datentyps


cout << "sizeof long double: " << sizeof(long double) << '\n';
// wenn s eine Variable vom Typ short int ist:
cout << "sizeof s: " << sizeof(s) << '\n';
cout << "sizeof lf string: " << sizeof("\n") << "\n";
cout << "sizeof lf char: " << sizeof('\n') << '\n';

Ausgabe dazu: // kennzeichnet in C++ einen


sizeof long double: 16 Zeilen-Kommentar, d.h. der
sizeof s: 2 nachfolgende Text bis zum
sizeof lf string: 2 Zeilenende wird vom
Grenangaben sind relativ zum Compiler berlesen
sizeof lf char: 1 Datentyp char, dessen Gre mit 1
definiert wird. 43
Variablenvereinbarung

Um dem Compiler zu erlauben, fr Variablen genau den bentigten


Speicherplatz festzulegen und Werte richtig zu deuten, mssen Variable
vereinbart (definiert) werden.

Variablenvereinbarung (einfachste Form):


Datentyp Variablenliste;

Beispiele:
double X; int a, b, c; short int s;

Die Vereinbarung legt hier bereits den bentigten Speicherplatz an.

44
Initialisierung

Eine Alternative zur Wertzuweisung ist die Initialisierung einer Variable


im Zuge ihrer Vereinbarung:

Beispiel: int X = 3, Y = X * 7;

Nicht initialisierte Variablen enthalten bis zu ihrer ersten Wertzuweisung


keinen wohldefinierten Wert (zuflliges Bitmuster).

so nicht: int X; int Y = X * 7;

45
Ausdrcke

Ausdrcke sind Verknpfungen von Operanden durch entsprechende


Operatoren. Ihre Auswertung liefert einen (typisierten) Wert. Operanden
sind (evt. geklammerte) Teilausdrcke, Variablen, Konstanten und
Funktionsaufrufe.
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 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)

Die Auflsung von ungeklammerten Ausdrcken in Teilausdrcke wird


durch die Bindungsstrke und links/rechts-Assoziativitt der Operatoren
bestimmt.
In erster Linie regelt die Bindungsstrke (Operatorprioritt) die implizite
Klammerung von Teilausdrcken.
3 + 4 * 2 entspricht 3 + (4 * 2)
x * y + 5 * z entspricht (x * y) + (5 * z)
Bei Operatoren gleicher Bindungsstrke regelt die links/rechts-
Assoziativitt die implizite Klammerung von Teilausdrcken.
3 - 4 - 2 entspricht (3 - 4) - 2 linksassoziativ
x = y = z entspricht x = (y = z) rechtsassoziativ

Anhang: Operatoren bersicht


48
Ausdruckauswertung (2)

Die Auswertungsreihenfolge von Teilausdrcken ist in C++ im Allgemeinen


jedoch undefiniert.
Konstruktionen, die eine bestimmte Auswertungsreihenfolge voraussetzen,
liefern implementationsabhngige Ergebnisse und sind daher zu
vermeiden.

Negativbeispiel:
int x, y, z;
(x * y) + (5 * z) // welche Multiplikation zuerst?
x = (y = 3) + (y = 4); // hat y den Wert 3 oder 4?

Durch die Seiteneffekte wird


die Auswertungsreihenfolge
entscheidend
49
Konstanten
Konstanten werden durch das Voranstellen des Schlsselwortes const
vor eine Variablendefinition vereinbart. Der Wert der Variable muss bei der
Definition initialisiert werden und kann danach nicht mehr verndert
werden.
const char newline = '\n';
const double pi = 3.1415;
Ausdrcke, die nur aus (benannten oder unbenannten) Konstanten
bestehen, heien konstante Ausdrcke. Sie knnen vom bersetzer
ausgewertet werden.
const double rezpi = 1.0 / pi;
Zur besseren Unterscheidung zwischen benannten Konstanten (wie pi)
und unbenannten Konstanten (wie 1.0, true , ) werden letztere
auch Literale genannt.

50
Typkonversionen

Betrachten wir einen arithmetischen Ausdruck wie x + y,


wobei x und y zu verschiedenen arithmetischen Typen gehren.
Vor der Auswertung des Ausdrucks werden geeignete Typ-
Konvertierungen durchgefhrt (sogenannte automatische Konversionen,
Standardkonversionen).
Automatische Konversionen basieren auf einer Reihe von eher
komplizierten Konversionsregeln. Fr arithmetische Ausdrcke wie
x + y gilt jedoch eine einfache Faustregel:

Ausweiten auf den greren der beiden Datentypen

51
Typkonversionen Variante 1

double x = 12.5;
cout << 3 / 2 + x;
ostream int int double 12.5

int 1 Die Division zweier


ganzer Zahlen liefert
wieder eine ganze
Zahl, d.h. 3 / 2 == 1
double 1.0

double 13.5

ostream cout Ausgabe: 13.5 52


Typkonversionen Variante 2

double x = 12.5;
cout << 3. / 2 + x;
ostream double int double 12.5

double 2.0

double 1.5

double 14.0

ostream cout Ausgabe: 14 53


Typkonversionen type cast
Explizite Typkonversion (cast): In C++ kann man die Typumwandlung durch den
Prfixoperator (Typ) erzwingen:
hexadezimale Zahl,
Format 1: (Typ) Ausdruck
Bsp: int i = 0xFFFFFFFF;
beginnt mit 0x.
cout << i << " : " << (unsigned int) i;
Ausgabe: -1 : 4294967295
unsigned int bezeichnet
positive ganze Zahlen
Format 2 (nur wenn der Ziel-Typ einen Namen hat):
Typ (Ausdruck)
Bsp: double x = 15; int y = 2;
cout << int(x) / y;
Ausgabe: 7

54
Typkonversionen - automatisch

Automatische Typkonversionen bringen eine schreibtechnische


Vereinfachung, aber die Gefahr einer unbemerkten Konversion und eines
damit verbundenen unbeabsichtigten Informationsverlustes mit sich.
Explizite Konversionsangaben sind nicht nur oft notwendig,
man kann mit ihnen manche implizite Umwandlungen auch
dokumentieren:
int i; unsigned c;
...
i = int(c); // redundant, aber fr den Leser informativ
Anmerkung: Im aktuellen Sprachstandard sind weitere, spezielle
Typkonversionsoperatoren vorgesehen, z.B.:
i = static_cast<int>(c);

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

Datentyp int, double


Variablenvereinbarung int x, y; double z;
Wertzuweisung y = 3;
Initialisierung int x = y;
Ausdrcke 3 * x
Bindungsstrke 3 + x * z
Assoziativitt 3 - 4 - 5; x = y = 3;
Konstante const int a = 2;
Typkonversion 2 / 3.0 + 2 / 3
cast double (2)
(unsigned int) 3.0

58
3. Anweisungen und Fehlerbehandlung
Sequenz

Folge von nacheinander auszufhrenden Anweisungen


BEGIN Anweisung ( 1 ) ... Anweisung ( n ) END

Anweisung 1

Anweisung 1
Anweisung 2
Anweisung 2
Anweisung 3

Anweisung 3

60
Anweisungsblock

Ein C++-Programm besteht aus einer Folge von hintereinander


auszufhrenden Anweisungen: Auch Vereinbarungen
gelten als Anweisungen
{ Einzelne
float a; Anweisungen
werden mit ';'
cout << "A eingeben";
abgeschlossen
cin >> a;
} Geschwungene Klammern
begrenzen einen Anweisungsblock
Eine durch geschwungene Klammern begrenzte Anweisungsfolge wird als
Anweisungsblock (Sequenz, Verbundanweisung) bezeichnet

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;
}

Was passiert bei der Eingabe von z.B. 1 und 1?


Offensichtlich ist die Antwort "1 groesser als 1" nicht ganz korrekt.

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)

Logische Ausdrcke knnen aus arithmetischen Ausdrcken durch


Vergleichsoperatoren gebildet werden:
< > <= >= != == Achtung Falle!
Wahre Vergleiche liefern true, falsche false.
a == 4 // Vergleich
a = 4 // Zuweisung
Beispiel: 3 >= 2 liefert true (1)
3 == 2 liefert false (0) beides sind Ausdrcke!
Logische Ausdrcke knnen miteinander durch logische Operatoren
kombiniert werden:
&& || !
UND ODER NICHT

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

UND ODER NICHT


Whrend die Auswertungsreihenfolge von Ausdrcken in C++ im
Allgemeinen undefiniert ist, werden logische Ausdrcke (das sind in
diesem Zusammenhang Ausdrcke mit && und ||) immer strikt von links
nach rechts ausgewertet, und zwar nur solange, bis das Ergebnis des
Ausdrucks feststeht. daher
Beispiel: 3 < 4 || 2 > 3
true || ??? immer true
Ergebnis steht jetzt schon fest false && ??? immer false
68
Beispiel: Reihenfolge dreier Zahlen

Aufgabe: Bestimmen Sie die Reihenfolge dreier Zahlen


#include<iostream>
using namespace std;
int main() {
double a, b, c;
cout << "Geben Sie die 3 Zahlen ein: ";
cin >> a >> b >> c;
if(a >= b && b >= c) cout << "a >= b >= c";
else if(a >= c && c >= b) cout << "a >= c >= b";
else if(b >= a && a >= c) cout << "b >= a >= c";
else if(b >= c && c >= a) cout << "b >= c >= a";
else if(c >= a && a >= b) cout << "c >= a >= b";
else cout << "c >= b >= a";
}

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

Jedes else gehrt zum unmittelbar vorangehenden if.


if (B1)
if (B2)
A1;
else
// B1 && !B2
A2;

Abhilfe durch leeres else oder Klammern:


if (B1) if (B1) {
if (B2) if (B2)
A1; A1;
else ;
else } else
// !B1 // !B1
A2; A2;

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)

C++ kennt verschiedene Wiederholungsanweisungen:


Schleife mit Test am Beginn:
while( logischer-Ausdruck ) Eintrittsbedingung
Anweisung // Schleifenrumpf Anweisung

Schleife mit Test am Ende:


Anweisung
do Anweisung // Schleifenrumpf
while ( logischer-Ausdruck ); Verbleibensbedingung

In beiden Fllen wird die Anweisung im Schleifenrumpf solange


wiederholt, wie die Auswertung des logischen Ausdrucks wahr ergibt.

76
Wiederholungsanweisung (3)

#include<iostream> Test: n ist 0


using namespace std; Ergebnis (0) ist korrekt.
int main() {
int n, s = 0;
Eine while-Schleife kann auch gar
int i = 1;
nicht durchlaufen werden.
cin >> n;
while (i <= n) {
s = s + i;
i = i + 1;
}
cout << s;
}

77
Wiederholungsanweisung (4)

#include<iostream> Test: n ist 0


using namespace std; Ergebnis (1) ist sehr ungenau.
int main() {
int n, s = 0; Eine do-Schleife wird immer
int i = 1; mindestens einmal durchlaufen.
cin >> n;
do {
s = s + i;
i = i + 1;
} while (i <= n);
cout << s;
}

78
Wiederholungsanweisung (5)

#include<iostream> Dieses Muster tritt bei while-Schleifen


using namespace std; sehr hufig auf:
int main() { Die Schleifenbedingung (Test)
berprft eine Zhlvariable (i), die
int n, s = 0;
int i = 1; Initialisierung vor Beginn der Schleife initialisiert
cin >> n; und innerhalb der Schleife
while (i <= n) { Test verndert (reinitialisiert) wird.
s = s + i;
i = i + 1;
}
cout << s; Re-Initialisierung
}

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

Bedingungen so weit bzw. eng fassen, dass auch mgliche


Sonderflle korrekt behandelt werden.

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

Spezialfall reelle Zahlen:

for (double x=0; x!=1; x=x+0.1); Nicht


//mgliche Endlosschleife 1-eps<x<1+eps

for (double x=0; !((x>1-eps) && (x<1+eps)); x=x+0.1);


//mit einer geeigneten Konstanten eps

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)

In einem Feld (Array) werden mehrere Objekte gleichen Typs


zusammengefasst. Die einzelnen Feldelemente werden ber ihre
Positionsnummer (Index) angesprochen.
Der Index ist eine ganze Zahl zwischen 0 und (Elementanzahl-1).
Definition:
Datentyp Name [ Anzahl ] { [ Anzahl ] } ;
int vektor[3];
double matrix[3][3];
int kubus[10][10][10];

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)

Zeiger (Pointer) werden fr den indirekten Zugriff auf Daten verwendet.

Ein Zeiger enthlt die Adresse des Speicherplatzes, an dem das


Datenelement zu finden ist.

..... .....
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)

Bei Zeigern gibt es (im Unterschied zu normalen Variablen) zwei


verschiedene Manipulationsmglichkeiten (Zuweisungen):

..... .....
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

Zeigervariable werden vereinbart, indem in einer normalen Vereinbarung dem


Variablennamen ein * vorangestellt wird.
int * x, * y, z;
x und y sind Zeiger auf int-Objekte, z kann ein int-Objekt aufnehmen.

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)

Um die Adresse einer Variablen zu ermitteln, wird dem Namen der


Variable der Adressoperator & vorangestellt.

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.

int i = 3; double d = 1.5; int *ip = &i; double *dp = &d;


*ip = *ip + *dp; //OK, implizite Typumwandlungen; i wird 4
ip = dp; //keine implizite Typumwandlung!
ip = (int *)dp; //Erlaubt; (wahrscheinlich) berraschende
//Effekte
cout << *ip; //Ausgabe: ?

99
Zeiger (6)

Zeiger knnen auch andere Zeiger referenzieren.


Keine Dereferenz-
int i = 3; operatoren!
int *pi = &i;
int **ppi = &pi;
**ppi = *pi * 2;

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

Zugriff auf beliebige Speicherzellen mglich. (Wird von modernen


Betriebssystemen meist ein wenig entschrft.)

int *ip;
??? 42
*ip = 42;

ip ???

Defensive Programmierung: Zeiger immer initialisieren. Der Wert 0 kann


fr Zeiger verwendet werden, die (noch) nirgendwohin zeigen. Beim
Versuch den Nullzeiger (Nullpointer) zu dereferenzieren, wird das
Programm abgebrochen. Es kann daher kein unerwnschter
Speicherzugriff erfolgen.
int *ip = 0; Laut C++ -Standard fhrt das Dereferenzieren des Nullpointers zu
undefiniertem Verhalten (undefined behavior).
Auf den meisten Systemen ist es aber unbedenklich
101
Zeiger und Felder (1)

Zu Zeigern (Adressen) knnen ganzzahlige Werte addiert werden.


Fr diese Addition wird die sogenannte Zeigerarithmetik verwendet:
Der zum Zeiger addierte Wert wird als Offset in einem Feld interpretiert. Addition
des Werts 1 ergibt somit nicht die nchste (Byte)Adresse, sondern die Adresse des
nchsten Feldelements (unter der Annahme, dass der Zeiger ursprnglich auf ein
Element eines Feldes zeigte).

int ia[]={1,3,5}; 12300 1


int *ip = &ia[1] + 1; 12304 3
cout << *ip; //Ausgabe: 5
ip = ip - 2;
12308 5
cout << *ip; //Ausgabe: 1
cout << *(ip + 1); //Ausgabe: 3 ip
cout << *ip + 1; //Ausgabe: 2
ip 12300
ip+1 12304 12300+sizeof(*ip) 12300 + sizeof(int)
102
Zeiger und Felder (2)
Die Zeigerarithmethik erlaubt den Zugriff auf Feldelemente mittels eines Zeigers. Umgekehrt
kann ein Array als konstanter Zeiger auf das erste Feldelement interpretiert werden.

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:

int ia[] = {1,3,5};


int *ip1 = ia; //gleicher Wert wie &ia (allerdings anderer Datentyp)
int *ip2 = ia+2; //quivalent zu &ia[2]
int *ip3 = ia[1]; //Falsch! Die Differenz zweier
int diff = ip2-ip1; //diff == 2 Zeiger (vom gleichen
char str[] = "C++"; Typ) ergibt die Anzahl der
char *cp = str+1; Arrayelemente zwischen
cout << cp; //Ausgabe: ++ (I/O nur fr char *) den beiden Adressen
cout << ip1; //Ausgabe: Adresse von ia

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

Verwendung: Zur Vergabe von Namen fr sonst unbenannte Variable; Zur


Parameterbergabe bei Funktionsaufrufen.
int ia[] = {1,3,5};
int &elem = ia[1];
cout << elem; //Ausgabe: 3

105
Kontextabhngige Interpretation der Operatoren * und &
Die Operatoren * und & haben in C++ drei unterschiedliche Bedeutungen

Multiplikation / bitweises Und:


i * j;
i & j;
Dereferenzieren / Adressoperator:
*ip = 5;
ip = &i;
Definition eines Zeigers / einer Referenz:
int *ip;
int &ir = i;

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.

Universal Initalization kann auch fr normale Initialisierungen verwendet werden:

int i{42};
int i[]{0,0,7}; Beachte fehlendes = Zeichen!
char c[]{"abc"};

Wir werden Universal Initalization im Lauf der Lehrveranstaltung nicht verwenden.

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

Funktioniert aber auch mit double


Die Werte sind: 47.11, 0x7fff232dbdb0 und
0x7fff232dbda8
v = 47.11 = 47.11 = 47.11
Index: 0 Wert: 0 Adresse: 0x7fff232dbd30
Index: 1 Wert: 0.5 Adresse: 0x7fff232dbd38
Index: 2 Wert: 1 Adresse: 0x7fff232dbd40
Index: 3 Wert: 1.5 Adresse: 0x7fff232dbd48
Index: 4 Wert: 2 Adresse: 0x7fff232dbd50
Index: 5 Wert: 2.5 Adresse: 0x7fff232dbd58
Index: 6 Wert: 3 Adresse: 0x7fff232dbd60
Index: 7 Wert: 3.5 Adresse: 0x7fff232dbd68
Index: 8 Wert: 4 Adresse: 0x7fff232dbd70
Index: 9 Wert: 4.5 Adresse: 0x7fff232dbd78
Letzter Wert indirekt: 4.5
Dynamisches Element: 4711
111
Wiederholung

Feldvereinbarung int x[10]; int y[2][2];


Feldinitialisierung int x[10] = {7, 4};
char s[] = "C++";
char s[]={'C','+','+','\0'};
Feldverwendung x[0] = x[9] x[8];
Zeichenketten char s[] = "C++";
cout << sizeof(s) << sizeof(s[0]);
Zeiger int i, *p; p = &i; *p = i+1;
Zeigerarithmetik char s[] = "C++"; char *p = s+2;
Referenzen int i, &ri = i;
Dynamischer Speicher char *s = new char; delete s;
char *s = new char[7]; delete[] s;

112
5. Geltungsbereich und Funktionen
Geltungsbereich

Der Geltungsbereich eines Namens definiert jenen Teil des


Programmtextes, in dem der Name verwendet werden darf.

Als Verwendung gilt dabei jede Aufnahme des Namens im Programmtext


mit Ausnahme der Vereinbarung selbst.

Unter anderem unterscheidet man in C++ zwischen


lokalem Geltungsbereich und
globalem Geltungsbereich

114
Lokaler Geltungsbereich

Ein Name hat lokalen Geltungsbereich, wenn er innerhalb eines Blocks


vereinbart ist. Der Geltungsbereich beginnt dann mit der Vereinbarung des
Namens und endet mit dem Block.
Wird derselbe Name in einem inneren Block fr ein anderes Objekt
bentzt, berlagert diese Vereinbarung die uere.
int i = 1; // spter nicht benutzt
int main() {
int i = 2; // berlagert ueres int i
{
double i = 3.1415; // berlagert wiederum int i
cout << i;
} // Ende der Gltigkeit von double
cout << i << "\n";
} // Ende der Gltigkeit inneres int i
Ausgabe: 3.14152

115
Globaler Geltungsbereich

Ein Name hat globalen Geltungsbereich, wenn er auerhalb smtlicher


Blcke vereinbart ist. Der Geltungsbereich beginnt mit der Vereinbarung
und endet mit dem Programmtext.
Auf berlagerte globale Namen kann man mit dem Bereichsoperator ::
zugreifen.
int i = 1; // wird unten benutzt
int main() {
int i = 2; // berlagert ueres int i
{
double i = 3.1415; // wird nicht benutzt
cout << ::i; // bezeichnet uerstes i
} // Ende der Gltigkeit von double i
cout << i << "\n";
} // Ende der Gltigkeit inneres int i

Ausgabe: 12 116
Funktionen (1)

Teile eines Programms knnen auch an anderer Stelle als Unter-


programm formuliert sein, wobei der Ablauf bei Bedarf dorthin verzweigt
(Aufruf) und nach Abarbeitung des Unterprogramms wieder an die Stelle
des Aufrufs zurckkehrt (Rcksprung).

Arten von Unterprogrammen:


Einfache Prozeduren
Unterprogramme, die beim Rcksprung einen Wert an das
rufende Programm zurckliefern (Funktionen)

In C++ ist diese Unterscheidung unscharf, man spricht blicherweise nur


von Funktionen. C++ Funktionen, die keinen Wert zurckliefern, werden
mit dem Datentyp void gekennzeichnet.

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.

Eine Prozedur/Funktion P heit direkt rekursiv, wenn sie sich explizit


selbst aufruft. Hingegen ist P indirekt rekursiv, wenn sie den Aufruf einer
anderen Prozedur/Funktion enthlt, die ihrerseits wieder P (direkt oder
indirekt) aufruft.

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));
}

fkt(3) fkt(2) fkt(1) fkt(0)

n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)

return return return return(1)


(3*2) (2*1) (1*1)
3*2*1*1 2*1*1

128
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


Definition Fibonacci Zahlen:
if (n <= 0)
return 0; fibo(1) = 1
else fibo(2) = 1
if (n <= 2) fibo(n) = fibo(n-2) + fibo(n-1) fr n > 2
return 1;
else
return
fibo(n-2)+ fibo(n-1);
}
int main () {
cout << fibo(5) << endl;
}

129
Direkte Rekursion: Fibonacci Zahlen (2)

Aufrufgraph int fibo (int n){


if (n <= 0)
return 0;
5 else
fibo(5) if (n <= 2)
2 3
return 1;
fibo(3) fibo(4) else
1 1 1 2 return
fibo(2) fibo(1) fibo(2) fibo(3) fibo(n-2)+ fibo(n-1);
}
1 1 int main () {
fibo(2) fibo(1) cout << fibo(5) << endl;
}

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

lokaler Geltungsbereich { int i; }


globaler Geltungsbereich int i; { }
Bereichsoperator ::i
Funktion int sqr(int a) {return a*a;}
Rekursion unsigned add(unsigned a, unsigned b)
if (b == 0) return a;
return add(a + 1, b - 1);
}

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):

void f(int i, int j) {


j = j*i;
i = j*2;
cout << i << j;
}
int main() {
int i = 1;
int j = 2;
f(i,j);
cout << i << j;
return 0; i und j werden durch
} die Funktion nicht
verndert!
134
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 ..... 135
Referenzparameter call by reference (2)
Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die
Werte.
.....
void sort(int* i, int* j) {
if (*i>*j) { a 5
int help = *i; b 7
*i = *j;
*j = help; #1
}
}
int main() {
int a,b;
cin >> a >> b; Anmerkung: Die
7
sort(&a,&b); Parameterbergabe ?
assert(a<=b); erfolgt wieder by
return 0; value, nur werden ?
} Adresswerte kopiert. ?
..... 136
Referenzparameter call by reference (3)

Statt Zeigern knnen fr den gleichen Zweck auch Referenzen verwendet werden.

void sort(int& i, int& j) {


if (i>j) {
Vorteil: Syntaktisch
int help = i;
einfacher, daher
i = j;
bevorzugt zu
j = help;
verwenden
}
}
int main() {
int a,b;
cin >> a >> b; Nachteil: Mgliche
sort(a,b); Seiteneffekte (Vernderung
assert(a<=b); von Parameterwerten) nicht
return 0; aus Funktionsaufruf alleine
} erkennbar
137
Parameterbergabe

Arten der Parameterbergabe:


Wertparameter: Es werden die Inhalte der aktuellen Parameter in die formalen
Parameter kopiert. Es ist sichergestellt, dass die aktuellen Parameter nicht
verndert werden knnen. Damit knnen nur Eingangsparameter realisiert
werden.
Referenzparameter: Es werden die Adressen der zu bergebenden Objekte in
die formalen Parameter kopiert (Zeiger, Referenzen). Die bergebenen Objekte
knnen daher im Unterprogramm verndert werden. Damit knnen auch
Ausgangs- und Durchgangsparameter realisiert werden.
Referenzparameter werden oft auch aus Effizienzgrnden verwendet: Anstatt
groe Objekte zu kopieren, braucht nur die Adresse bergeben zu werden. In
diesem Fall will man aber gerade nicht, dass das Objekt verndert werden kann

138
const - Parameter

Werden Referenzparameter nur aus Effizienzgrnden verwendet, knnen


Seiteneffekte vermieden werden:

void foo(const int &par1, const int *par2)

Zeiger auf konstanten


int Wert. Im
Gegensatz zu
int *const
(konstanter Zeiger
auf int Wert)

139
Datenaustausch mit Unterprogrammen

x soll Eingabe bzw. Resultat eines Unterprogramms f darstellen:


Wert- Referenzparameter Funktions- Globale
parameter Zeiger Referenz wert Variable
-
Rich-
-
tung
- -
Ver- int x;
einbarung f(int x); f(int* x); f(int& x); int f(); int f();

int var; int var;


Funktions- int var; int var; int var; x = var;
f(var); f();
aufruf f(&var); f(var); var = f();
f(2*var); var = x; 140
Arrays als Parameter

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)

Bei mehrdimensionalen Arrays werden die Elementanzahlen fr die


Adressberechnung bentigt. Es darf daher nur die erste (innerste)
Dimension unterdrckt werden:
void foo(int par1[][4][5]) Bei Verwendung eines Zeigers
als Parameter muss die
Adressberechnung hndisch
programmiert werden.

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 f(int *x) {


int res = *x;
*x = *x+1;
return res;
}

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

Zwei (oder mehrere) Funktionen drfen denselben Namen haben, wenn


sie sich durch Anzahl oder Datentypen ihrer Parameter voneinander
unterscheiden. Der Compiler ordnet einem Aufruf (wenn mglich) die
passende Funktion zu.

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)

Vorsicht: Defaultargumente knnen nur am Ende der Parameterliste


definiert werden!
Wird fr einen Parameter ein Defaultwert
angegeben, so mssen auch alle Parameter rechts
int binomial(int = 45, int); davon ebenfalls mit einem Defaultwert versehen
werden.

Da die Reihenfolge, in welcher die Parameter ausgewertet werden, nicht


definiert ist, drfen bei der Initialisierung auch die anderen Parameter nicht
verwendet werden!

int i;
int f(int i, int j=2*i); //Parameter i zur default Berechnung

145
Defaultargumente und berladen

Im Prinzip werden durch die Angabe von Defaultwerten mehrere


berladene Funktionen deklariert/definiert.

int binomial(int n = 45, int k = 6);


entspricht
int binomial();
int binomial(int n);
int binomial(int n, int k);

Weitere berladungen sind mglich:


binomial(double x, double y = 10);
binomial(char *cstr);
Defaultwerte fr x
bzw. cstr hier
nicht sinnvoll!
Warum?
146
inline Funktionen
Manche Funktionen sind derart simpel, dass der fr den Aufruf der Funktion
erzeugte Code mehr Laufzeit (und Speicherplatz) kostet als der Code fr den
Funktionsrumpf:
double max (double a, double b){return a > b ? a : b;}
oder
double abs (double x) {return x>=0 ? x : -x;}
Mit dem Schlsselwort inline wird der Funktionsaufruf, falls mglich, vom
Compiler durch die Funktionsdefinition ersetzt:

inline double max(double a,double b){return a>b ? a:b;}


int main(){
double x,y,z;
...
z = max(x, y); x > y ? x : y;

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)

In Kombination mit Funktionen werden die Strken des Konzepts der


Exceptions offenbar.

Lst eine Funktion eine Exception aus, so werden alle Funktionsaufrufe


beendet, bis ein passendes catch-Statement gefunden (oder das
Betriebssystem erreicht) wird. Dabei wird der Speicherplatz der lokalen
Variablen aller abgebrochenen Funktionen ordnungsgem freigegeben
(stack unwinding). Dies gilt nicht fr mit new (bzw. new[ ]) angeforderten
Speicher.

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

Eine Exception Spezifikation legt fest, welche Typen von Exceptions


eine Funktion mglicherweise werfen kann.

void f() throw(int, char, Klasse) {}

Wirft die Funktion eine Exception von einem nicht spezifizierten Typ
(eventuell auch durch Aufruf einer anderen Funktion), so wird das
Programm abgebrochen

void f() throw() {} //Funktion wirft keine Exception

void f() {} //Funktion kann beliebige Exception werfen

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)

Der Aufzhltyp erlaubt es, statt einer Menge von konstanten


Integerwerten entsprechende Bezeichner zu verwenden:
enum days {sun,mon,tues,wed,thur,fri,sat}aDay;

Die Zuordnung von Werten zu den einzelnen Bezeichnern erfolgt implizit


von 0 beginnend der Reihe nach:
enum days {sun,mon,tues,wed,thur,fri,sat}aDay;
0 1 2 3 4 5 6
Die Zuordnung kann aber auch explizit erfolgen:
enum karten
{zehn=10, bube=2, dame, koenig, as=11};
(hier wird keine Variable, sondern nur der Typ karten und die Konstanten
definiert. dame erhlt den Wert 3 und koenig den Wert 4 zugewiesen)

154
Aufzhltyp (2)

Die Angabe des Namens des Aufzhlungstyps kann entfallen:


enum {sun,mon,tues,wed,thur,fri,sat} aDay;
Es knnen keine weiteren Variablen von diesem Typ vereinbart werden!
Verwendung: aDay = mon;

Will man nur die Konstanten sun = 0, mon = 1, ... definieren:


enum {sun, mon, tues, wed, thur, fri, sat};

Konversionen zwischen Aufzhltypen und int:


enum fuzzy { False, True, Unknown };
fuzzy b = False; // O.K.
b = 0; // Fehler
int i = False; // erlaubt

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;
}

Und auch nicht:


if (gerade(x) == true)

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))

cout << false << ' ' << true;


liefert 0 1

Typumwandlungen zwischen int und bool werden


(wegen eben dieser Historie) implizit durchgefhrt.

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)

for (i = j, j = 0; i > 0; i--, j++); //Leeranw.


cout << i << ',' << j; // Ausgabe: 0,2 160
Spezielle Operatoren (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.

for (int i = 0; i < 1000; ++i)


cout << ((i % 10) ? '*' : '\n');
// 9 Sterne pro Zeile; uere Klammern wichtig
162
Und wieder Seiteneffekte

Zuweisungsoperatoren, Inkrement und Dekrement liefern nicht nur einen Wert,


sondern ndern nebenbei noch die Werte von Variablen. Da in C++ die
Reihenfolge der Auswertung von Ausdrcken und Funktionsparametern und der
genaue Zeitpunkt, zu welchem die Seiteneffekte wirklich ausgefhrt werden, im
Allgemeinen nicht fix vorgegeben ist, darf das Programmergebnis nicht von der
Reihenfolge der Seiteneffekte abhngen!
x = x++; // x wird grer?
x = 0, y = x + ++x; // y = 1 ??
x = 1, y = (x*=3)+ x; // y = 4 ??
x = 0, f(x++, x+3); // f(0,4)??
y = ((x++ * 3)*x++); // ?? trotz Klammerung
y = x++ && (x % 5); // OK
y = x++ ? x+=3 : x*2; // OK
x = 2, x++, x++, y = x; // OK (y = 4) 163
Bitmanipulation

Zustzliche Operatoren fr ganze Zahlen, die auf den einzelnen Bits


operieren
i.a. fr systemnahe Programmierung

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

auch 1 mglich, Regeln relativ kompliziert


164
Spezielle Operatoren und Zeigerarithmetik
Die Zuweisungsoperatoren += und -= , sowie Inkrement- und
Dekrementoperatoren (++ und --) knnen auch im Zusammenhang mit Zeigern
verwendet werden. Es gilt dann die bliche Zeigerarithmetik.

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

Aufzhltyp enum Geschlecht {m, w} g;


logischer Datentyp bool a = true; cout << a+4;
spezielle Operatoren ++x x++ --x x-- , ? :
+= -= *= /= %=
>> << & ^ | ~
>>= <<= &= ^= |=
Seiteneffekte x = x++;

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];
}

int main() { Werte der Instanzvariablen sind


String s; C++ -typisch zunchst undefiniert. Sie
s.set("Test"); mssen erst initialisiert werden, um ein
} konsistentes String-Objekt zu erhalten.
Verwendung einer eigenen
Initialisierungsmethode ist unpraktisch
(sie msste fr jedes Objekt explizit
aufgerufen werden).
Lsung: Konstruktoren

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

String::String(int maxlen, const char* cstr) {


if (maxlen<0) maxlen = 0;
this->maxlen ist hier notwendig,
this -> maxlen = maxlen; weil die Instanzvariable maxlen durch
len = cstr ? strlen(cstr) : 0; den gleichnamigen Parameter
berlagert ist
if (len>maxlen) len = maxlen;
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = cstr[i];
}

Hier wird eventuell eine Exception ausgelst. Wird ein


Konstruktor via Exception verlassen, dann gilt das
Objekt als nicht existent. (Das Objekt lebt vom Ende
des Konstruktors bis zur Vernichtung der Variable,
bzw. Freigabe mit delete.) 177
Konstruktoren (3)

Implementierung

String::String(const char* cstr) {


maxlen = len = strlen(cstr);
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = cstr[i];
}

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

delete[] sa2; Bei Arrays wird fr jedes Element der


} Defaultkonstruktor aufgerufen
(bis C++11 syntaktisch keine Parameterbergabe
mglich; Universal initializer knnen ab C++11
verwendet werden)

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.

Die implizite Anwendung des Konstruktors kann durch Verwenden des


Schlsselworts explicit verhindert werden. Danach kann der Konstruktor nur
mehr direkt oder durch explizite Typumwandlung aufgerufen werden.
explicit String(const char* cstr); //nur in der Deklaration
String s("Test"); //OK
s=String("a"); //moderner cast OK
s=(String)"b"; //alter cast OK
s.find("e"); //keine implizite Umwandlung nicht erlaubt

180
Konversionsoperatoren

Ein Konstruktor mit einem Parameter kann zur Konversion eines


beliebigen Datentyps in ein Objekt der Klasse verwendet werden. Will man
eine Konversion in der anderen Richtung definieren, kann man
Konversions-Operatoren definieren:
Einzige Mglichkeit
class String { der Konversion in
einen
Standarddatentyp
operator int();
Konversionsoperator hat
operator AndereKlasse(); keinen Returntyp (auch nicht
void) und keine Parameter.

Warum?
} Wird verwendet, wenn
kein entsprechender
Konstruktor in der
anderen Klasse erstellt
werden kann

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

Durch Anfgen des Schlsselworts const an den Funktionskopf (sowohl


bei der Deklaration, als auch bei der Definition) kann der Zeiger this als
konstanter Zeiger auf ein konstantes Objekt definiert werden.
class String {

char& operator[] (int index) const;

};
char& String::operator[] (int index) const {}
Innerhalb der Methode operator[] ist der Datentyp von this nun
const String *const. Die ursprngliche Version der Methode find
ist damit korrekt.

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+

String String::operator+ (const String& rightop) {


int newmax = len + rightop.len;
Neues Objekt
if (newmax<maxlen) newmax = maxlen;
zur Konstruktion
if (newmax<rightop.maxlen) newmax = rightop.maxlen; des
String res(newmax); Ergebnisses,
das spter
res.len = len + rightop.len; retourniert wird
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;
}

188
String::operator-

String String::operator- (const String& rightop) {


int pos=this->find(rightop);
if (pos>=0) {
String res(maxlen);
Sollte immer
res.len=len-rightop.len; erfllt sein
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)
res.buf[i-rightop.len]=buf[i];
return res;
}
return *this;
}

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:

Explizit durch Schsselwort inline Implizit (ohne Angabe des Schlsselwortes


(sowohl bei Deklaration als auch bei inline) durch Definition innerhalb der
Definition) Klassendefinition
class String {
class String {
inline String();
inline void print();
String() { //leerer String
}; len = maxlen = 0;
buf = 0;
inline String::String() {
len = maxlen = 0; }
buf = 0;
} void print() {
inline void String::print() { for (int i=0; i<len; ++i) cout << buf[i];
} }

};

191
Hochkommata
" " statt spitzen
Organisation der Programmteile (1) Klammern <>

Datei string.h Datei string.C


#include "string.h"
class String {
void String::print ()
....
{ ....
};
}

g++
#include "string.h"
int main () {
String x; g++ stringtest.o string.o
} ld
Datei stringtest.C
stringtest 192
Organisation der Programmteile (2)

Erzeugung des exekutierbaren Programms (test):


g++ -Wall -c string.C (bersetzen, liefert string.o)
g++ -Wall -c stringtest.C (bersetzen, liefert stringtest.o)
compile only

g++ stringtest.o string.o -o stringtest (binden)

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.

int getlen() const;


int getmaxlen() const;
char* getbuf() const; //gefhrlich, ermglicht indirekte Manipulationen
const char* getbuf() const;
void setlen(int val); Geeignete Vorkehrungen zur
Aufrechterhaltung der Konsistenz ntig
void setmaxlen(int val);
void setbuf(char* buf); //gefhrlich, da Lnge nicht verifizierbar

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)

Der Destruktor gibt nun den allozierten Speicherplatz wieder


ordnungsgem frei, wenn ein Objekt zerstrt wird (entweder automatisch
oder mit delete bzw. delete[ ]).

for (int i=0; i>=0; ++i) {String s(100000); cout << i << endl;}

Ausgabe:
0
1

2147483646
2147483647

Schleife wird normal


beendet. (Sollte das nicht
eine Endlosschleife sein?)
200
Destruktoren (3)
Ein Objekt gilt als zerstrt, sobald der Destruktor zu laufen beginnt. Die
Lebensdauer eines Objekts ist somit vom Ende (der schlieenden Klammer) des
Konstruktors bis zum Anfang (zur ffnenden Klammer) des Destruktors.

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

String::String (const String& original) {


maxlen = original.maxlen; len = original.len;
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = original.buf[i];
}

204
Kopierkonstruktoren (2)

Wird kein Kopierkonstruktor definiert, aber einer bentigt (weil Objekte


kopiert werden mssen), so wird vom Compiler einer automatisch
erzeugt!
Ein vom Compiler erzeugter Kopierkonstruktor kopiert einfach die
Instanzvariablen der Reihe nach = komponentenweise oder flache Kopie
(bei Objekten mit dynamischem Speicher fast immer unerwnscht !)

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

Wird kein Zuweisungsoperator definiert, aber einer bentigt (weil Objekte


kopiert werden mssen), so wird vom Compiler einer automatisch
erzeugt (nur fr Zuweisungen innerhalb der gleichen Klasse)!
Ein vom Compiler erzeugter Zuweisungsoperator fhrt eine
komponentenweise Zuweisung der Instanzvariablen durch (bei
Objekten mit dynamischem Speicher fast immer unerwnscht !)

208
Zuweisungsoperatoren (3)

class Point {
String label;
double x;
double y;
Automatisch erstellter
Zuweisungsoperator

wrde ausreichen.
};

209
Zuweisung versus Initialisierung (1)

Unterscheidung i.A. nur bei Vorhandensein dynamischer Datentypen


relevant!
Initialisierungen erfolgen bei neuen (nackten) Objekten
Objekt muss erst aufgebaut werden
Zustndig: Konstruktor(en), insb. auch Kopierkonstruktor
Zuweisungen erfolgen auf bereits bestehende (fertige) Objekte
Alte Objektstruktur kann u.U. wiederverwertet werden
Falls nicht mglich: ordentlich zurckgeben, dann Objekt neu aufbauen,
dann Daten der rechten Seite bernehmen
Zustndig: Zuweisungsoperator(en)
Parameterbergabe: Initialisierung der formalen Parameter

210
Zuweisung versus Initialisierung (2)

Konstruktoren sollen i.A. die Instanzvariablen ihrer Objekte initialisieren.


Bisher jedoch durch Zuweisungen bewerkstelligt:
String::String() { maxlen = 0; len = 0; }
Implizit korrekte
Sauberere Lsung durch eigene Initialisierungssyntax Auflsung der beiden
(nur bei Definition von Konstruktoren erlaubt): gleichbenannten
Variablen
String::String(): maxlen(0),len(0) { }
String::String(int maxlen) : maxlen(maxlen),len(0) { }
Hier egal - bei Instanzvariablen mit Klassen-Datentypen (Aktivierung von
Kopierkonstruktor oder Zuweisungsoperator!) und bei Instanzvariablen, die
konstant (const) oder Referenzen (Typ&) sind, aber wichtig!

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; }
}

Aufruf des Zuweisungsoperators (falsch, wenn Aufruf des


Instanzvariable label noch nicht initialisiert. Kopierkonstruktors
Existiert kein Defaultkonstruktor fr String, so
wird eventuell versucht, den Zeiger
label.buf, der uninitialisiert ist, freizugeben;
jedenfalls ineffizient) 212
Zuweisung versus Initialisierung (4)

Faustregel:
Instanzvariablen immer mit der Initialisierungssyntax initialisieren.

Wichtiges Detail:
String::String() : len(++n), maxlen(n){}

Reihenfolge der Angabe hier ist irrelevant!


Wird in der Reihenfolge der Deklaration
der Instanzvariablen in der Klasse abgearbeitet!
(Der Compiler weist eventuell durch eine Warnung darauf hin.)
213
Ausgabe von Strings (1)

Konventioneller Ansatz Mgliche Realisierung:


int main() { void String::print() {
String s = "Test"; for (int i=0; i<len; ++i)
s.print(); cout << buf[i];
} }
Mehrere Nachteile
Ausgabe fr unterschiedliche Klassen mit unterschiedlichen Methoden
Ort der Ausgabe cout durch Realisierung definiert (hard-coded)
Wunsch:
int main() {
String s = "Test";
cout << s; // bzw. cerr << s; oder file << s;
}

214
Ausgabe von Strings (2)

Mit cout << ... knnen unterschiedliche Objekte ausgege-


ben werden, weil der Operator << (mehrfach) berladen ist
class ostream { public:
ostream& operator<<(int n);
Auszug aus
ostream& operator<<(long n); der Datei
ostream& operator<<(double n); iostream
ostream& operator<<(char c);
ostream& operator<<(const char *s);
};

Durchschleifen des Orts der Ausgabe (z.B. cout, cerr), daher


Folgendes mglich:
int n; char c;
cout << n << c << endl;
Eigentlich: (((cout << n) << c) << endl);
215
Ausgabe von Strings (3)

Zwei prinzipielle Mglichkeiten, um Ausgabe mit << zu erreichen:

Typkonversion: Erstellen einer Methode zur Konversion in einen


Datentyp, der schon ausgegeben werden kann, etwa
String::operator char*() {}

Weiteres berladen des Operators <<: Die berladene Methode


operator<< msste zur Klasse ostream hinzugefgt werden (da der
linke Operand immer als this-Objekt bergeben wird und somit die Klasse
festlegt, in der die Methode verwirklicht werden muss). Die Vernderung
der vorgegebenen Bibliotheken ist aber (wenn berhaupt mglich) keine
gute Idee!

216
Ausgabe von Strings (4)

Operator << wird daher als globale Funktion berladen

ostream& operator<< (ostream& o, const String &s);

ostream& operator<< (ostream& o, const String &s) {


// s ausgeben
return o;
} Ausgabe nicht so einfach, weil
Zugriff auf die Instanzvariablen
fr die globale Funktion
verboten ist

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

ostream& operator<< (ostream& o, const String &s) { //Definition


for (int i=0; i<s.len; ++i) o << s.buf[i];
return o;
}
Zugriff auf Instanzvariablen erlaubt, aber Angabe eines Objekts erforderlich, da
operator<< keine Methode ist und damit auch kein this-Objekt existiert. 218
Ausgabe von Strings (5)

friend-Funktionen sollten nur verwendet werden, wenn sie aus


Effizienzgrnden unbedingt erforderlich sind. Sie weichen das
Klassenprinzip auf und machen somit die Programme wieder
fehleranflliger und schwieriger zu warten.
Als Alternative bietet sich die Verwendung von Accessoren oder fr den
jeweiligen Zweck speziell zu implementierenden Methoden an.
Ausgabe ist sowieso langsam, daher ist die Effizienz nicht relevant. Es
kann die (noch anzupassende) print-Methode verwendet werden.

ostream& operator<< (ostream& o, const String &s) { //kein friend


s.print();
return o;
}
Compilerfehler:
str.C:39: error: passing <const String> as <this> argument of <void String::print()> discards qualifiers
219
Ausgabe von Strings (6)

print muss als const-Methode implementiert werden


void print() const;

Programm kompiliert nun ordnungsgem. Aber


#include<fstream>
int main() {
void String::print() const {
String s = "Test";
for (int i=0; i<len; ++i)
fstream file("out.txt", ios::out);
cout << buf[i];
file << s;
}
return 0;
}
Ausgabe erfolgt nicht, wie
gewnscht, auf Datei, sondern auf
den Bildschirm!

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

Alle Ausgaben an das gewnschte


Ziel statt an cout

Aufruf in operator<<:
ostream& operator<< (ostream& o, const String &s) { //kein friend
s.print(o);
return o;
}

221
Eingabe von Strings

Alle fr die Ausgabe mittels << getroffenen Feststellungen gelten analog


auch fr die Eingabe mittels >> (auer, dass bei der Eingabe die
Verwendung einer Typkonversion nicht zielfhrend ist)

222
Klassenvariablen und Klassenmethoden

Instanzvariablen existieren fr jede Instanz einmal. Gibt es keine


Instanzen einer Klasse, so gibt es auch keine Instanzvariablen.
Klassenvariablen existieren fr jede Klasse genau einmal. Sie
beschreiben Eigenschaften der Klasse (aller Objekte der Klasse).
class KDTest { static unsigned no; }; // Deklaration
unsigned KDTest::no = 0; // Definition auerhalb, ohne static!
Genauso gehren Klassenmethoden zur Klasse (sie haben kein this-
Objekt und knnen deshalb ohne ein Objekt aufgerufen werden; Auf
Instanzvariable kann daher nur unter Spezifizierung eines Objekts
zugegriffen werden.):
class KDTest {
static unsigned no;
public:
static unsigned readCounter() { return no; };
};
Objekt k dient nur zur Festlegung
Verwendung: KDTest::readCounter(); der Klasse. Eventuell wird eine
oder auch: KDTest k; k.readCounter(); Compilerwarnung generiert.
223
Resource Acquisition is Initialization
void foo() { class BufClass {
try { char *buf;
char *buf = new char[10]; public:
} BufClass() {buf = new char[10];}
catch(std::bad_alloc) { ~BufClass() {delete[] buf;}
return; //kein delete!
} }; Destruktor wird ganz
sicher aufgerufen,
try { void foo() { wenn das Objekt
BufClass b; zerstrt wird (allerdings
} gibt es kein Objekt,
catch(...) { } wenn der Konstruktor
fehlschlgt und damit
delete[] buf; auch keinen
Eventuell try-catch-
} Block anwenden, um Destruktoraufruf)
} Fehler bei der
Mhselig und
fehleranfllig, vor Erzeugung des
allem, wenn mehrere Objekts abzufangen
Ressourcen Dieses Konzept ist fr beliebige Ressourcen
verwendet werden (Speicherplatz, Dateien, Drucker, Gerte, ) einsetzbar!
224
Wiederholung
Destruktor ~String();
Kopierkonstruktor String(const String&);
Zuweisungsoperator String& operator=(const String&);
Zuweisung vs. Initialisierung String s="Init";s="Assign";
Initialisierungssyntax String(int maxlen):maxlen(maxlen);
Ein-/Ausgabe ostream& operator<<(ostream& o,String &s);
istream& operator>>(istream& o,String &s);
Klassenvariable class C{ static String desc;};
String C::desc="Description";
Klassenmethode class C{ static int getc();};
Resource Acquisition is Initialization SpeicherObjekt so;

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;
};

Baum (Wurzel)Element mit Bumen als Kindern 227


Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A

AC D E F GH I J K L MN OP QR C

B einfgen

228
Bearbeiten von rekursiven Datenstrukturen

Aufgrund des rekursiven Aufbaus der Datenstrukturen ist eine rekursive


Programmierung oft sehr einfach und naheliegend. Nur wenn
Laufzeiteffizienz sehr bedeutend ist, wird man einen (meist etwas
umstndlicheren) iterativen Ansatz whlen.
Beispiel: Ausgabe aller gespeicherten Elemente

List::print(ostream& o) const { Tree::print(ostream& o) const {


o << value; if (leftChild) {
if (next) { leftChild->print(o);
o << ", "; }
next->print(o); o << ", ";
} o << value;
if (rightChild) {
}
rightChild->print(o);
}
}
229
Iterative Implementierung von print
List::print(ostream& o) const {
o << value;
if (next) {
o << ", ";
next->print(o); Die iterative Implementierung fr
} den binren Baum ist schon
}
nicht mehr ganz so einfach
List::printIt(ostream& o) const {
o << value;
List *help = next;
while (help) {
o << ", " << help->value;
help = help->next;
}
}

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

Bei allgemeinen Bumen gibt es keine Beschrnkungen fr die Anzahl der


Kinder, die ein Knoten haben darf.
Zwei mgliche Implementierungen:
class Tree { class ChildList{
int value; Tree *child;
ChildList *next;
Tree **children;
};
}; class Tree {
Dynamisches int value;
Array von
Zeigern auf
ChildList *children;
Kinder };

Liste von
Zeigern auf
Kinder 232
Wiederholung

Rekursive Datenstrukturen class R {R *rek;}


Listen 7 5 9

(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

allgemein gilt nur:


1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)
sizeof(float) <= sizeof(double) <= sizeof(long double) 236
Anhang: Operatoren bersicht (1)

Die folgende Tabelle enthlt alle C++ Operatoren. Die Bindungsstrke


(precedence) aller Operatoren in einem Kasten ist gleich. Je weiter unten
der Kasten, desto kleiner die Bindungsstrke.
In der letzten Spalte ist vermerkt, ob die Operatoren der jeweiligen Gruppe
rechts- oder linksassoziativ sind.

operator name example


:: scope resolution Class::member right
:: global ::variable
237
Anhang: Operatoren bersicht (2)

operator name example


. member selection object.member
-> member selection object->member
[] subscripting array[index]
() function call function (parameters) left
() value construction type(expressions)
++ post increment lvalue++
-- post decrement lvalue--

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

<< shift left expr<<expr


left
>> shift right expr>>expr 240
Anhang: Operatoren bersicht (5)
operator name example
< less than expr<expr
<= less or equal expr<=expr
> greater than expr>expr left
>= greater or equal expr>=expr

== equal expr==expr
!= not equal expr!=expr left

& bitwise AND expr&expr left

^ bitwise exclusive OR expr^expr left

| bitwise inclusive OR expr|expr left


241
Anhang: Operatoren bersicht (6)

operator name example


&& logical AND expr&&expr left

|| logical OR expr||expr left

?: conditional expr expr?expr:expr right

242
Anhang: Operatoren bersicht (7)

operator name example


= simple assignment lvalue=expr
*= multiply and assign lvalue*=expr
/= divide and assign lvalue/=expr
%= modulo and assign lvalue%=expr
+= add and assign lvalue+=expr right
-= subtract and assign lvalue-=expr
<<= shift left and assign lvalue<<=expr
>>= shift right and assign lvalue>>=expr
&= AND and assign lvalue&=expr
|= OR and assign lvalue|=expr
^= XOR and assign lvalue^=expr 243
Anhang: Operatoren bersicht (8)

operator name example


throw throw exception throw expr

, sequencing expr,expr left

244
Anhang: Syntaxbeschreibung

if, else usw. sind unmittelbar in den Programmtext zu bernehmen.


logischer-Ausdruck, Anweisung1 usw. stehen fr noch nher zu
definierende Programmteile.
[ ] markiert Programmteile, die ausgelassen werden knnen.
/ trennt mgliche Alternativen.
{ } markieren beliebig oft wiederholbare Programmteile.

Achtung: Die Metasymbole [ ], / und { } drfen nicht mit den C++ Symbolen
[], | und {} verwechselt werden

245
Anhang: Schlsselwrter in C/C++

asm auto bool break case catch


char class const const_cast continue default
delete do double dynamic_cast else enum
explicit export extern false float for
friend goto if inline int long
mutable namespace new operator private protected
public register reinterpret_cast return short signal
signed sizeof static static_cast struct switch
template this throw true try typedef
typeid typename union using unsigned virtual
void volatile wchar_t while

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;}

operator char*() {return buf;} //Achtung! Nicht Null-terminiert!

void set (const char* source);


int find(const String& substr);

String& operator=(const String& rightop);


String operator+ (const String& rightop);
String operator- (const String& rightop);
char& operator[](int index) const;
void print (ostream &o) const;
}; 247
Anhang: Die Klasse String (2)
ostream& operator<< (ostream& o, const String& s) {
s.print(o);
return o;
}

String operator+ (const char* cstr,String str) {


return String(cstr)+str;
}

String::String(int maxlen, const char* cstr): maxlen(maxlen) {


if (maxlen<0) maxlen = 0;
this -> maxlen = maxlen;
len = cstr ? strlen(cstr) : 0;
if (len>maxlen) len = maxlen;
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = cstr[i];
}

String::String (const String& original): maxlen(original.maxlen), len(original.len) {


buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = original.buf[i];
}
248
Anhang: Die Klasse String (3)
String::String(const char* cstr) {
maxlen = len = strlen(cstr);
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = cstr[i];
}

void String::set(const char* source) {


len = strlen(source);
if (len > maxlen) {
if (buf) delete[] buf;
maxlen = len;
buf = new char[maxlen];
}
for (int i=0; i<len; ++i) buf[i] = source[i];
}

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;
}

String& String::operator= (const String& rightop) {


if (&rightop == this) return *this;
if (buf) delete[] buf;
maxlen = rightop.maxlen; len = rightop.len;
buf = maxlen ? new char[maxlen] : 0;
for (int i=0; i<len; ++i) buf[i] = rightop.buf[i];
return *this;
}

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;
}

String String::operator- (const String& rightop) {


int pos = this->find(rightop);
if (pos>=0) {
String res(maxlen);
res.len = len - rightop.len;
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) res.buf[i-rightop.len] = buf[i];
return res;
}
return *this;
251
}
Anhang: Die Klasse String (6)
char& String::operator[] (int index) const {
if (!len) throw "Positionszugriff auf leeren String";
if (index<0 || index>len) throw "Positionszugriff auf ungueltige Position";
return buf[index];
}

void String::print(ostream& o) const {


for (int i=0; i<len; ++i)
o << buf[i];
}

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

Die Zahlenangaben beziehen sich auf die Kapitelnummern.


254
Thematische Inhaltsbersicht (2)
Datenstrukturen 10
Listen 10
Bume 10
Datentypen 2,4,7,8,9
Aufzhltyp enum 7
Einfache Datentypen int, double, bool, 2
Felder 4
Klassen 8,9
Konstante 2
Referenzen 4
Typkonversionen 2
Zeiger 4

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

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;
}

260
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982

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;
}

261
Pi ist gleich 3.14159
Der Umfang des Kreises mit Radius 1.2 betraegt 7.53982
7 Tage hat die Woche

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;
}

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

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;
}

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));
}}
}

fkt(3) fkt(2) fkt(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));
}}
}

fkt(3) fkt(2) fkt(1)

3*fkt(2) 2*fkt(1) 1*fkt(0)

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));
}}
}}

fkt(3) fkt(2) fkt(1) fkt(0)

3*fkt(2) 2*fkt(1) 1*fkt(0)

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));
}}
}}

fkt(3) fkt(2) fkt(1) fkt(0)

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));
}}
}}

fkt(3) fkt(2) fkt(1) fkt(0)

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));
}}
}

fkt(3) fkt(2) fkt(1) fkt(0)

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));
}}

fkt(3) fkt(2) fkt(1) fkt(0)

n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)

return return return(1)


(2*1) (1*1)
2*1*1

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));
}

fkt(3) fkt(2) fkt(1) fkt(0)

n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)

return return return return(1)


(3*2) (2*1) (1*1)
3*2*1*1 2*1*1

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));
}

fkt(3) fkt(2) fkt(1) fkt(0)

n==0
3*fkt(2) 2*fkt(1) 1*fkt(0)

return return return return(1)


(3*2) (2*1) (1*1)
3*2*1*1 2*1*1

350
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; ?
else
?
if (n <= 2)
?
return 1;
else
?
return fibo(n-2)+ fibo(n-1); ?
} ?
int main () { ?
cout << fibo(5) << endl; ?
} .....

351
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
?
return 1;
else
?
return fibo(n-2)+ fibo(n-1); ?
} ?
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

352
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
?
return 1;
else
?
return fibo(n-2)+ fibo(n-1); ?
} ?
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

353
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
?
return 1;
else
?
return fibo(n-2)+ fibo(n-1); ?
} ?
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

354
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
?
return 1;
else
?
return fibo(n-2)+ fibo(n-1); ?
} ?
int main () { ?
cout << fibo(5) << endl; ?
}
#1 Annahme: linker
.....
Teilausdruck wird
zuerst bewertet 355
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); ?
} ?
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

356
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); ?
} ?
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

357
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); ?
} ?
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

358
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); ?
} ?
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

359
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #2
} n 10024 1
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

360
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #2
} n 10024 1
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

361
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #2
} n 10024 1
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

362
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2) 1 #2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #2
} n 10024 1
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

363
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else 1
return fibo(n-2)+ fibo(n-1); #2
} 1
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

364
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #3
} n 10024 2
#2 #3
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

365
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #3
} n 10024 2
#2 #3
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

366
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #3
} n 10024 2
#2 #3
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

367
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2) 1 #2
return 1;
n 10016 3
else
return fibo(n-2)+ fibo(n-1); #3
} n 10024 2
#2 #3
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

368
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
else 1 1
2 n 10016 3
return fibo(n-2)+ fibo(n-1); #3
} 2
#2
int main () { ?
cout << fibo(5) << endl; ?
}
#1 .....

369
Direkte Rekursion: Fibonacci Zahlen (1)

int fibo (int n){


if (n <= 0) tmp 10000 ?
return 0; #1
else n 10008 5
if (n <= 2)
#2
return 1;
else 2
3
return fibo(n-2)+ fibo(n-1); #3
} 2
int main () { ?
cout << fibo(5) << endl; ?
}
#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;
}

Funktioniert aber auch mit double


Die Werte sind: 47.11, 0x7fff232dbdb0 und 0x7fff232dbda8
v = 47.11 = 47.11 = 47.11
Index: 0 Wert: 0 Adresse: 0x7fff232dbd30
Index: 1 Wert: 0.5 Adresse: 0x7fff232dbd38
Index: 2 Wert: 1 Adresse: 0x7fff232dbd40
Index: 3 Wert: 1.5 Adresse: 0x7fff232dbd48
Index: 4 Wert: 2 Adresse: 0x7fff232dbd50
Index: 5 Wert: 2.5 Adresse: 0x7fff232dbd58
Index: 6 Wert: 3 Adresse: 0x7fff232dbd60
Index: 7 Wert: 3.5 Adresse: 0x7fff232dbd68
Index: 8 Wert: 4 Adresse: 0x7fff232dbd70
Index: 9 Wert: 4.5 Adresse: 0x7fff232dbd78
Letzter Wert indirekt: 4.5
Dynamisches Element: 4711

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

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a ?
*i = *j; b ?
*j = help; ?
}
} ?
int main() { ?
int a,b; ?
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 396
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 7
*i = *j; b 5
*j = help; ?
}
} ?
int main() { ?
int a,b; ?
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 397
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 7
*i = *j; b 5
*j = help; ?
}
} ?
int main() { ?
int a,b; ?
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 398
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 7
*i = *j; b 5
*j = help; #1
}
} i
int main() { j
int a,b; ?
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 399
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 7
*i = *j; b 5
*j = help; #1
}
} i
int main() { j
int a,b; ?
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 400
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 7
*i = *j; b 5
*j = help; #1
}
} i
int main() { j
int a,b; help 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 401
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 5
*i = *j; b 5
*j = help; #1
}
} i
int main() { j
int a,b; help 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 402
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 5
*i = *j; b 7
*j = help; #1
}
} i
int main() { j
int a,b; help 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 403
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 5
*i = *j; b 7
*j = help; #1
}
} i
int main() { j
int a,b; 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 404
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 5
*i = *j; b 7
*j = help; #1
}
}
int main() {
int a,b; 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 405
Referenzparameter call by reference (2)

Lsung: Es werden nicht die Werte direkt bergeben, sondern Zeiger auf die Werte

void sort(int* i, int* j) { .....


if (*i>*j) {
int help = *i;
a 5
*i = *j; b 7
*j = help; #1
}
}
int main() {
int a,b; 7
cin >> a >> b;
sort(&a,&b); ?
assert(a<=b); ?
return 0;
}
?
..... 406
Weitere Probleme mit Klasse String
main() {
String s = "Init";

(s-"ni").print(); s maxlen 4
len 4
}
buf

I
n

407
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; I
for(int i=pos+rightop.len; i<len; ++i) n
res.buf[i-rightop.len] = buf[i]; i
return res; t
}
return *this;
}

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

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; I
for(int i=pos+rightop.len; i<len; ++i) n
res.buf[i-rightop.len] = buf[i]; i
return res; t
}
return *this;
}

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

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; rightop maxlen 2
for(int i=pos+rightop.len; i<len; ++i) len 2
res.buf[i-rightop.len] = buf[i]; buf
return res;
} n
return *this; i
}
I

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

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; rightop maxlen 2
for(int i=pos+rightop.len; i<len; ++i) len 2
res.buf[i-rightop.len] = buf[i]; buf
return res;
this
}
return *this; n
} i

main() { I
String s = "Init"; n
(s-"ni").print(); i
}
t

411
Weitere Probleme mit Klasse String
String String::operator- (const String& rightop) {
int pos=this->find(rightop);
if (pos>=0) { s maxlen 4

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; rightop maxlen 2
for(int i=pos+rightop.len; i<len; ++i) len 2
res.buf[i-rightop.len] = buf[i]; buf
return res;
this
} 1
pos
return *this;

}
n

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

String res(maxlen); len 4


res.len=len-rightop.len; buf
assert(res.len>=0);
for(int i=0; i<pos; ++i) res.buf[i] = buf[i]; rightop maxlen 2
for(int i=pos+rightop.len; i<len; ++i) len 2
res.buf[i-rightop.len] = buf[i]; buf
return res;
this
} 1
pos
return *this;

}
n

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

Initialisierung mittels len 4


}
komponentenweiser buf
Kopie!
s1 maxlen 4
len 4
buf

I
n

431
Weitere Probleme mit Klasse String
main() {
String s = "Init";

String s1 = s; s maxlen 4
len 4
}
buf

Zerstrung der lokalen s1 maxlen 4


len 4
Variablen
buf

I
n

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

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

Falls Platz im Array nicht ausreicht, muss ein neues


AC D E F GH I J K L MN OP QR alloziert werden. Die vorhandenen Daten mssen in
dieses neue Array kopiert werden.

B einfgen

440
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN OP Q R

B einfgen

441
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN OP QR

B einfgen

442
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

443
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

444
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L M N O PQR

B einfgen

445
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

446
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

447
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

448
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

449
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

450
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F G H I J K L MN O PQR

B einfgen

451
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E F GH I J K L MN O PQR

B einfgen

452
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E FGH I J K L MN O PQR

B einfgen

453
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E FGH I J K L MN O PQR

B einfgen

454
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AC D E FGH I J K L MN O PQR

B einfgen

455
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

A C D E FGH I J K L MN O PQR

B einfgen

456
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).

AB C D E FGH I J K L MN O PQR

457
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A
Suchen der
C
Einfgeposition
D

B einfgen

458
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A
Hier einfgen
C

B einfgen

459
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A

B einfgen
B

R
Neues Element mit
gewnschtem Inhalt
erzeugen
460
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A

B einfgen
B

R
Einhngen

461
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A

462
Vergleich mit Array

Einfacher dynamisch zu vergrern, bzw. zu verkleinern.


Sortierkriterien aufrecht zu halten weniger aufwndig.
Dafr grerer Speicherplatzbedarf (fr Zeiger)
Indizierter Zugriff kann durch berladen von operator[] ermglicht
werden (ist allerdings in der Regel wesentlich aufwndiger).
A

D
Es wurden keine aufwndigen
Verschiebungen im Speicher
bentigt! B

463