home |
20. Abend, Zeiger und Vektoren, Dateiverarbeitung |
||
Druckversion dieser Seite |
Ziele
|
||
Zeiger und Vektoren |
|||
Gleichheit von Zeigern und Vektoren |
|||
Definieren wir ein Array | |||
|
|||
so steht der Name einArray nicht nur für das ganze Array sondern ist auch ein Zeiger auf das erste Element des Arrays! Nehmen wir an, dass das Array im Speicher an der Adresse 1000 beginnt, das sieht unser Speicher ungefähr so aus: | |||
Adresse
|
einArray | ||
einArray
|
1000
|
9 | |
1004
|
8 | ||
1008
|
7 | ||
1012
|
6 | ||
1016
|
5 | ||
Den Beweis liefert ein kleines Testprogramm: | |||
|
|||
Das Programm gibt bei mir folgende Werte aus : | |||
|
|||
Beachte, dass wir nicht die Elemente im Array ausgeben, sondern das was der Compiler für einArray intern benutzt, nähmlich eine Adresse im Speicher. | |||
Der Name, dem wir einem Array geben ist zugleich ein konstanter Zeiger auf das erste Element im Array! Konstant deshalb, weil wir ihn nachträglich nicht mehr neu zuweisen können. | |||
Ein weiteres Beispiel, diesmal mit Zeiger auf char zeigt, dass allgemein gilt: | |||
|
|||
|
|||
Beide Variablen einText und auchEinText werden gleich behandelt, beide Texte erscheinen auf der Konsole. | |||
Was auch möglich ist: | |||
|
|||
Da einText ja nichts anderes als ein Zeiger auf das erste Element des Arrays ist, können wir den Zeiger auchEinText auf die gleiche Stelle zeigen lassen. | |||
Adressierung mit Zeigern und Vektoren |
|||
Um Elemente aus Vektoren/Arrays einzeln anzusprechen verwenden wir die Index-Schreibweise: | |||
|
|||
Aufgrund der Aussage weiter oben, sollte folgendes auch möglich sein: | |||
|
|||
Tatsächlich können wir einen Zeiger verwenden als wäre er ein Vektor/Array. Mit einem Zeiger können wir aber auch rechnen. Die Variable zeiger zeigt zuerst auf das erste Element im Array. Wir können den Zeiger einfach um eins erhöhen, danach sollte er auf das zweite Element zeigen! | |||
|
|||
Hier eine kleine Tabelle, die diesen Zusammenhang verdeutlichen soll. | |||
Das heisst dass folgende Ausdrücke gleichwertig sind : | |||
|
|||
&einArray[2] zeiger + 2 |
|||
Wert des dritten Elementes: | |||
einArray[2] *(zeiger + 2) |
|||
Rechnen mit Zeigern |
|||
Zeiger können wie wir bereits gesehen haben durch Addition verändert werden. Genauso können wir auch subtrahieren oder mit den ++ oder -- Operatoren Zeiger verändern. Mit einem kleinen Beispiel wollen wir das auch probieren. Wenn wir einen Zeiger auf int (int*) mit cout verwenden erscheint in der Konsole der Wert des Zeigers. Verwenden wir aber ein Zeiger auf char (char*) ist das Verhalten anders. Hier werden alle Zeichen ausgegeben bis eine abschliessende 0 erscheint. Wir schreiben eine kleine Funktion, die genau das gleiche macht, wie wenn man einen char* mit << an das cout schickt: | |||
|
|||
Die Funktion TextAusgeben könnte jedoch auch so aussehen, wenn wir anstatt der Zeiger- die Index-Schreibweise verwenden: | |||
|
|||
Wir können auch einen Zeiger von einem anderen subtrahieren: | |||
|
|||
Vektoren/Arrays von Zeigern |
|||
Wir kennen bereits Arrays von char (C-Strings) von int und von anderen selbstdefinierten Klassen (CDListe, Notenliste). Es ist auch möglich Arrays von Zeigern zu bilden. | |||
Nehmen wir an, wir wollen Termine verwalten und merken uns dafür auch den Wochentag. Dafür definieren wir folgenden Aufzählung: | |||
|
|||
Zu jedem Wochentag gehört ein string, den wir für eine Ausgabe verwenden können. Schön wäre, wir könnten ein Array bilden, bei dem beim Index = 0 der string für den Montag steht, bei Index = 1 der für Dienstag. Einzelne Strings zu definieren nützt hier nichts : | |||
|
|||
Aber folgendes ist möglich, wir definieren ein Array von Zeigern: | |||
|
|||
WochentageEng ist also ein Array von Zeigern. Oder auch anders ausgedrückt : ein Array von Arrays, oder auch ein mehrdimensionales Array. Hier ein einfacheres Beispiel für ein mehrdimensionales Array, eine Matrix: | |||
|
|||
Wobei die erste Dimension angibt wieviele Zeilen (sozusagen y) unsere Matrix hat und die zweite wieviele Kolonnen (entsprechend dem x). | |||
Argumente an die main-Funktion |
|||
Erinnert ihr euch an das Registrieren des Programms TsuZeichnen.exe? Das Programm musste dafür so gestartet werden: | |||
|
|||
Dieser string "-regserver" ist ein sogenanntes Programm-Argument. Es ist nämlich möglich einem Programm direkt beim Starten Parameter zu übergeben. Um in einem Programm Argumente zu verwenden muss unsere main-Funktion anders definiert werden. Betrachte folgendes Beispiel: | |||
|
|||
Das erste Argument im Array ist der Pfadname, mit dem das Programm gestartet wurde. Die nächsten sind Argumente, die wir dem Programm zusätzlich übergeben können. | |||
Dateiverarbeitung |
|||
Die File-Stream Klassen |
|||
Wie die iostream-Klassen, die wir zur Ein- und Ausgabe von der Konsole verwenden, gibt es Klassen um Dateien zu schreiben und zu lesen. Das cin-Objekt ist ein Objekt der Klasse istream. Das Objekt cout ist von der Klasse ostream. Um in eine Datei zu schreiben, erzeugen wir ein Objekt der Klasse ofstream (beachte das f für file). Um aus einer Datei zu lesen verwenden wir ein Objekt der Klasse ifstream. | |||
Grundfunktionen der File-Stream Klassen |
|||
Die wichtigsten Funktionen der File-Stream Klassen sind sicherlich die Konstruktoren. Beim Konstruktor der Klasse ofstream oder ifstream ist es möglich direkt einen Dateinamen anzugeben. | |||
Wie bei cin und cout ist es möglich mit den Operatoren << und >> in eine Datei zu schreiben beziehungsweise zu lesen. | |||
|
|||
Um eine ganze Zeile aus einer Datei zu lesen verwenden wir die Funktion getline. | |||
Weitere Funktionen der File-Stream Klassen |
|||
Mit close wird eine Datei wieder geschlossen. Die Datei wird auch geschlossen, wenn das File-Stream Objekt zerstört wird. Wichtig ist das im Beispiel oben, weil wir eine Datei zuerst zum Schreiben erzeugen und diese danach zum Lesen wieder öffnen. Es kann aber sein, dass eine Datei nicht gleichzeitig zum Schreiben und Lesen geöffnet werden kann. Mit der Methode is_open wird geprüft, ob eine Datei geöffnet werden konnte. | |||
Probleme beim Mischen von Text und Zahlen |
|||
Text und Zahlen beim Schreiben in ein Datei zu mischen stellt kein Problem dar: | |||
|
|||
Die Aufgabe, die es zu lösen gilt ist, die Textzeilen in ein string-Objekt zu lesen und die Zahlen in eine int-Variable. Folgender Code funktioniert nicht! | |||
|
|||
Die erste Zeile und die erste Zahl werden korrekt eingelesen, aber die nachfolgenden nicht mehr. Nach dem ersten Einlesen einer Zahl mit | |||
|
|||
bleibt das endl im File-Stream und das nachfolgende getline liest nur bis zum nächsten endl, der string bleibt leer. Danach stimmt nichts mehr, denn das Lesen der nächsten Zahl schlägt auch fehl... etc. | |||
Mit der Methode ignore können wir das endl aus dem stream auslesen und ignorieren, danach funktioniert das Lesen so wie wir uns das vorstellen: | |||
|
|||
Ähnlichkeit von stream-Klassen |
|||
Wir können ein Objekt der Klasse anstelle ofstream anstelle eines Objektes der Klasse ostream verwenden. Auf diese Weise haben wir in der Übung Notenliste zur Ausgabe nur eine Funktion geschrieben, die als Parameter ein Objekt der Klasse ostream verwendet. Dieser Funktion können wir als Parameter das cout-Objekt übergeben genauso wie ein ofstream-Objekt, das wir als Datei erzeugen. |