webentwicklung-frage-antwort-db.com.de

Zeiger und Strings C++

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.

13
DWade64

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? .

16

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.

3
Barmar

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

3
janepe

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".

3
Turix

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.

3
user1508519

Sie stellen die richtigen Fragen als Lernende.

Antworten:

  1. In C++ ist string ein Objekt, das c_str() im Wesentlichen einen Zeiger auf das erste -Zeichen der Zeichenfolge zurückgibt (C-Stil)
  2. Sie haben Recht mit Strings in C, die Variable zeigt tatsächlich auf das erste Zeichen von Des Strings
  3. C++ führt viele Dinge aus, die auf dem Typ der Variablen basieren. Wenn Sie ein string object übergeben, gibt die Variable cout den String aus. Außerdem ist C++ intelligent genug, um festzustellen, dass *testing illegal ist
2
Sudhee

Warum 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.

2
Brian Stinar

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:

  1. Im Gegensatz zu anderen Arrays ist es einfacher, sich eine C-Style-Zeichenfolge als eine ganze Reihe von Zeichen vorzustellen, sodass der Gedanke erhalten bleibt
  2. Dies ist der einfachste Weg, ein Array als Funktionsparameter zu verwenden, um nur einen Zeiger auf das erste Element zu erhalten, insbesondere im Fall von C-Strings, wo sie bis zum NULL-Zeichen gelesen werden können.
1
IllusiveBrian

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.

1
jundl77