2009-06-17 8 views
1

Ich lerne über Zeiger und ein Konzept beunruhigt mich. Ich verstehe, dass wenn Sie einen Zeiger (z. B. "Zeiger1") des Typs INT, der auf ein Array zeigt, dann können Sie dieses Array mit INTS füllen. Wenn Sie ein Mitglied des Arrays adressieren möchten, können Sie den Zeiger verwenden und Sie können pointer1 ++; um durch das Array zu gehen. Das Programm weiß, dass es ein Array von INTs ist, so dass es weiß, dass es in INT-Größenschritten durchlaufen werden muss. Aber was ist, wenn das Array aus Strings besteht, die in der Länge variieren können. Wie weiß es, was zu tun ist, wenn Sie versuchen, mit ++ zu inkrementieren, da jedes Element eine andere Länge hat?Zeigerarithmetik auf String-Arrays, wie geht C++ damit um?

Ähnlich, wenn Sie einen Vektor von Zeichenfolgen erstellen und das reserve-Schlüsselwort verwenden, wie weiß es, wie viel zu reservieren ist, wenn Zeichenfolgen unterschiedliche Längen haben können? Dies ist wahrscheinlich wirklich offensichtlich, aber ich kann es nicht ausarbeiten und es passt nicht in meine aktuelle (wahrscheinlich falsch) Denken auf Zeigern. Danke

Antwort

10

Ganz einfach.

Ein Array von Strings unterscheidet sich von einem Vektor von Strings.

Ein Array von Strings (C-style Pointer) ist ein Array von Zeigern auf ein Array von Zeichen, "char **". Jedes Element im Array-of-Strings hat also die Größe "Pointer-to-char-array", sodass es ohne Probleme durch die Elemente im Stringarray gehen kann. Die Zeiger in dem Array können auf unterschiedlich große Speicherblöcke zeigen.

Mit einem Vektor von Strings ist es ein Array von String-Objekten (C++ Stil). Jedes String-Objekt hat das gleiche Objekt Größe, aber enthält irgendwo einen Zeiger auf ein Stück Speicher, in dem der Inhalt der Zeichenfolge tatsächlich gespeichert wird. Also sind in diesem Fall die Elemente im Vektor auch in der Größe identisch, obwohl sie sich von "nur einem Zeiger-zu-Char-Array" unterscheiden, was eine einfache Elementadressenberechnung erlaubt.

+0

+1: gute Erklärung –

+0

Vielen Dank, ich verstehe jetzt. – Columbo

+0

Dies erklärt richtig, warum alle std :: string Objekte die gleiche Größe haben. Aber ich stimme nicht zu, dass "Array von Strings" char * [] bedeutet, während "Vektor von Strings" den Vektor bedeutet. Sie können eine Zeichenkette [] oder einen Vektor haben, wenn Sie also über "Strings" sprechen, müssen Sie (vielleicht aus dem Kontext) klarstellen, was Sie meinen. –

0

Ein Array von Strings ist ein Array von Zeigern auf das erste Zeichen einiger Strings. Die Größe eines Zeigers auf ein Zeichen hat wahrscheinlich die gleiche Größe wie ein Zeiger auf ein int.

Im Wesentlichen ist ein 2D-Array nicht notwendigerweise linear im Speicher, die Arrays, auf die verwiesen wird, könnten irgendwo sein.

6

Dies liegt daran, eine Zeichenfolge (zumindest in C/C++) ist nicht ganz die gleiche Art wie eine ganze Zahl. Wenn wir C-Strings sprechen, dann eine Reihe von ihnen wie

char* test[3] = { "foo", "bar", "baz" }; 

, was tatsächlich unter der Haube passiert ist, dass „Test“ ist ein Array von Zeigern, von denen jeder Punkt auf die tatsächlichen Daten, bei denen Die Charaktere sind. Lassen Sie uns sagen, nach dem Zufallsprinzip, dass die „test“ Array an der Adresse 0x10000 Speicher beginnt, und dass Zeiger vier Bytes lang sind, dann könnten wir

test[0] (memory location 0x10000) contains 0x10020 
test[1] (memory location 0x10004) contains 0x10074 
test[2] (memory location 0x10008) contains 0x10320 

Dann haben wir an den Speicherstellen um 0x10020 aussehen könnte, würden wir findet die aktuellen Zeichendatum:

test[0][0] (memory location 0x10020) contains 'f' 
test[0][1] (memory location 0x10021) contains 'o' 
test[0][2] (memory location 0x10022) contains 'o' 
test[0][3] (memory location 0x10023) contains '\0' 

Und um Speicherplatz 0x10074

test[1][0] (memory location 0x10074) contains 'b' 
test[1][1] (memory location 0x10075) contains 'a' 
test[1][2] (memory location 0x10076) contains 'r' 
test[1][3] (memory location 0x10077) contains '\0' 

Mit C++ std :: string Objekten viel die gleiche Sache vor sich geht: die tatsächlichen C++ st Ring-Objekt "enthält" die Zeichen nicht, da die Strings variabler Länge sind. Was es tatsächlich enthält, ist ein Zeiger auf die Zeichen.(Zumindest in einer einfachen Implementierung von std :: string - in Wirklichkeit hat es eine kompliziertere Struktur, um bessere Speicherausnutzung und -leistung zu bieten).

+0

Danke, Ihre und haavees Antworten haben wirklich geholfen – Columbo

0

Dies mag wie Pedanterie erscheinen, aber in einem Shoot-yer-Fuß-Sprache wie C++ ist dies wichtig: in Ihrer ursprünglichen Frage sagen Sie:

Sie können Zeiger1 tun ++; um durch das Array zu gehen.

Postinkrement (Zeiger1 ++) ist in der Regel semantisch falsch hier, weil es „Zuwachs Zeiger1 aber den Ausdruckswert auf den ursprünglichen Wert von Zeiger1 halten“ bedeutet. Wenn Sie den ursprünglichen Wert von pointer1 nicht benötigen, verwenden Sie stattdessen pre-increment (++ pointer1), was semantisch genau die Bedeutung von "inkrementiere den Zeiger um eins" hat.

Aus irgendeinem Grund die meisten C++ Lehrbücher tun, um die überall Postinkrement Sache, neue C++ Lehre - ;-) schlechte Gewohnheiten ers

0

In C++ Arrays und Vektoren sind immer Elemente mit fester Größe enthält. Strings passen diese Bedingung, weil Ihre String-Elemente dann entweder Zeiger auf null-terminierte C-Strings (char *) sind, die irgendwo anders gespeichert sind, oder einfache std :: string-Objekte.

Das std :: string-Objekt hat eine konstante Größe, die eigentlichen String-Daten werden woanders zugewiesen (außer für kleine String-Optimierung, aber das ist eine andere Geschichte).

vector<string> a; 
a.resize(2); // allocate memory for 2 strings of any length. 

vector<char *> b; 
b.resize(2); // allocate memory for 2 string pointers. 

vector<char> c; // one string. Should use std::string instead. 
c.resize(2); // allocate memory for 2 characters (including or not the terminator). 

Beachten Sie, dass die reserve() Funktion von std :: vector nur den Vektor zum Wachsen vorbereiten. Es wird hauptsächlich zur Optimierung verwendet. Sie möchten wahrscheinlich resize() verwenden.

Verwandte Themen