Basistechniken – Zeiger

Werbung
Software Praktikum: C++ und LEDA / STL
Sommersemester 2003
Oliver Zlotowski
FB IV – Informatik
Universität Trier
20. Mai 2003
Basistechniken – Überblick
• Typen und Deklarationen
• Zeiger, Felder und Strukturen
• Ausdrücke und Anweisungen
• Funktionen
• Namensbereiche und Ausnahmen
• Quelldateien und Programme
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
1
Basistechniken – Typen
• jeder Bezeichner (engl. identifier) in C++ hat einen Typ
• der Typ bestimmt, welche Operationen auf der Variable angewendet
werden dürfen
float x;
// x ist eine Gleitkommavariable
int y = 7;
// y ist eine int-Variable mit 7 als Initalwert
double d(3.14); // d ist eine Gleitkommavariable mit Initialwert 3.14
float f(int);
// f ist eine Funktion, die ein Argument vom Typ int
// erhält und eine Gleitkommazahl zurücklierfert
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
2
Basistechniken – Fundamentale Typen
C++ hat eine Anzahl von fundamentalen Typen
• ein Boolescher Typ (bool)
• Zeichentypen (z. B. char)
• ganzzahlige Typen (z. B. int, long)
• Gleitkommatypen (z. B. float, double)
zusätzliche Typen
• Aufzählungstypen zur Darstellung von bestimmten Mengen von Werten (enum)
• einen Typ void, der für die Abwesenheit von Information steht
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
3
Basistechniken – Fundamentale Typen
Aus diesen Typen können andere Typen konstruiert werden
• Zeigertypen (z. B. int*)
• Feldtypen (z. B. char[])
• Referenztypen (z. B. double&)
• benutzerdefinierte Datentypen
Boolesche Werte, Zeichen und ganze Zahlen sind integrale Typen.
Integrale und Gleitkommatypen sind arithmetische Typen.
Aufzählungen und Klassen sind benutzerdefinierte Typen.
In der Regel benutzt man bool für logische Werte, char für Zeichen, int für
ganzzahlige Werte und double für Gleitkommawerte.
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
4
Basistechniken – Boolesche Werte
• Boolescher Wert bool hat nur zwei Werte true oder false
• bool wird zur Darstellung des Ergebnisses eine logischen Operation benutzt
bool f(int a, int b)
{ bool b = a == b; // = ist Zuweisung, == ist Gleichheit
return b;
}
• true hat den Wert 1, wenn es in eine ganze Zahl konvertiert wird;
entsprechend hat false den Wert 0
• ganze Zahlen können auch implizit in bool-Werte konvertiert werden
• jeder Zeiger kann implizit nach bool konvertiert werden
• Nullzeiger konvertiert nach false, jeder andere zu true
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
5
Basistechniken – Boolesche Werte
Beispiel
bool a = true;
bool b = 7;
// bool(7) ist true, daher wird b true
int i = true;
// int(true) ist 1, daher wird i 1
bool x = a + b;
bool y = a | b;
// a+b ist 2, daher wird x true
// a|b ist 1, daher wird y true
int* p = 0;
bool z = p;
// z ist false, da p der Nullzeiger ist
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
6
Basistechniken – Zeichentypen
• eine Variable vom Typ char kann Zeichen speichern
• eine char-Variable hat immer 8 Bit und kann daher 256 Werte speichern
• singned char kann Werte zwischen -128 bis 127 speichern
• unsigned char kann Werte zwischen 0 bis 255 speichern
• ob char vorzeichenbehaftet oder vorzeichenlos ist,
ist implementierungsabhängig
• der Typ von Zeichenkonstanten ist char
Beispiel: char c = ’a’;
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
7
Basistechniken – Ganzzahlige- und Gleitkommatypen
• es gibt vier Darstellungsformen für ganzzahlige Konstanten
1.
2.
3.
4.
dezimal (zur Basis 10) int i = 63
oktal (zur Basis 8) int j = 077
hexadezimal (zur Basis 16) int k = 0x3f
Zeichenkonstante
• Gleitkommatypen gibt es in drei Größen
1. single-precision float i = 2.9e-3f
2. double-precision double j = 0.23
3. extended-precision long double k = 3.14159265l
#include <iostream>
#include <limits>
void f()
{ std::cout << "max float " << std::numeric_limits<float>::max();
std::cout << " char is signed " << std::numeric_limits<char>::is_signed << ’\n’;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
8
Basistechniken – void
• void ist ein fundamentaler Typ, von dem keine Objekte
erzeugt werden können
• void wird benutzt
? wenn eine Funktion keinen Rückgabewert hat
? als Basistyp für Zeiger auf Objekte unbekannten Typs
auch als generischer Zeiger bezeichnet
• Beispiel
void x;
void& r;
void f();
void* pv;
//
//
//
//
Fehler: es gibt keine
Fehler: es gibt keine
Funktion liefert kein
Zeiger auf ein Objekt
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
void-Objekte
Referenzen auf void
Ergebnis
unbekannten Typs
9
Basistechniken – Aufzählungstypen
• Aufzählung (engl. enumeration) ist ein Typ, der eine Menge durch den
Anwender spezifizierte Werte speichert
• Aufzählungstypen werden ähnlich benutzt wie int
• ganzzahlige Konstanten (Enumeratoren) bekommen Werte von 0 an
aufsteigend zugewiesen; Beispiel: enum { ASM, AUTO, BREAK };
• jede Aufzählung ist ein unterschiedlicher Typ
enum e1 { dunkel, hell }; // Bereich 0:1
enum e2 { a = 3, b = 9 }; // Bereich 0:15
enum e3 { min = -10, max = 1000000 }; // Bereich -1048576:1048575
enum flag { x = 1, y = 2, z = 4, e = 8 };
flag f(flag f) { return f == e ? x : y; }
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
10
Basistechniken – Deklarationen
• jeder Bezeichner muß in einem Programm deklariert werden,
d. h. sein Typ muß spezifiziert werden
char ch;
string s;
int count = 1;
const double pi = 3.141592;
const char* name = "Praktikum";
const char* jahreszeit[] = { "Frühlung", "Sommer", "Herbst", "Winter" };
struct Datum { int t, m, j };
int tag (Datum* p) { return p->d; }
template <class T> T abs(T a) { return a < 0 ? -a : a; }
• obige Deklarationen sind auch Definitionen
• Deklarationen aber keine Definitionen sind
double sqrt(double);
extern int fehler;
struct User;
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
11
Basistechniken – Deklarationen
• der Rumpf der Funktion sqrt(), der Speicher für die int-Variable und
die Definition des Typs User müssen durch eine andere Deklaration
spezifiziert werden
double sqrt(double d) { /* ... */ }
int fehler = 1;
struct User { /* ... */ };
• es muß genau eine Definition für einen Namen in einem C++-Programm geben
• es kann aber viele Deklarationen geben
int count;
int count;
// Fehler: Redefinition
extern int fehler;
extern short fehler;
// Fehler: Typfehler
extern int fehler;
extern int fehler;
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
12
Basistechniken – Struktur einer Deklaration
• Deklaration besteht aus vier Teilen
1.
2.
3.
4.
einem optionalen »Spezifizierer«, z. B. virtual, extern
einem Basistyp, z. B. char
einem Deklarator, z. B. *king[]
einem optionalen Initialisierer, z. B. { ... }
• Beispiel
char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" };
• ein Deklarator ist aus einem Namen und optionalen
Dekalaratoroperatoren zusammengesetzt
*
* const
&
[]
()
Zeiger
konstanter Zeiger
Referenz
Feld
Funktion
Oliver Zlotowski, FB IV – Informatik, Universität Trier
Präfix
Präfix
Präfix
Postfix
Postfix
❂ ⇐ ⇐▲▼ ⇒ ⇒
* ptr
* const ptr
& ref
A[]
f()
13
Basistechniken – Deklaration
• es ist möglich, mehrere Namen in einer einzelnen Deklaration zu deklarieren
int x, y; // int x; int y;
• die Deklarationsoperatoren beziehen sich aber nur auf einzelne Namen
int *p, y;
// int *p; int y; NICHT: int* y
int x, *q;
// int x; int *q;
int v[10], *pv; // int v[10]; int* pv;
• ein Name besteht aus einer Folge von Buchstaben und Ziffern, wobei das erste
Zeichen ein Buchstabe sein muß (Unterstrich ist ein Buchstabe)
• die Länge der Namen ist nicht beschränkt;
Groß-/Kleinschreibung wird unterschieden
• Schlüsselwörter (new, delete, . . . ) dürfen keine Bezeichner sein
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
14
Basistechniken – Gültigkeitsbereich
• Deklaration führt einen Namen in einen Gültigkeitsbereich ein
• der Gültigkeitsbereich eines Namens beginnt ab seiner Deklaration bis
zum Ende eines Blocks
• ein Block ist ein durch {...} Klammern begrenzter Codebereich
int x;
void f()
{ int x;
x = 1;
{ int x;
x = 2;
}
x = 3;
}
// globale Variable x
//
//
//
//
lokales x verdeckt globales x
Zuweisung an lokales x
verdeckt erstes lokales x
Zuweisung an zweites lokales x
// Zuweisung an erstes lokales x
int* p = &x; // holt die Adresse des globalen x
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
15
Basistechniken – Gültigkeitsbereich
• ein verdeckter globaler Name kann durch Bereichsauf
lösungsoperator :: sichtbar gemacht werden
int x;
// globale Variable x
void f()
{ int x;
::x = 2;
x = 2;
}
// lokales x verdeckt globales x
// Zuweisung an globales x
// Zuweisung an lokales x
• es gibt keine Möglichkeit, einen verdeckten lokalen Namen zu benutzen
• Namen von Funktionsargumenten werden im äußersten
Block einer Funktion deklariert
void f(int x)
{ int x; // Fehler: x schon deklariert
//...
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
16
Basistechniken – Initialisierung
• ist für ein Objekt ein Initialisierer spezifiziert, bestimmt dieser den Initialwert des
Objekts
• ist kein Initialisierer angegeben, dann wird ein globales Objekt, ein in einem Namesbereich stehendes Objekt oder ein lokales statisches Objekt mit einer 0 des
passenden Typs initialisiert
int a;
double d;
// Bedeutet int a = 0;
// Bedeutet double d = 0.0;
• lokale Variablen und auf dem Heap erzeugte Objekte, werden standardmäßig
nicht initialisiert
void f()
{ int x;
//...
}
// x hat keinen wohldefinierten Wert
• benutzerdefinierte Typen können eine Default-Initialisierung definiert haben, oder
haben Konstruktoren mit funktionstypischen Argumentlisten
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
17
Basistechniken – typedef
• eine Deklaration mit dem Präfix typedef deklariert einen neuen Namen
für den Typ (statt einer neuen Variable des Typs)
typedef char* pointer;
pointer p1, p2; // p1 und p2 sind jeweils char*
char* p3 = p1;
typedef unsigned char uchar;
typedef int int32;
typedef short int 16;
• so definierte Namen sind in der Regel bequeme Abkürzungen für
unhandliche Namen
• typedefs sind Synonyme für andere Typen und definieren keine neuen Typen
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
18
Basistechniken – Überblick
• Typen und Deklarationen
• Zeiger, Felder und Strukturen
• Ausdrücke und Anweisungen
• Funktionen
• Namensbereiche und Ausnahmen
• Quelldateien und Programme
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
19
Basistechniken – Zeiger
• für einen Typ T ist T* der Typ »Zeiger auf T«
• eine Variable vom Typ T* kann die Adresse eines Objekts von Typ T speichern
char c = ’a’;
char* p = &c;
// p enthält die Adresse von c
• Zeiger auf Felder und Zeiger auf Funktionen
int* pi;
char** ppc;
int* ap[15];
int (*fp)(char*);
int* f(char*);
//
//
//
//
//
//
//
Zeiger auf ein int
Zeiger auf Zeiger auf char
Feld von 15 Zeigern auf int
Zeiger auf Funktionen mit einem char*-Argument;
liefert einen int
Funktion mit einem char*-Argument;
liefert einen Zeiger auf int
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
20
Basistechniken – Zeiger
• fundamentale Operation auf einem Zeiger ist das Dereferenzieren, d. h.
»sich auf ein Objekt beziehen, auf das ein Zeiger zeigt«
• die Operation wird auch als Indirektion bezeichnet
• der Dereferenzierungsoperator ist das einstellig *
char c = ’a’;
char*p = &c; // p enthält die Adresse von c
char x = *p; // x = ’a’
• es ist möglich einige arithmetische Operationen auf Zeigern auf Feldelemente
durchzuführen (Pointerarithmetik)
• Implementierung von Zeigern soll direkt den Adressierungsmechanismus des
Rechners abbilden; die meisten Rechner können als kleinste Einheit ein Byte
adressieren
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
21
Basistechniken – Zeiger
Null
• Null (0) ist ein int
• Null wird als Zeigerkonstante benutzt, um anzuzeigen, daß ein
Zeiger nicht auf ein Objekt zeigt, da kein Objekt die Adresse Null hat
• in wird C häufig ein Makro NULL definiert
#define NULL 0
int* p = NULL;
if (p) {
//...
}
// Zeiger mit dem Nullzeiger initialisiert
// implizite Zeiger zu bool-Konvertierung
void* q = p;
// jeder Zeiger kann einem generischen Zeiger
// zugewiesen werden
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
22
Basistechniken – Felder
• für eine Typ T ist T[size] der Typ
»Feld mit size Elementen vom Typ T«
• die Elemente sind von 0 bis size -1 indiziert
float v[3]; // Feld von drei floats v[0], v[1], v[2]
char* a[32]; // Feld von 32 Zeigern auf char: a[0]...a[31]
• die Feldgröße muß ein konstanter Ausdruck sein
void f(int i)
{ int v1[i];
// Fehler: Feldgröße kein konstanter Ausdruck
vector<int> v2(i); // OK
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
23
Basistechniken – Felder
• Mehrdimensionale Felder werden als Felder von Feldern dargestellt
int d2[10][20]; // d2 ist ein Feld von 10 Feldern von 20 int
d2[0][0] = 3;
// weist der 0. Komponente des 1. Feldes 3 zu
Feldinitialisierer
• ein Feld kann durch eine Liste von Werten initialisiert werden
int v1[] = { 1, 2, 3, 4, 5 };
char v2[] = { ’a’, ’b’, ’c’, 0 };
• Größe der Felder v1 und v2 wird automatisch ermittelt
int v3[8] = { 1, 2, 3, 4, 5 };
// explizite Größe;
// v3[5]...v3[7] mit 0 initialisiert
int v4[8] = { 1, 2, 3, 4, 5, 0, 0, 0 }; // äquivalent zu v3
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
24
Basistechniken – Zeiger in Felder
• Zeiger und Felder sind eng verwandt
• der Name eines Feldes kann als Zeiger auf sein erstes Element benutzt werden
int v[]
int* p1
int* p2
int* p3
=
=
=
=
{ 1, 2, 3, 4 };
v;
// Zeiger auf erstes Element; implizite Konvertierung
&v[0]; // Zeiger auf erstes Element
v + 4; // Zeiger hinter das letzte Element
• der Zeiger hinter das letzte Element p3 darf nicht zum Lesen oder Schreiben
benutzt werden
• Zuweisung und implizite Konvertierung
char v[] = "Software";
char* p = v; // implizite Konvertierung von char[] nach char*
// Größe des Feldes geht verlohren
v = p;
// Fehler: Zuweisung an Feld nicht möglich
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
25
Basistechniken – Zeiger in Felder
Navigieren in Feldern
• Zugriff auf ein Element kann über einen Zeiger auf ein
Feld plus einem Index erfolgen
int v[] = { 1, 2, 3, 4 };
for (int i = 0; i < 5; ++i) f(v[i]);
• oder über einen Zeiger auf ein Element
for (int *p = v, *p_stop = v + 5; p != p_stop; ++p) f(*p);
• der Präfixoperator * dereferenziert den Zeiger, so daß *p die Zahl ist,
auf die p zeigt
• ++ inkrementiert den Zeiger, so daß er auf das nächste Element des Feldes zeigt
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
26
Basistechniken – Zeiger in Felder
Pointerartihmetik
• Subtraktion von Zeigern ist nur dann definiert, wenn beide Zeiger auf Elemente
desselben Feldes zeigen; das Ergebnis der Subtraktion ist eine ganze Zahl
• man kann eine ganze Zahl zu einem Zeiger addieren oder subtrahieren,
in beiden Fällen ist das Ergebnis ein Zeiger
void f()
{ int v1[10], v2[10];
int i1 = v1 + 5 - v1 + 3; // i1 = 2
int i2 = &v1[5] - &v2[3]; // Resultat undefiniert
int* p1 = v2 + 2; // p1 = &v2[2]
int* p2 = v2 - 2; // *p2 undefiniert
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
27
Basistechniken – Konstanten
• mit const kann man benutzerdefinierte Konstanten deklarieren
• an eine Konstante kann keine Zuweisung erfolgen, daher muß sie
initialisiert werden
const int model = 90;
// model ist ein Konstante
const int v[] = { 1, 2, 3, 4 }; // v[i] ist eine Konstante
const int x;
// Fehler: keine Initialisierung
void f()
{ model = 200; // Fehler
v[2]++;
// Fehler
}
• const verändert einen Typ, es beschränkt die Möglichkeiten, wie das Objekt
benutzt werden kann
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
28
Basistechniken – Konstanten
Zeiger und Konstanten
• benutzt man Zeiger, sind zwei Objekte beteiligt: der Zeiger und das Objekt, auf
das er zeigt
• einer Deklaration eines Zeigers ein const voranzustellen, macht aus dem Objekt, aber nicht aus dem Zeiger, eine Konstante
• um den Zeiger selbst (anstatt des Objekts, auf das er zeigt) als konstant zu deklarieren, benutzt man den Deklaratoroperator *const
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
29
Basistechniken – Konstanten
void f(char* p)
{ char s[] = "Gnom";
const char* pc = s;
pc[3] = ’g’;
pc = p;
char *const cp = s;
cp[3] = ’g’;
cp = p;
// Zeiger auf Konstante
// Fehler: pc zeigt auf Konstante
// OK
// Konstanter Zeiger
// OK
// Fehler: cp ist konstant
const char *const cpc = s; // Konstanter Zeiger auf Konstante
cpc[3] = ’g’;
// Fehler: cpc zeigt auf Konstante
cpc = p;
// Fehler: cpc ist konstant
int a = 1;
const int c = 2;
const int* p1 = &c;
const int* p2 = &a;
int* p3 = &c;
*p3 = 7;
//
//
//
//
OK
OK
Fehler: Initialisierung von int* mit const int*
Versuch, den Wert von c zu ändern
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
30
Basistechniken – Referenzen
• eine Referenz ist ein alternativer Name für ein Objekt
• Hauptanwendung ist die Angabe von Argumenten und Rückgabewerten für
Funktionen
void f()
{ int i = 1;
int& r = i; // r und i beziehen sich nun auf das gleiche int-Objekt
int x = r; // x = 1;
r = 2;
// i = 2;
}
• Referenzen müssen bei der Erzeugung immer initialisiert werden
int i = 1;
int& r1 = i;
// OK: r1 initialisiert
int& r2;
// Fehler: Initialisierer fehlt
extern int& r3; // OK: r3 wird anderswo initialisiert
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
31
Basistechniken – Referenzen
• eine Referenz wird einmal initialisiert und verweist
dann immer auf das gleiche Objekt
• eine Referenz ist eine Art konstanter Zeiger, der bei jeder Benutzung
derefernziert wird
void f()
{ int i = 0;
int& r = i;
r++;
// i wird auf 1 initialisiert
int* p = &r; // p zeigt auf i
}
• Initialisierer einer einfachen Referenz muß ein lvalue sein,
d. h. ein Objekt des Adresse sich ermitteln läßt
double& dr = 1;
// Fehler: lvalue benötigt
const double& cdr = 1; // OK: erzeugt eine temp. Variable
// die als Initialisierer benutzt wird
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
32
Basistechniken – Referenzen
• eine Referenzen und Zeiger als Funktionsargumente
template <class T>
void swap(T& x, T& y)
{ T tmp = x;
x = y;
y = tmp;
}
template <class T>
void swap(T* x, T* y)
{ T tmp = *x;
*x = *y;
*y = tmp;
}
template<class T>
T swap(const T& x, T& y)
{ T tmp = y;
y = x;
return tmp;
}
void f()
{ int x = 1, y = 2;
swap(x,y);
// x = 2; y = 1;
}
void g()
{ int x = 1, y = 2;
swap(&x,&y);
// x = 2; y = 1;
}
void h()
{ int x = 1, y = 2;
x = swap(x,y);
// x = 2; y = 1;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
33
Basistechniken – Referenzen
struct Pair {
string
name;
unsigned value; // positive ganze Zahl
};
vector<Pair> Pairs;
unsigned& values(const string& s)
{ for (int i = 0; i < Pairs.size(); ++i)
if (s == Pairs[i].name) return Pairs[i].value;
Pair p = { s, 0 };
Pairs.push_back(p);
return Pairs.back().value;
}
int main()
{ string buf;
while (cin >> buf) values(buf)++;
for (vector<Pair>::const_iterator it = Pairs.begin(); it != Pairs.end(); ++it)
cout << it->name << " : " << (*it).value << ’\n’;
return 0;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
34
Basistechniken – Zeiger auf void
• ein Zeiger auf ein beliebiges Objekt kann einer Variablen vom
Typ void* zugewiesen werden
• ein void* kann einem anderen void* zugewiesen werden
• void*s können auf Gleichheit und Ungleichheit getestet werden
• void* kann explizit in einen Zeiger auf einen anderen Typ konvertiert werden
void f(int* pi)
{ void* pv = pi; // OK: implizite Konvertierung von int* auf void*
*pv;
// Fehler: void* nicht derefernzierbar
pv++;
// Fehler: keine Pointerarithmetik auf void*
int* pi2 = static_cast<int*>(pv); // explizite Konvertierung zurück zu int*
int* pi3 = (int*) pv;
double& dpi1 = *static_cast<double*>(pv); // Unsicher
double& dpi2 = *(double*) pv;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
35
Basistechniken – Strukturen
• eine Struktur struct ist die Zusammenfassung von beliebigen Typen
struct address
{
struct
{ string first;
string last;
} name;
// Struktur ohne Namen
unsigned postal;
string
town;
};
int main()
{ address x;
cin >> x;
cout << x;
return 0;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
36
Basistechniken – Strukturen
• Definition eines Ausgabestreams für die Struktur address
ostream& operator<<(ostream& os, const address& x)
{ os << "first name " << x.name.first << ’\n’;
os << "last name
" << x.name.last << ’\n’;
os << "postal code " << x.postal << ’\n’;
os << "town
" << x.town << ’\n’;
return os;
}
• Definition eines Eingabestreams für die Struktur address
istream& operator>>(istream& is, address& x)
{ is >> x.name.first >> x.name.last;
is >> x.postal >> x.town;
return is;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
37
Basistechniken – Überblick
• Typen und Deklarationen
• Zeiger, Felder und Strukturen
• Ausdrücke und Anweisungen
• Funktionen
• Namensbereiche und Ausnahmen
• Quelldateien und Programme
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
38
Basistechniken – Kommandozeilenargumente
• Programm wird mit dem Aufruf von main() gestartet
• main() kann zwei Argumente enthalten: argc und argv[]
• argc enthält die Anzahl der Argumente plus das Programm selbst
• argv enthält die Argumente als Zeichenketten
istream* input;
int main(int argc, char* argv[])
{ switch (argc)
{ case 1:
// keine Parameter übergeben
input = &cin; // lies von Standardeingabe
break;
case 2:
// verarbeite das erste Argument
input = new istringstream(argv[1]);
break;
}
//...
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
39
Basistechniken – Kommandozeilenargumente
//...
while (*input)
{ char ch;
(*input).get(ch);
cout << ch;
}
if (input != &cin) delete input;
}
• istringstream ist ein istream der aus einem Zeichenkettenargument liest
• ist das Ende der Zeichenkette erreicht, schlägt istringstream fehl
• um istringstream zu benutzen muß man <sstream> per #include einbinden
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
40
Basistechniken – Zusammenfassung von Ausdrücken
Bereichsauflösung
Elementselektion
Indizierung
Funktionsaufruf
Postinkrement
Postdekrement
Präincrement
Prädekrement
Typgröße
Adresse
Derefernzierung
Cast (Typkonvertierung)
Oliver Zlotowski, FB IV – Informatik, Universität Trier
klassenname::element
namensbereichsname::element
objekt.element
zeiger->element
zeiger[ausdruck]
ausdruck(ausdrucksliste)
lvalue++
lvalue−−
++lvalue
−−lvalue
sizeof(typ)
&lvalue
*ausdruck
(typ) ausdruck
❂ ⇐ ⇐▲▼ ⇒ ⇒
41
Basistechniken – Zusammenfassung von Ausdrücken
Erzeugung
Erzeugung und
Initialisierung
Erzeugung (Plazierung)
Erzeugung (Plazierung)
und Initialisierung
Zerstörung
Feldzerstörung
new typ
Multiplikation
...
Addition
Substraktion
ausdruck * ausdruck
new typ (ausdrucksliste)
new (ausdrucksliste) typ
new (ausdrucksliste) typ (ausdrucksliste)
delete zeiger
delete [] zeiger
ausdruck + ausdruck
ausdruck - ausdruck
Einfache Zuweisung
lvalue
Multiplikation und Zuweisung lvalue
Division und Zuweisung
lvalue
Addition und Zuweisung
lvalue
Oliver Zlotowski, FB IV – Informatik, Universität Trier
= ausdruck
*= ausdruck
/= ausdurck
+= ausdruck
❂ ⇐ ⇐▲▼ ⇒ ⇒
42
Basistechniken – Inkrement und Dekrement
• ++lvalue ist dasselbe wie lvalue += 1, was dasselbe
wie lvalue = lvalue + 1
• Operatoren ++ und −− können als Präfix- und als
Postfixoperatoren verwendet werden
• y = ++x; ist äquivalent zu y = (x + 1);
• y = x++; ist äquivalent zu y = x; x = x + 1;
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
43
Basistechniken – Dynamischer Speicher
• new-Operator erzeugt Objekte, die unabhängig von einem Gültigkeitsbereich exisitieren
• delete-Operator zerstört diese dynamisch erzeugten Objekte wieder
• ein mit new erzeugtes Objekt exisitert solange bis es mit delete zerstört wird
• der delete-Operator darf nur auf von new gelieferte Zeiger oder Null angewendet werden
int* iptr = 0;
void f() { iptr = new int(1); }
void g()
{ *iptr++;
delete iptr;
}
// Erzeuge und Initialsiere ein int
// Zerstöre das int
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
44
Basistechniken – Dynamischer Speicher
Dynamische Felder
• Felder von Objekten können auch mit new angelegt
char* string_copy(const char* p)
{ char* s = new char[strlen(p) + 1];
strcpy(s,p);
return s;
}
• und mit delete [] wieder freigegeben werden
int main(int argc, char* argv[])
{ if (argc < 2) return 1;
char* p = string_copy(argv[1]);
//...
delete [] p;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
45
Basistechniken – Zusammenfassung der Anweisungen
anweisung:
deklaration
ausdruck;
if (bedinung) anweisung
if (bedinung) anweisung else anweisung
switch (bedingung) anweisung
while (bedingung) anweisung
do anweisung while (bedingung);
for (for-init-anweisung; bedingung; ausdruck) anweisung
case konstanter-ausdruck: anweisung
default: anweisung
break;
contiunue;
return ausdruck;
goto label;
label: anweisung
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
46
Basistechniken – Auswahlanweisung
• mit if- oder switch-Anweisung kann ein Wert überprüft werden
• Vergleichsoperatoren: == != < <= > >=
• der Vergleich liefert true oder false
• logische Operatoren: && || !
if (p && p->count > 1) //...
if (x) //...
if (x != 0) //...
if (a <= b)
max = b;
else
max = a;
switch (wert) // Vorsicht!
{ case 1:
cout << "Fall 1\n"; // wert == 1 fällt man
case 2:
// auch in nächste case
cout << "Fall 2\n";
break;
default:
cout << "Fall nicht gefunden!\n";
}
max = (a <= b) ? b : a;
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
47
Basistechniken – Schleifenanweisung
• jede Schleifenanweisung führt solange einen Schleifenrumpf aus, wie die Bedinung true ist
• eine Schleife kann explitzit mittels break, return, throw oder goto verlassen
werden
• Endlosschleifen: for (;;) oder while (1)
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
48
Basistechniken – Goto
• goto hat wenige Anwendung in guten Programmen
• in seltenen Fällen wichtig für die Effizienz
void f()
{ for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (M[i][j] == a) goto gefunden;
gefunden:
//...
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
49
Basistechniken – Überblick
• Typen und Deklarationen
• Zeiger, Felder und Strukturen
• Ausdrücke und Anweisungen
• Funktionen
• Namensbereiche und Ausnahmen
• Quelldateien und Programme
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
50
Basistechniken – Funktionsdeklarationen
• eine Funktion muß vor ihrer ersten Verwendung deklariert werden
• eine Funktionsdeklaration bestimmt den Namen der Funktion, den Typ des
zurückgelieferten Wertes und die Anzahl und Typen der Argumente
char* strcpy(char* to, const char* from);
double sqrt(double);
void
exit(int);
• die Argumententypen werden überprüft und ggf. wird eine implizite
Typkonvertierung der Argumente durchgeführt
double sr2 = sqrt(2);
// implizite Konvertierung von int nach double
double sr3 = sqrt("drei"); // Fehler: sqrt() benötigt ein double-Argument
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
51
Basistechniken – Funktionsdeklarationen
Funktionsdefinition
• eine Funktionsdefinition ist eine Funktionsdeklaration, bei der der Rumpf der
Funktion angegeben wird
template <class T> inline void swap(T&, T&);
// ...
// eine Deklaration
template <class T>
// eine Definition
inline void swap(T& x, T& y)
{ T tmp = x;
x = y;
y = tmp;
}
• Argumentnamen sind nicht Teil des Typs und müssen nicht übereinstimmen
• Funktionen können als inline definiert werden
• der Compiler versucht den Code der Funktion anstelle eines Funktionsaufrufs an
der Aufrufstelle der Funktion einzusetzen
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
52
Basistechniken – Funktionsdeklarationen
Statische Variablen
• statische lokale Variablen in einer Funktion, werden nur einmal
erzeugt und initialisiert
void f(int a)
{ while (a--)
{ static int n = 0; // einmal initialisiert
int x = 0;
cout << "n = " << n++ << ", x = " << x++ << ’\n’;
}
}
Aufruf f(3) erzeugt folgende Ausgabe
n = 0, x = 0
n = 1, x = 0
n = 2, x = 0
• statische Variablen in Funktionen sind eine Art »Gedächtnis« für die Funktion,
so daß keine globalen Variablen notwendig sind
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
53
Basistechniken – Argumentenübergabe bei Funktionen
• wird eine Funktion aufgerufen, dann wird Speicher für die formalen Argumente
bereitgestellt
• jedes formale Argument wird durch das korrespondierende aktuelle Argument
initialisiert
• dabei werden die Argumenttypen überprüft und ggf. eine Typkonvertierung
durchgeführt
void f(int wert, int& ref)
{ wert++;
ref++;
}
void g()
{ int i = 1, j = 1;
f(i,j);
// i = 1, j = 2;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
54
Basistechniken – Argumentenübergabe bei Funktionen
• beim Aufruf von f() erhöht wert++ eine lokale Kopie des ersten aktuellen Arguments und eine Referenz ref++ des zweiten Arguments
• i wird als Wert (engl. by value) übergeben;
nach dem Funktionsaufruf hat i in g() den Wert 1
• j wird als Referenz (engl. by reference) übergeben;
nach dem Funktionsaufruf hat j in g() den Wert 2
• für viele Typen (beispielsweise Container) ist es effizienter das Objekt per Referenz und nicht per Wert zu übergeben
• soll das Objekt nicht geändert werden, kann es als const deklariert werden
void f(const vector<int>& V)
{ // ohne explizite Typkonvertierung darf auf
// V keine modifzieren Operationen ausgeführt werden
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
55
Basistechniken – Argumentenübergabe bei Funktionen
• ist ein Feld ein Funktionsargument, wird ein Zeiger auf das
erste Element übergeben
int strlen(const char*);
// übergebene String darf nicht modifiziert werden
void f()
{ char v[] = "ein Feld";
int i = strlen(v);
int j = strlen("Software");
}
• ein Argument vom Typ T[] wird bei der Übergabe in ein T* konvertiert
• Felder können damit nicht per Wert übergeben werden
• die Größe des Feldes ist der aufrufenden Funkton nicht bekannt,
daher terminieren C-Strings mit einem Null-Zeichen
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
56
Basistechniken – Wertrückgabe
• eine nicht als void deklarierte Funktion muß eine Wert zurückliefern; entsprechend kann eine als void deklarierte Funktion keinen Wert zurückliefern
int f1() { }
void f2() { }
// Fehler: es wird kein Wert zurückgeliefert
// OK
int f3() { return 1; } // OK
void f4() { return 1; } // Fehler: return in void Funktion
int f5() { return; }
void f6() { return; }
// Fehler: kein Rückgabewert
// OK
• Rückgabewert durch return-Anweisung
int fac(int n) { return n > 1 ? n * fac(n - 1) : 1; }
• es kann mehrere return-Anweisungen in einer Funktion geben
int fac(int n)
{ if (n > 1) return n * fac(n - 1);
return 1;
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
57
Basistechniken – Überladene Funktionsnamen
• Benutzung desselben Names für Operationen für verschiedene Typen wird Überladen (engl. overloading) genannt
void ausgeben(int);
void ausgeben(const char*);
// gib einen int aus
// gib eine C-String aus
• das Finden der richtigen Version aus einer Menge von überladenen Funktionen
wird durch Suchen nach einer besten Übereinstimmung zwischen den Typen des
Argumentenausdrucks und den formalen Argumenten der Funktion durchgeführt
void ausgeben(long);
void ausgeben(double);
void f()
{ ausgeben(1L);
ausgeben(1.0);
ausgeben(1);
}
//
//
//
//
ausgeben(long)
ausgeben(double)
Fehler, mehrdeutig: ausgeben(long(1))
oder ausgeben(double(1)) rufen?
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
58
Basistechniken – Überladene Funktionsnamen
Manuelle Auflösung von Mehrdeutigkeiten
• häufig kann das Problem der Mehrdeutigkeit durch Hinzufügen einer weiteren
Version behoben werden
void ausgeben(int);
void f()
{ ausgeben(1L);
ausgeben(1);
}
// ausgeben(long)
// ausgeben(int)
• man kann auch eine explizite Typkonvertierung zur Auflösung benutzen
int f(char*);
void f(int*);
void g()
{ f(0);
// Fehler, mehrdeutig f(char*) oder f(int*)
f(static_cast<int*>(0)); // OK: f(int*)
f((char*)0);
// OK: f(char*)
}
• Rückgabetypen werden bei der Auflösung von Überladungen nicht berücksichtigt
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
59
Basistechniken – Default-Argumente
• ein Default-Argument wird bei der Funktionsdeklaration typgeprüft und zum Aufrufszeitpunkt ausgewertet
• Default-Argumente können nur für hinten stehende Argumente benutzt werden
int f(int, int = 0, char* = 0); // OK
int g(int = 0, int = 0, char*); // Fehler
• und dürfen nicht im gleichen Gültigkeitsbereich wiederholt werden
int h(int = 0, int = 0, char* = 0); // OK
int h(int = 0, int = 0, char* = 0); // Fehler, Wiederholung
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
60
Basistechniken – Funktionszeiger
• neben dem Aufruf einer Funktion, ist es möglich auch ihre Adresse zu ermitteln
• ein Zeiger (Funktionszeiger) der auf eine Funktion verweist, kann benutzt werden, um die Funktion aufzurufen
void error(string s) { /* ... */ }
void (*fptr)(string s); // Funktionszeiger
void f()
{ fptr = error; // fptr zeigt auf error
fptr("Fehler"); // rufe error über fptr
}
• bei der Zuweisung an einen Funktionszeiger muß der komplette Funktionstyp
exakt übereinstimmen
void f1(string s);
int f2(string s);
void (*fptr)(string s);
// Funktionszeiger
void g()
{ fptr = f1; // OK
fptr = f2; // Fehler: falscher Rückgabetyp
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
61
Basistechniken – Funktionszeiger
• Anwendung eines Funktionszeigers in der Sortierfunktion ssort()
typedef int (*CFT)(const void*, const void*);
void ssort(void* base, int n, int sz, CFT cmp)
{ for (int gap = n / 2; gap > 0; gap /= 2)
{ for (int i = gap; i < n; ++i)
{ for (int j = i - gap; j >= 0; j -= gap)
{ char* b = static_cast<char*>(base); // notwendiger cast
char* pj = b + j * sz;
// &base[j]
char* pjg = b + (j + gap) * sz;
// &base[j + gap]
if (cmp(pjg,pj) < 0)
{ for (int k = 0; k < sz; ++k)
{ char tmp = pj[k];
pj[k] = pjg[k];
pjg[k] = tmp;
}
}
}
}
}
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
62
Basistechniken – Funktionszeiger
struct user
{ char* name;
char* id;
int
dept;
}
user heads[] = {
"Ritchie D.",
"dr", 11271,
"Sethi R.",
"rs", 11272,
"Kernighan B.", "bk", 11273
};
int cmp1(const void* p, const void* q) // Vergleicht die Namen
{ retrun strcmp(static_cast<const user*>(p)->name,
static_cast<const user*>(q)->name);
}
int cmp2(const void* p, const void* q) // Vergleicht die Namen
{ retrun static_cast<const user*>(p)->dept static_cast<const user*>(q)->dept);
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
63
Basistechniken – Funktionszeiger
• Aufruf der Funktion mit verschiedenen Vergleichsfunktionen
void ausgeben(user* v, int n)
{ for (int i = 0; i < n; ++i)
cout << v[i].name << ’\t’ << v[i].id << ’\t’ << v[i].dept << ’\n’;
}
int main()
{ cout << "Leiter in alphabetischer Reihenfolge\n"
ssort(heads, 3, sizeof(user), cmp1);
ausgeben(heads, 3);
cout << "Leiter sortiert nach Abteilungsnummern\n"
ssort(heads, 3, sizeof(user), cmp2);
ausgeben(heads, 3);
}
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
64
Basistechniken – Überblick
• Typen und Deklarationen
• Zeiger, Felder und Strukturen
• Ausdrücke und Anweisungen
• Funktionen
• Namensbereiche und Ausnahmen
• Quelldateien und Programme
Vielleicht später noch. . .
Oliver Zlotowski, FB IV – Informatik, Universität Trier
❂ ⇐ ⇐▲▼ ⇒ ⇒
65
Herunterladen