Der folgende Quelltext aus Beispiel 3 der dritten Sitzung (04.11.2014, http://www.hki.uni-koeln.de/node/17010) veranschaulicht die Arbeit mit Zeichenketten in Form von char-Arrays und dem Adressoperator („&“):
#include <iostream> using namespace std; int main() { char zeile[100] = "Dies ist ein Text aus mi Nacht."; int worte[20]; int anzahlWorte; worte[0] = 0; anzahlWorte = 1; for (int i = 0; zeile[i] != '\0'; i = i + 1) { if (zeile[i] == ' ') { zeile[i] = '\0'; worte[anzahlWorte] = i + 1; anzahlWorte = anzahlWorte + 1; } } for (int i = 0; i<anzahlWorte; i++) { cout << "Ein Wort: "; cout << &zeile[worte[i]]; cout << endl; } return 1; }
Nach Kompilierung präsentiert sich der Quelltext mit einer Konsolenanwendung, in der die einzelnen Wörter der Zeichenkette „Dies ist ein Text aus mi Nacht.“ zeilenweise ausgegeben werden:

Fehlt der Adressoperator „&“ in der Ausgaberoutine „cout“ (Zeile 27), so wird nur der jeweils erste Buchstabe der einzelnen Wörter zeilenweise ausgegeben:
Warum das so ist, das erschließt sich mit einem Blick auf den Arbeitsspeicher und der Funktionalität, die das Konsolenprogramm realisiert. In den ersten Zeilen werden die Arbeitsvariablen deklariert und initialisiert:
// char-Array zur Speicherung der Arbeitszeichenkette char zeile[100] = "Dies ist ein Text aus mi Nacht."; // Das int-Array "worte" dient dazu, die Startpositionen der einzelnen Wörter zu speichern int worte[20]; // Anzahl der verarbeiteten Wörter int anzahlWorte; // Initialisierung: Das erste Wort startet an Position 0 der Arbeitszeichenkette worte[0] = 0; // Zähler für die Anzahl der Wörter anzahlWorte = 1;
Die erste for-Schleife (Zeilen 14-22) dient dazu, die Arbeitszeichenkette Zeichen für Zeichen, d.h. von zeile[0] (Zeichen „D“) bis zeile[31] (Zeichen „\0“) zu durchlaufen und jedes Leerzeichen durch den String-Terminator „\0“ zu ersetzen:
// Jedes Zeichen der Arbeitszeichenkette betrachten, // solange das betrachtete Zeichen nicht das String-terminierende Zeichen "\0" ist for (int i = 0; zeile[i] != '\0'; i = i + 1) { // Ist das aktuell betrachtete Zeichen ein Leerzeichen? if (zeile[i] == ' ') { // Wenn ja, dann ersetze das Leerzeichen durch den Stringterminator zeile[i] = '\0'; // Startposition des Wortes speichern, das mit der vorherigen Zeile abgeschlossen wurde worte[anzahlWorte] = i + 1; // Wortzähler erhöhen anzahlWorte = anzahlWorte + 1; } }
Hat die for-Schleife ihre Arbeit beendet, stehen die Anfangspositionen der einzelnen Wörter im Array „worte“:
- Wort 0 beginnt an Position 0 (gespeichert in worte[0])
- Wort 1 beginnt an Position 5 (gespeichert in worte[1])
- Wort 2 beginnt an Position 9 (gespeichert in worte[2])
- Wort 3 beginnt an Position 13 (gespeichert in worte[3])
- Wort 4 beginnt an Position 18 (gespeichert in worte[4])
- Wort 5 beginnt an Position 22 (gespeichert in worte[5])
- Wort 6 beginnt an Position 25 (gespeichert in worte[6])
Nachvollziehen lässt sich das mit der folgenden for-Schleife:
for (int i = 0; i < anzahlWorte; i++) { cout << "Wort " << i << " beginnt an Position " << worte[i] << " (gespeichert in worte[" << i << "])" << endl; }
Mit der letzten for-Schleife werden die einzelnen Wörter der Zeichenkette, wie sie in dem char-Array „zeile“ gespeichert sind, nach und nach ausgegeben:
// Gehe jedes mit "\0" abgeschlossene Wort durch for (int i = 0; i < anzahlWorte; i++) { cout << "Ein Wort: "; // Zugriff auf die Startadresse des 1., 2., 3., etc. Wortes cout << &zeile[worte[i]]; cout << endl; }
Dass nun durch die Verwendung des Adressoperators „&“ die entsprechenden Wörter – und nicht einzelne Zeichen / Buchstaben – ausgegeben werden, erschließt sich mit einem Blick auf die Speicherung des char-Arrays „zeile“ im Arbeitsspeicher und der Adressierung seiner Bestandteile. Nach der Initialisierung des char-Arrays „zeile“ mit der Anweisung
char zeile[100] = "Dies ist ein Text aus mi Nacht.";
belegt das Array 31 Speicherstellen vom Typ char (Unterstrich == Leerzeichen):
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
D | i | e | s | _ | i | s | t | _ | e | i | n | _ | T | e | x |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
t | _ | a | u | s | _ | m | i | _ | N | a | c | h | t | . | \0 |
Nach der ersten for-Schleife schaut das Array so aus:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
D | i | e | s | \0 | i | s | t | \0 | e | i | n | \0 | T | e | x |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
t | \0 | a | u | s | \0 | m | i | \0 | N | a | c | h | t | . | \0 |
Würde der Adressoperator „&“ in der for-Schleife weggelassen und die Ausgabe mit der Anweisung
cout << zeile[worte[i]];
vorgenommen, so würden die Inhalte der Speicherstellen 0, 5, 9, 13, 18, 22, 25 (vgl. Array „worte“) ausgegeben:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
D | i | e | s | \0 | i | s | t | \0 | e | i | n | \0 | T | e | x |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
t | \0 | a | u | s | \0 | m | i | \0 | N | a | c | h | t | . | \0 |
Wird nun in der for-Schleife mit
cout << &zeile[worte[i]];
der Adressoperator verwendet, dann wird an cout die Startadresse („los geht’s an Stelle 0 des Arrays“, „los geht’s an Stelle 5 des Arrays“) des entsprechenden Wortes übergeben. Die Funktion cout gibt anschließend alle Zeichen vom Anfang der Startadresse bis zum String-terminierenden Zeichen „\0“ aus. Weil cout in der Anweisung ohne „&“ mit dem Zeiger auf die Adresse der Speicherstelle arbeitet, lässt sich auf die einzelnen Zeichen der Zeichenkette wiederum über die folgende Anweisung zugreifen:
cout << *&zeile[worte[i]];
Das ist ein Zeiger („*“) auf die Adresse („&“) der Speicherstelle mit dem Index „worte[i]“ des char-Arrays „zeile“ – klingt kompliziert, ist eigentlich aber ganz einfach. 😉
Nützliche Links:
Comments are closed, but trackbacks and pingbacks are open.