Ich lehre mich selbst in C++ und bin etwas verwirrt über Zeiger (insbesondere im folgenden Quellcode). Aber zuerst zeige ich Ihnen, was ich weiß (und dann den Code dem gegenüberstellen, weil ich das Gefühl habe, dass es einige Widersprüche gibt).
Was ich weiß:
int Age = 30;
int* pointer = &Age;
cout << "The location of variable Age is: " << pointer << endl;
cout << "The value stored in this location is: " << *pointer << endl;
Zeiger enthalten Speicheradressen. Mit dem Indirektionsoperator (dereference) (der *) können Sie auf das zugreifen, was im Speicher des Zeigers gespeichert ist. Auf den Code in diesem Buch habe ich Schwierigkeiten zu verstehen ...
cout << "Enter your name: ";
string name;
getline(cin, name); //gets full line up to NULL terminating character
int CharsToAllocate = name.length() + 1; //calculates length of string input
//adds one onto it to adjust for NULL character
char* CopyOfName = new char[CharsToAllocate];
// pointer to char's called CopyOfName, is given the memory address of the
//beginning of a block
//of memory enough to fit CharsToAllocate. Why we added 1? Because char's need a
//NULL terminating character (\0)
strcpy(CopyOfName, name.c_str()); //copies the string name, into a pointer?
cout << "Dynamically allocated buffer contains: " << CopyOfName << endl;
delete[] CopyOfName; //always delete a pointer assigned by new to prevent memory leaks
Ausgabe:
Enter your name: Adam
Dynamically allocated buffer contains: Adam
Die Kommentare im obigen Code sind meine Kommentare. Mein Problem beginnt mit strcpy
. Warum wird name.c_str()
in einen Zeiger CopyOfName
kopiert? Bedeutet das, dass alle Strings wesentliche Zeiger sind? So wie string testing = "Hello world"; Zeigt tatsächlich ein Zeiger auf den Speicherplatz, an dem "H" gespeichert ist?
Warum wird in der Ausdruck-Anweisung CopyOfName
und nicht *CopyOfName
verwendet? Zeiger halten Speicheradressen? Mit *CopyOfName
wird der Inhalt des Speicherplatzes ausgedruckt. Ich habe dies in Code :: Blocks versucht und wenn der Eingabetext "Hallo Welt" war. Die Verwendung von *CopyOfName
in der Ausdruck-Anweisung würde nur ein "H" ergeben. Dies ist sinnvoll, da bei der Deklaration eines Speicherblocks mit der 'neuen' Sache tatsächlich ein Zeiger auf den ersten Teil des dynamisch zugewiesenen Speicherblocks zurückgegeben wird.
Der einzige Weg, auf den ich das abstimmen kann, ist, ob ein String tatsächlich ein Zeiger ist.
string testing = "Confused";
cout << testing << endl;
würde das Wort "verwirrt" ausdrucken
Jedoch wenn ich versuche zu kompilieren
string testing = "Confused";
cout << *testing;
Ich bekomme eine Fehlermeldung.
Um meine Frage zusammenzufassen, versuche ich den Code mit strcpy
und der cout
-Anweisung zu verstehen.
Es scheint, als würden Sie verstehen, was Strings im C-Stil sind, aber zusammenfassend sind dies nur Arrays von Zeichen im Speicher, die normalerweise durch ein Nullzeichen \0
abgeschlossen werden. Normalerweise werden sie über einen char*
referenziert, der auf den ersten Buchstaben der Zeichenfolge zeigt. Wenn sie gedruckt werden, werden normalerweise die Zeichen der Zeichenfolge beginnend mit der ersten Zeichenfolge gedruckt, und das Drucken (oder Kopieren usw.) stoppt, wenn das \0
-Abschlusszeichen erreicht wird.
Ein std::string
ist eine Klasse, die eine Zeichenfolge im C-Stil (normalerweise) umgibt. Dies bedeutet, dass ein std::string
-Objekt (normalerweise) eine private Zeichenfolge im C-Stil aufweist, die zum Implementieren seiner Funktionalität verwendet wird. Die Funktion std::string::c_str()
gibt einen Zeiger auf diese zugrunde liegende Zeichenfolge im C-Stil zurück.
Nehmen wir an, char *str;
verweist auf eine Zeichenfolge im C-Stil. Wenn Sie versuchen, cout << *str << endl;
auszuführen, haben Sie festgestellt, dass nur das erste Zeichen gedruckt wird. Das liegt an der Überlastung der C++ - Funktion. Der Datentyp von *str
ist char
, daher wird die char
-Version von cout
aufgerufen, und das einzelne Zeichen *str
wird getreu ausgegeben. Aus Gründen der Kompatibilität mit Zeichenfolgen im C-Stil behandelt die Version von cout
, die einen char*
als Argument verwendet, den Zeiger zum Ausdrucken als Zeichenfolge im C-Stil. Wenn Sie zum Beispiel cout
einen int*
haben, wird die zugrunde liegende int
nicht gedruckt.
Edit: Noch ein Kommentar:
Ihr Versuch, ein std::string
-Objekt zu demeferenzieren, ist fehlgeschlagen, weil es sich in der Tat nicht um einen Zeiger handelt. Sie könnten den Rückgabewert von std::string::c_str()
dereferenzieren und die erste char
der Zeichenfolge zurückgeben.
Related: Wie wird std :: string implementiert? .
In C sind Strings einfach Arrays von Zeichen. Arrays werden in Zeiger zerfallen, wenn sie als Argument für eine Funktion verwendet werden.
In C++ ist std::string
eine Klasse. Es enthält ein Zeichenarray im C-Stil, und c_str()
gibt es zurück. Die Zeichenfolge selbst ist jedoch kein Zeiger. Sie können sie also nicht dereferenzieren. Sie müssen die c_str()
-Methode verwenden, um einen Zeiger auf den String-Inhalt zu erhalten.
So wie
string testing = "Hello world";
Zeigt eigentlich ein Zeiger auf den Speicherort, an dem "H" gespeichert ist?
Nein, oben haben Sie ein Objekt namens string
. Es wäre für char* testing = "Hello World"
wahr. Wie Sie sehen, ist es sogar als Zeiger deklariert und zeigt auf das erste Zeichen in der Zeichenfolge - H.
Warum ist es in der Ausdrucksaussage, dass
CopyOfName
nicht*CopyOfName
ist? Zeiger halten Speicheradressen? Mit*CopyOfName
wird der Inhalt des Speicherplatzes ausgedruckt. Ich habe dies in Codeblöcken ausprobiert und wenn der Eingabetext "Hello World" war. Die Verwendung von*CopyOfName
in der Ausdruck-Anweisung würde nur ein "H" ergeben.
cout take zeige auf das erste Zeichen des Strings, also CopyOfName
ist richtig. In diesem Fall wird jedes Zeichen beginnend mit H gedruckt, bis\0 (Nullzeichen) gefunden wird. Zeichenfolgen wie "hallo" haben eigentlich 6 Zeichen - 'h' 'e' 'l' 'l' 'o' '\ 0' Wenn Sie *CopyOfName
schreiben, dereferenzieren Sie diesen Zeiger und *CopyOfName
ist eigentlich nur ein Zeichen
Ihre Fragen in Ordnung bringen:
"Warum wird name.c_str () in einen Zeiger CopyOfName kopiert? Bedeutet das .__, dass alle Zeichenfolgen wesentliche Zeiger sind? So wie String testing = " Hallo Welt "; __. Wo wird "H" gespeichert?
Wie Yu Hao in seinem Kommentar hervorgehoben hat, ist es wichtig, den Unterschied zwischen C++ - Zeichenketten und C-Zeichenketten zu verstehen. Im ersten Fall handelt es sich um ein "undurchsichtiges" Objekt, während Sie sich im letzteren Fall im Wesentlichen mit einem "Array" von Zeichen befassen.
Mit C++ - String-Objekten können Sie die c_str()
-Methode verwenden, um ein (Zeiger auf ein C-Stil-Array von Zeichen zu erhalten. In C wird ein Array unter Verwendung des Zeigers auf den Anfang des Arrays dargestellt, und dann werden Referenzen durch Angabe eines Offsets (des Index in das Array) von dieser Startadresse erzielt. Die Antwort auf die letzte Frage in diesem Bundle lautet also "Ja". Der Zeiger auf die Zeichenfolge im C-Stil ist der Zeiger auf das erste Zeichen "H".
"Warum wird CopyOfName in der Ausdruck-Anweisung nicht als * CopyOfName angegeben? Zeiger enthalten Speicheradressen?"
Weil der Operator <<
zur Verarbeitung von C-Strings überladen ist. Die Implementierung dieser Methode "weiß, was mit dem Zeiger zu tun ist".
Zeiger sind nicht dasselbe wie Arrays. String-Literale sind unveränderlich. Wenn Sie einen Zeiger auf ein String-Literal haben, können Sie dessen Inhalt überprüfen. Das Ändern dieser Werte ist jedoch undefiniertes Verhalten. Wenn Sie diese Syntax verwenden:
char arr[] = "hi there";
Das String-Literal wird in das Array kopiert. Da Sie keine Größe angeben, leitet der Compiler diese automatisch ab. Der NUL
-Terminator wird automatisch hinzugefügt. Wenn Sie eine Größe angeben, müssen Sie sicherstellen, dass der Puffer den NUL
-Terminator aufnehmen kann. Deshalb:
char arr[5] = "hello";
ist ein Fehler. Wenn Sie die Klammer-Initialisierungssyntax verwenden:
char arr[5] = { "h", "e", "l", "l", "o" };
Dies ist ein Fehler, da kein NUL
-Terminator vorhanden ist. Wenn Sie strcpy
verwenden, wird ein NUL
-Terminator für Sie hinzugefügt.
std::string
bietet zwei Methoden, um einen Zeiger auf den zugrunde liegenden Inhalt zurückzugeben: data
und c_str
. Vor C++ 11 besteht der einzige Unterschied darin, dass data
den NUL
-Terminator nicht enthält. In C++ 11 ist dies jetzt der Fall, daher ist ihr Verhalten identisch. Da der Zeiger leicht ungültig gemacht werden kann, ist das Manipulieren dieser Zeiger nicht sicher. Es ist auch nicht sicher, char * ptr = str.c_str();
auszuführen, da die Lebensdauer des von c_str
zurückgegebenen Arrays am Semikolon endet. Sie müssen es in einen Puffer kopieren.
Sie stellen die richtigen Fragen als Lernende.
Antworten:
string
ein Objekt, das c_str()
im Wesentlichen einen Zeiger auf das erste -Zeichen der Zeichenfolge zurückgibt (C-Stil)string
object übergeben, gibt die Variable cout
den String aus. Außerdem ist C++ intelligent genug, um festzustellen, dass *testing
illegal istWarum wird name.c_str () in einen Zeiger CopyOfName kopiert?
"name" ist eine STL-Zeichenfolge. Dies ist ein Objekt, das sich von einem C-String unterscheidet. Ein C-String ist eine Sammlung von Speicher, die Zeichen enthält und eine Null-Terminierung hat. Wenn Sie also STL-Strings verwenden und diese in C-Strings umwandeln möchten, verwenden Sie .c_str (), um den C-String abzurufen.
CopyOfName enthält genügend Speicherplatz, um den Namen zu speichern, da es für den Namen reserviert wurde.
cout hat eine Menge verschiedener Dinge, die Sie mit << verwenden können. Es sieht so aus, als könnte es char * 's (was c-Strings sind) oder STL-Strings sein. Es sieht nicht so aus, als könnte es Zeiger auf STL-Strings bringen.
Ich war irgendwie verwirrt, als Sie "testing" eingeführt haben, aber ich denke, Sie werden zwischen C-Strings (die Zeichen des Typs Char *) und STL-Strings sind, die Objekte sind. Fühle dich nicht schlecht oder gib nicht auf. Dieses Zeug ist knifflig und braucht eine Weile, um es zu bekommen.
Ich würde empfehlen, den Unterschied zwischen den Begriffen "C-String", "Char *", "STL-String" und möglicherweise "Zeiger auf STL-String" zu verstehen.
In C, wo keine C++ - Standardzeichenfolgen vorhanden waren, war char * die sogenannte "Zeichenfolge". Wie Sie bereits bemerkt haben, handelt es sich um ein Array von Zeichen, das mit einem Nullzeichen endet. Bei fast jeder Standard-Bibliotheksfunktion, die eine Zeichenfolge im C-Stil verwendet, wird aus zwei Gründen ein Zeiger auf die Zeichenfolge verwendet:
Ich denke, was Sie tun, und andere korrigieren mich bitte, wenn ich falsch liege. Sie kopieren Ihren String in ein dynamisches Zeichenfeld. Nein, Sie kopieren es nicht in einen Zeiger. Der Grund für einen Zeiger ist, dass dynamische Arrays Zeiger benötigen, um ihren Speicher richtig zuzuordnen, wenn ich Recht habe.