Anstelle von: char * writable = new char [str.size () + 1]; Sie können char writable [str.size () + 1] verwenden. Dann müssen Sie sich keine Gedanken mehr über das Löschen von beschreibbaren oder Ausnahmebehandlungen machen.
7
Sie können str.size () nur verwenden, wenn die Größe zum Zeitpunkt der Kompilierung bekannt ist. Außerdem kann Ihr Stapel überlaufen, wenn der Wert für die feste Größe sehr groß ist.
@cegprakash strcpyund mallocsind nicht wirklich die C ++ Art.
Boycy
4
Nein, char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)wäre aber idiomatischer C ++. strcpy()und malloc()sind nicht falsch oder problematisch, aber es scheint inkonsistent, eine C ++ - Zeichenfolge und C-Bibliotheksfunktionen mit C ++ - Entsprechungen im selben Codeblock zu verwenden.
Boycy
Antworten:
1055
Wenn Sie a nur std::stringan eine Funktion übergeben möchten, die const char*Sie benötigen , können Sie diese verwenden
std::string str;constchar* c = str.c_str();
Wenn Sie eine beschreibbare Kopie erhalten möchten char *, können Sie dies folgendermaßen tun:
std::string str;char* writable =newchar[str.size()+1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()]='\0';// don't forget the terminating 0// don't forget to free the string after finished using itdelete[] writable;
Bearbeiten : Beachten Sie, dass das oben Genannte nicht ausnahmesicher ist. Wenn zwischen dem newAnruf und dem deleteAnruf etwas ausgelöst wird, geht Speicher verloren, da nichts deleteautomatisch nach Ihnen ruft . Es gibt zwei unmittelbare Möglichkeiten, dies zu lösen.
boost :: scoped_array
boost::scoped_array löscht den Speicher für Sie, wenn Sie den Gültigkeitsbereich verlassen:
std::string str;
boost::scoped_array<char> writable(newchar[str.size()+1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()]='\0';// don't forget the terminating 0// get the char* using writable.get()// memory is automatically freed if the smart pointer goes // out of scope
std :: vector
Dies ist der Standardweg (erfordert keine externe Bibliothek). Sie verwenden std::vector, wodurch der Speicher für Sie vollständig verwaltet wird.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');// get the char* using &writable[0] or &*writable.begin()
Verwenden Sie einfach char * result = strdup (str.c_str ());
Jasper Bekkers
63
Sie könnten, aber strdup ist keine AC- oder C ++ - Standardfunktion, es ist von posix :)
Johannes Schaub - litb
14
Was ich im Allgemeinen wahrscheinlich bevorzugen würde, ist std :: vector <char> writable (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & beschreibbar [0];
Johannes Schaub - litb
17
std :: copy ist die c ++ - Methode, um dies zu tun, ohne auf den Zeichenfolgenzeiger zugreifen zu müssen. Ich versuche, C-Funktionen so weit wie möglich zu vermeiden.
Johannes Schaub - litb
16
Ab C ++ 17 wird std::string::data()jetzt a CharT*anstelle von a zurückgegeben const CharT*. Es könnte eine gute Idee sein, diese Antwort zu aktualisieren :)
Rakete1111
192
Gegeben sagen ...
std::string x ="hello";
Abrufen eines "char *" oder "const char *" aus einem "String"
So erhalten Sie einen Zeichenzeiger, der gültig ist, während er xim Gültigkeitsbereich bleibt und nicht weiter geändert wird
C ++ 11 vereinfacht die Dinge; Die folgenden Optionen bieten Zugriff auf denselben internen Zeichenfolgenpuffer:
constchar* p_c_str = x.c_str();constchar* p_data = x.data();char* p_writable_data = x.data();// for non-const x from C++17 constchar* p_x0 =&x[0];char* p_x0_rw =&x[0];// compiles iff x is not const...
Alle oben genannten Zeiger enthalten denselben Wert - die Adresse des ersten Zeichens im Puffer. Sogar eine leere Zeichenfolge hat ein "erstes Zeichen im Puffer", da C ++ 11 garantiert, dass nach dem explizit zugewiesenen Zeichenfolgeninhalt immer ein zusätzliches NUL / 0-Abschlusszeichen beibehalten wird (z. B. std::string("this\0that", 9)wird ein Puffer gespeichert "this\0that\0").
Angesichts eines der oben genannten Hinweise:
char c = p[n];// valid for n <= x.size()// i.e. you can safely read the NUL at p[x.size()]
Nur für den nicht - constZeiger p_writable_dataund aus &x[0]:
p_writable_data[n]= c;
p_x0_rw[n]= c;// valid for n <= x.size() - 1// i.e. don't overwrite the implementation maintained NUL
Das Schreiben eines NUL an einer anderen Stelle in der Zeichenfolge ändert nichts an den string's size(); string's dürfen eine beliebige Anzahl von NULs enthalten - sie werden von nicht speziell behandelt std::string(wie in C ++ 03).
In C ++ 03 waren die Dinge erheblich komplizierter (wichtige Unterschiede hervorgehoben ):
x.data()
kehrt const char*zum internen Puffer der Zeichenfolge zurück , der vom Standard nicht benötigt wurde, um mit einem NUL abzuschließen (dh es können nicht ['h', 'e', 'l', 'l', 'o']initialisierte oder Garbage-Werte folgen, wobei versehentliche Zugriffe darauf ein undefiniertes Verhalten aufweisen ).
x.size()Zeichen sind sicher zu lesen, dh x[0]durchx[x.size() - 1]
Für leere Zeichenfolgen ist ein Nicht-NULL-Zeiger garantiert, zu dem 0 sicher hinzugefügt werden kann (Hurra!), aber Sie sollten diesen Zeiger nicht dereferenzieren.
&x[0]
für leere Strings hat dies ein undefiniertes Verhalten (21.3.4)
zB gegeben f(const char* p, size_t n) { if (n == 0) return; ...whatever... }darf man nicht anrufen f(&x[0], x.size());wann x.empty()- einfach benutzen f(x.data(), ...).
ansonsten wie folgt x.data()aber:
für nicht constxergibt dies einen Nichtzeiger constchar*; Sie können den Inhalt von Zeichenfolgen überschreiben
x.c_str()
kehrt const char*zu einer ASCIIZ-Darstellung (NUL-terminiert) des Werts zurück (dh ['h', 'e', 'l', 'l', 'o', '\ 0']).
obwohl nur wenige , wenn irgendwelche Implementierungen dies wünschen, die C ++ 03 Standard - Wortlaut wurde der String Umsetzung die Freiheit zu ermöglichen , eine zu schaffen verschiedene NUL-terminierten Puffer im Fluge , von der potenziell nicht-NUL beendet „ausgesetzt“ Puffer durch x.data()und&x[0]
x.size() + 1 Zeichen sind sicher zu lesen.
garantiert sicher auch für leere Strings (['\ 0']).
Folgen des Zugriffs auf externe Rechtsindizes
Unabhängig davon, wie Sie einen Zeiger erhalten, dürfen Sie nicht weiter vom Zeiger entfernt auf den Speicher zugreifen als auf die in den obigen Beschreibungen garantierten Zeichen. Versuche, dies zu tun, haben ein undefiniertes Verhalten , mit einer sehr realen Wahrscheinlichkeit von Anwendungsabstürzen und Müllergebnissen, selbst bei Lesevorgängen, und zusätzlich Großhandelsdaten, Stapelbeschädigungen und / oder Sicherheitslücken für Schreibvorgänge.
Wann werden diese Zeiger ungültig?
Wenn Sie eine Elementfunktion aufrufen string, die die stringKapazität ändert oder weitere Kapazität reserviert, werden alle zuvor von einer der oben genannten Methoden zurückgegebenen Zeigerwerte ungültig . Sie können diese Methoden erneut verwenden, um einen weiteren Zeiger abzurufen. (Die Regeln sind die gleichen wie für Iteratoren in strings).
Siehe auch So erhalten Sie einen gültigen Zeichenzeiger, auch wenn er den Gültigkeitsbereich xverlässt oder weiter unten geändert wird ....
Also, was ist besser zu verwenden?
Verwenden .c_str()Sie ab C ++ 11 ASCIIZ-Daten und .data()"binäre" Daten (weiter unten erläutert).
Verwenden Sie in C ++ 03, .c_str()sofern dies nicht .data()ausreichend ist, und ziehen .data()Sie es vor, &x[0]da es für leere Zeichenfolgen sicher ist.
... versuchen Sie, das Programm so gut zu verstehen, data()dass es gegebenenfalls verwendet werden kann, oder Sie werden wahrscheinlich andere Fehler machen ...
Das von garantierte ASCII-NUL-Zeichen '\ 0' .c_str()wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und für den Zugriff sicherer Daten angibt. Dies gilt sowohl für C ++ - nur Funktionen wie say fstream::fstream(const char* filename, ...)und Funktionen, die mit C geteilt werden, wie strchr(), und printf().
Angesichts der Tatsache .c_str(), dass die Garantien von C ++ 03 für den zurückgegebenen Puffer sehr hoch sind .data(), können Sie sie immer sicher verwenden .c_str(), aber manchmal nicht, weil:
Die Verwendung von .data()kommuniziert mit anderen Programmierern, die den Quellcode lesen, dass die Daten nicht ASCIIZ sind (stattdessen verwenden Sie die Zeichenfolge zum Speichern eines Datenblocks (der manchmal nicht einmal wirklich textuell ist)) oder an den Sie ihn weitergeben eine weitere Funktion, die es als Block von "binären" Daten behandelt. Dies kann eine wichtige Erkenntnis sein, um sicherzustellen, dass die Codeänderungen anderer Programmierer die Daten weiterhin ordnungsgemäß verarbeiten.
Nur C ++ 03: Es besteht eine geringe Wahrscheinlichkeit, dass Ihre stringImplementierung zusätzliche Speicherzuweisung und / oder Datenkopie durchführen muss, um den NUL-terminierten Puffer vorzubereiten
Als weiterer Hinweis: Wenn die Parameter einer Funktion das ( const) erfordern, char*aber nicht darauf bestehen, dass sie abgerufen werden x.size(), benötigt die Funktion wahrscheinlich eine ASCIIZ-Eingabe. Dies .c_str()ist eine gute Wahl (die Funktion muss wissen, wo der Text irgendwie endet, wenn dies nicht der Fall ist Als separater Parameter kann es sich nur um eine Konvention wie ein Längenpräfix oder einen Sentinel oder eine feste erwartete Länge handeln.
So erhalten Sie einen Zeichenzeiger, der auch dann gültig ist, wenn er den Gültigkeitsbereich xverlässt oder weiter geändert wird
Sie müssen den Inhalt von in einen neuen Speicherbereich außerhalb kopieren . Dieser externe Puffer kann sich an vielen Stellen befinden, z. B. in einer anderen oder einer Zeichenarray-Variablen. Er kann eine andere Lebensdauer haben oder nicht als aufgrund eines anderen Bereichs (z. B. Namespace, global, statisch, Heap, gemeinsam genutzter Speicher, Speicherzuordnungsdatei). .stringxxstringx
So kopieren Sie den Text std::string xin ein unabhängiges Zeichenarray:
// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;// - old_x will not be affected by subsequent modifications to x...// - you can use `&old_x[0]` to get a writable char* to old_x's textual content// - you can use resize() to reduce/expand the string// - resizing isn't possible from within a function passed only the char* address
std::string old_x = x.c_str();// old_x will terminate early if x embeds NUL// Copies ASCIIZ data but could be less efficient as it needs to scan memory to// find the NUL terminator indicating string length before allocating that amount// of memory to copy into, or more efficient if it ends up allocating/copying a// lot less content.// Example, x == "ab\0cd" -> old_x == "ab".// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data()+ x.size());// without the NUL
std::vector<char> old_x(x.c_str(), x.c_str()+ x.size()+1);// with the NUL// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)char y[N +1];
strcpy(y, x.c_str());// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)char y[N +1];
strncpy(y, x.c_str(), N);// copy at most N, zero-padding if shorter
y[N]='\0';// ensure NUL terminated// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTHchar* y = alloca(x.size()+1);
strcpy(y, x.c_str());// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)char y[x.size()+1];
strcpy(y, x.c_str());// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETYchar* y =newchar[x.size()+1];
strcpy(y, x.c_str());// or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());// use y...delete[] y;// make sure no break, return, throw or branching bypasses this// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE// see boost shared_array usage in Johannes Schaub's answer// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETYchar* y = strdup(x.c_str());// use y...
free(y);
Andere Gründe, um ein zu wollen char*oder const char*aus einem generiertstring
Oben haben Sie gesehen, wie Sie ein ( const) erhalten char*und wie Sie eine Kopie des Textes unabhängig vom Original stringerstellen. Aber was können Sie damit tun ? Ein paar Beispiele ...
Geben Sie "C" -Code Zugriff auf den stringText von C ++ , wie inprintf("x is '%s'", x.c_str());
Kopieren Sie xden Text in einen Puffer, der vom Aufrufer Ihrer Funktion angegeben wurde (z. B. strncpy(callers_buffer, callers_buffer_size, x.c_str())), oder in einen flüchtigen Speicher, der für Geräte-E / A verwendet wird (z. B. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;).
Hängen Sie xden Text an ein Zeichenarray an, das bereits ASCIIZ-Text enthält (z. B. strcat(other_buffer, x.c_str())). Achten Sie darauf, den Puffer nicht zu überlaufen (in vielen Situationen müssen Sie ihn möglicherweise verwenden strncat).
eine const char*oder char*von einer Funktion zurückgeben (möglicherweise aus historischen Gründen - der Client verwendet Ihre vorhandene API - oder aus C-Kompatibilität möchten Sie keine zurückgeben std::string, aber Ihre stringDaten für den Anrufer irgendwo kopieren )
Achten Sie darauf, keinen Zeiger zurückzugeben, der vom Aufrufer möglicherweise dereferenziert wird, nachdem eine lokale stringVariable, auf die dieser Zeiger zeigt, den Gültigkeitsbereich verlassen hat
Einige Projekte mit gemeinsam genutzten Objekten, die für verschiedene std::stringImplementierungen kompiliert / verknüpft wurden (z. B. STLport und Compiler-native), übergeben möglicherweise Daten als ASCIIZ, um Konflikte zu vermeiden
Schön. Ein weiterer Grund, ein char * (non const) zu wollen, ist der Betrieb mit MPI-Broadcast. Es sieht besser aus, wenn Sie nicht hin und her kopieren müssen. Ich hätte persönlich einen char * const getter zum String angeboten. Const-Zeiger, aber bearbeitbarer String. Obwohl es möglicherweise mit der impliziten Konvertierung von const char * zu string zu
tun hat
33
Verwenden Sie die .c_str()Methode für const char *.
Sie können &mystring[0]einen char *Zeiger abrufen, aber es gibt einige Fallstricke: Sie erhalten nicht unbedingt eine nullterminierte Zeichenfolge, und Sie können die Größe der Zeichenfolge nicht ändern. Sie müssen besonders darauf achten, keine Zeichen nach dem Ende der Zeichenfolge hinzuzufügen, da sonst ein Pufferüberlauf (und ein wahrscheinlicher Absturz) auftritt.
Beachten Sie, dass viele stringElementfunktionen den internen Puffer neu zuordnen und alle möglicherweise gespeicherten Zeiger ungültig machen. Am besten sofort verwenden und dann wegwerfen.
Sie sollten beachten, dass data () const char * :) zurückgibt. Was Sie meinen, ist & str [0], das eine zusammenhängende, aber nicht notwendige nullterminierte Zeichenfolge zurückgibt.
Johannes Schaub - litb
1
@ Litb, Argh! Das bekomme ich, wenn ich versuche, eine schnelle Antwort zu finden. Ich habe Ihre Lösung in der Vergangenheit verwendet und weiß nicht, warum es nicht das erste war, was mir in den Sinn kam. Ich habe meine Antwort bearbeitet.
Mark Ransom
2
Technisch gesehen ist der Speicher von std :: string nur in C ++ 0x zusammenhängend.
MSalters
1
@ MSalters, danke - das wusste ich nicht. Es würde mir jedoch schwer fallen, eine Implementierung zu finden, bei der dies jedoch nicht der Fall war.
C ++ 17 (kommender Standard) ändert die Inhaltsangabe der Vorlage und basic_stringfügt eine nicht konstante Überladung von data():
charT* data() noexcept;
Rückgabe: Ein Zeiger p, so dass p + i == & Operator für jedes i in [0, size ()].
CharT const * von std::basic_string<CharT>
std::string const cstr ={"..."};charconst* p = cstr.data();// or .c_str()
CharT * von std::basic_string<CharT>
std::string str ={"..."};char* p = str.data();
C ++ 11
CharT const * von std::basic_string<CharT>
std::string str ={"..."};
str.c_str();
CharT * von std::basic_string<CharT>
Ab C ++ 11 heißt es im Standard:
Die char-ähnlichen Objekte in einem basic_stringObjekt sind zusammenhängend zu speichern. Das heißt, für jedes basic_stringObjekt sgilt die Identität &*(s.begin() + n) == &*s.begin() + nfür alle Werte n, die 0 <= n < s.size().
Rückgabe: *(begin() + pos)if pos < size(), andernfalls ein Verweis auf ein Objekt vom Typ CharTmit Wert CharT(); Der angegebene Wert darf nicht geändert werden.
void aFunctionAPI(char* input);// other stuff
aFunctionAPI("Foo");//this call is not safe. if the function modified the //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str());//this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str()));//this is not safe std::string //implement reference counting and //it may change the value of other//strings as well.DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str());//this is fine
Ich habe die Klasse aufgerufen, DeepStringweil sie eine tiefe und eindeutige Kopie (die DeepStringnicht kopierbar ist) einer vorhandenen Zeichenfolge erstellt.
Ich würde diese Namenskonvention vermeiden. c_str()wie von verwendet stdwird eine Abkürzung für "C-String" nicht "const String" und gibt str()immer ein std::basic_string, nicht char*(zum Beispiel std::stringstream::str())
bcrist
8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
sieht schick aus, ist aber schwer zu verstehen ... Einfach ist die beste IMO
Naeem A. Malik
4
strcpy (), malloc (), length () und c_str () sind Grundfunktionen, und daran ist nichts Schwieriges. Nur Speicher zuweisen und kopieren.
Cegprakash
5
Ja, die Funktionen sind grundlegend, aber Sie haben sie verdreht und gebogen, um wie eine Schüssel Spaghetti oder ein Liner Frankensteins Monster auszusehen :)
Naeem A. Malik
4
Ja, die Funktionen sind grundlegend, aber ... haben Sie sich erinnert, als Sie angefangen haben, sich mit einer Programmiersprache zu beschäftigen? Einige Zeilen mehr zu erklären und es wird einem Neuling wirklich helfen zu lernen, warum zum Beispiel anders oder besser als diese Antwort ist :)
Hastur
2
@cegprakash: Immer wenn es ein malloc () gibt, muss es auch ein freies () geben. Andernfalls verliert der Code Speicher, ebenso wie die Lösung in Ihrer Antwort. Das Zuweisen von Speicher, ohne zumindest auf die erforderliche Freigabe hinzuweisen, ist für solche Fragen eine schlechte Praxis.
Hallo, was Sie gepostet haben, wurde bereits mehrfach mit weiteren Details in anderen Antworten auf die 5 Jahre alte Frage gesagt. Es ist in Ordnung, ältere Fragen zu beantworten, aber nur, wenn Sie neue Informationen hinzufügen. Ansonsten ist es nur Lärm.
strcpy
undmalloc
sind nicht wirklich die C ++ Art.char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)
wäre aber idiomatischer C ++.strcpy()
undmalloc()
sind nicht falsch oder problematisch, aber es scheint inkonsistent, eine C ++ - Zeichenfolge und C-Bibliotheksfunktionen mit C ++ - Entsprechungen im selben Codeblock zu verwenden.Antworten:
Wenn Sie a nur
std::string
an eine Funktion übergeben möchten, dieconst char*
Sie benötigen , können Sie diese verwendenWenn Sie eine beschreibbare Kopie erhalten möchten
char *
, können Sie dies folgendermaßen tun:Bearbeiten : Beachten Sie, dass das oben Genannte nicht ausnahmesicher ist. Wenn zwischen dem
new
Anruf und demdelete
Anruf etwas ausgelöst wird, geht Speicher verloren, da nichtsdelete
automatisch nach Ihnen ruft . Es gibt zwei unmittelbare Möglichkeiten, dies zu lösen.boost :: scoped_array
boost::scoped_array
löscht den Speicher für Sie, wenn Sie den Gültigkeitsbereich verlassen:std :: vector
Dies ist der Standardweg (erfordert keine externe Bibliothek). Sie verwenden
std::vector
, wodurch der Speicher für Sie vollständig verwaltet wird.quelle
std::string::data()
jetzt aCharT*
anstelle von a zurückgegebenconst CharT*
. Es könnte eine gute Idee sein, diese Antwort zu aktualisieren :)Gegeben sagen ...
Abrufen eines "char *" oder "const char *" aus einem "String"
So erhalten Sie einen Zeichenzeiger, der gültig ist, während er
x
im Gültigkeitsbereich bleibt und nicht weiter geändert wirdC ++ 11 vereinfacht die Dinge; Die folgenden Optionen bieten Zugriff auf denselben internen Zeichenfolgenpuffer:
Alle oben genannten Zeiger enthalten denselben Wert - die Adresse des ersten Zeichens im Puffer. Sogar eine leere Zeichenfolge hat ein "erstes Zeichen im Puffer", da C ++ 11 garantiert, dass nach dem explizit zugewiesenen Zeichenfolgeninhalt immer ein zusätzliches NUL / 0-Abschlusszeichen beibehalten wird (z. B.
std::string("this\0that", 9)
wird ein Puffer gespeichert"this\0that\0"
).Angesichts eines der oben genannten Hinweise:
Nur für den nicht -
const
Zeigerp_writable_data
und aus&x[0]
:Das Schreiben eines NUL an einer anderen Stelle in der Zeichenfolge ändert nichts an den
string
'ssize()
;string
's dürfen eine beliebige Anzahl von NULs enthalten - sie werden von nicht speziell behandeltstd::string
(wie in C ++ 03).In C ++ 03 waren die Dinge erheblich komplizierter (wichtige Unterschiede hervorgehoben ):
x.data()
const char*
zum internen Puffer der Zeichenfolge zurück , der vom Standard nicht benötigt wurde, um mit einem NUL abzuschließen (dh es können nicht['h', 'e', 'l', 'l', 'o']
initialisierte oder Garbage-Werte folgen, wobei versehentliche Zugriffe darauf ein undefiniertes Verhalten aufweisen ).x.size()
Zeichen sind sicher zu lesen, dhx[0]
durchx[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
darf man nicht anrufenf(&x[0], x.size());
wannx.empty()
- einfach benutzenf(x.data(), ...)
.x.data()
aber:const
x
ergibt dies einen Nichtzeigerconst
char*
; Sie können den Inhalt von Zeichenfolgen überschreibenx.c_str()
const char*
zu einer ASCIIZ-Darstellung (NUL-terminiert) des Werts zurück (dh ['h', 'e', 'l', 'l', 'o', '\ 0']).x.data()
und&x[0]
x.size()
+ 1 Zeichen sind sicher zu lesen.Folgen des Zugriffs auf externe Rechtsindizes
Unabhängig davon, wie Sie einen Zeiger erhalten, dürfen Sie nicht weiter vom Zeiger entfernt auf den Speicher zugreifen als auf die in den obigen Beschreibungen garantierten Zeichen. Versuche, dies zu tun, haben ein undefiniertes Verhalten , mit einer sehr realen Wahrscheinlichkeit von Anwendungsabstürzen und Müllergebnissen, selbst bei Lesevorgängen, und zusätzlich Großhandelsdaten, Stapelbeschädigungen und / oder Sicherheitslücken für Schreibvorgänge.
Wann werden diese Zeiger ungültig?
Wenn Sie eine Elementfunktion aufrufen
string
, die diestring
Kapazität ändert oder weitere Kapazität reserviert, werden alle zuvor von einer der oben genannten Methoden zurückgegebenen Zeigerwerte ungültig . Sie können diese Methoden erneut verwenden, um einen weiteren Zeiger abzurufen. (Die Regeln sind die gleichen wie für Iteratoren instring
s).Siehe auch So erhalten Sie einen gültigen Zeichenzeiger, auch wenn er den Gültigkeitsbereich
x
verlässt oder weiter unten geändert wird ....Also, was ist besser zu verwenden?
Verwenden
.c_str()
Sie ab C ++ 11 ASCIIZ-Daten und.data()
"binäre" Daten (weiter unten erläutert).Verwenden Sie in C ++ 03,
.c_str()
sofern dies nicht.data()
ausreichend ist, und ziehen.data()
Sie es vor,&x[0]
da es für leere Zeichenfolgen sicher ist.... versuchen Sie, das Programm so gut zu verstehen,
data()
dass es gegebenenfalls verwendet werden kann, oder Sie werden wahrscheinlich andere Fehler machen ...Das von garantierte ASCII-NUL-Zeichen '\ 0'
.c_str()
wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und für den Zugriff sicherer Daten angibt. Dies gilt sowohl für C ++ - nur Funktionen wie sayfstream::fstream(const char* filename, ...)
und Funktionen, die mit C geteilt werden, wiestrchr()
, undprintf()
.Angesichts der Tatsache
.c_str()
, dass die Garantien von C ++ 03 für den zurückgegebenen Puffer sehr hoch sind.data()
, können Sie sie immer sicher verwenden.c_str()
, aber manchmal nicht, weil:.data()
kommuniziert mit anderen Programmierern, die den Quellcode lesen, dass die Daten nicht ASCIIZ sind (stattdessen verwenden Sie die Zeichenfolge zum Speichern eines Datenblocks (der manchmal nicht einmal wirklich textuell ist)) oder an den Sie ihn weitergeben eine weitere Funktion, die es als Block von "binären" Daten behandelt. Dies kann eine wichtige Erkenntnis sein, um sicherzustellen, dass die Codeänderungen anderer Programmierer die Daten weiterhin ordnungsgemäß verarbeiten.string
Implementierung zusätzliche Speicherzuweisung und / oder Datenkopie durchführen muss, um den NUL-terminierten Puffer vorzubereitenAls weiterer Hinweis: Wenn die Parameter einer Funktion das (
const
) erfordern,char*
aber nicht darauf bestehen, dass sie abgerufen werdenx.size()
, benötigt die Funktion wahrscheinlich eine ASCIIZ-Eingabe. Dies.c_str()
ist eine gute Wahl (die Funktion muss wissen, wo der Text irgendwie endet, wenn dies nicht der Fall ist Als separater Parameter kann es sich nur um eine Konvention wie ein Längenpräfix oder einen Sentinel oder eine feste erwartete Länge handeln.So erhalten Sie einen Zeichenzeiger, der auch dann gültig ist, wenn er den Gültigkeitsbereich
x
verlässt oder weiter geändert wirdSie müssen den Inhalt von in einen neuen Speicherbereich außerhalb kopieren . Dieser externe Puffer kann sich an vielen Stellen befinden, z. B. in einer anderen oder einer Zeichenarray-Variablen. Er kann eine andere Lebensdauer haben oder nicht als aufgrund eines anderen Bereichs (z. B. Namespace, global, statisch, Heap, gemeinsam genutzter Speicher, Speicherzuordnungsdatei). .
string
x
x
string
x
So kopieren Sie den Text
std::string x
in ein unabhängiges Zeichenarray:Andere Gründe, um ein zu wollen
char*
oderconst char*
aus einem generiertstring
Oben haben Sie gesehen, wie Sie ein (
const
) erhaltenchar*
und wie Sie eine Kopie des Textes unabhängig vom Originalstring
erstellen. Aber was können Sie damit tun ? Ein paar Beispiele ...string
Text von C ++ , wie inprintf("x is '%s'", x.c_str());
x
den Text in einen Puffer, der vom Aufrufer Ihrer Funktion angegeben wurde (z. B.strncpy(callers_buffer, callers_buffer_size, x.c_str())
), oder in einen flüchtigen Speicher, der für Geräte-E / A verwendet wird (z. B.for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
).x
den Text an ein Zeichenarray an, das bereits ASCIIZ-Text enthält (z. B.strcat(other_buffer, x.c_str())
). Achten Sie darauf, den Puffer nicht zu überlaufen (in vielen Situationen müssen Sie ihn möglicherweise verwendenstrncat
).const char*
oderchar*
von einer Funktion zurückgeben (möglicherweise aus historischen Gründen - der Client verwendet Ihre vorhandene API - oder aus C-Kompatibilität möchten Sie keine zurückgebenstd::string
, aber Ihrestring
Daten für den Anrufer irgendwo kopieren )string
Variable, auf die dieser Zeiger zeigt, den Gültigkeitsbereich verlassen hatstd::string
Implementierungen kompiliert / verknüpft wurden (z. B. STLport und Compiler-native), übergeben möglicherweise Daten als ASCIIZ, um Konflikte zu vermeidenquelle
Verwenden Sie die
.c_str()
Methode fürconst char *
.Sie können
&mystring[0]
einenchar *
Zeiger abrufen, aber es gibt einige Fallstricke: Sie erhalten nicht unbedingt eine nullterminierte Zeichenfolge, und Sie können die Größe der Zeichenfolge nicht ändern. Sie müssen besonders darauf achten, keine Zeichen nach dem Ende der Zeichenfolge hinzuzufügen, da sonst ein Pufferüberlauf (und ein wahrscheinlicher Absturz) auftritt.Es gab keine Garantie dafür, dass alle Zeichen bis C ++ 11 Teil desselben zusammenhängenden Puffers waren, aber in der Praxis
std::string
funktionierten alle bekannten Implementierungen trotzdem so. Siehe Zeigt "& s [0]" auf zusammenhängende Zeichen in einem std :: string? .Beachten Sie, dass viele
string
Elementfunktionen den internen Puffer neu zuordnen und alle möglicherweise gespeicherten Zeiger ungültig machen. Am besten sofort verwenden und dann wegwerfen.quelle
C ++ 17
C ++ 17 (kommender Standard) ändert die Inhaltsangabe der Vorlage und
basic_string
fügt eine nicht konstante Überladung vondata()
:CharT const *
vonstd::basic_string<CharT>
CharT *
vonstd::basic_string<CharT>
C ++ 11
CharT const *
vonstd::basic_string<CharT>
CharT *
vonstd::basic_string<CharT>
Ab C ++ 11 heißt es im Standard:
Es gibt verschiedene Möglichkeiten, einen nicht konstanten Zeichenzeiger zu erhalten.
1. Verwenden Sie den zusammenhängenden Speicher von C ++ 11
Profi
Nachteile
'\0'
darf nicht geändert werden / ist nicht unbedingt Teil des nicht konstanten Speichers.2. Verwenden Sie
std::vector<CharT>
Profi
Nachteile
3. Verwenden Sie
std::array<CharT, N>
ifN
, wenn die Kompilierungszeit konstant ist (und klein genug).Profi
Nachteile
4. Rohspeicherzuordnung mit automatischer Speicherlöschung
Profi
Nachteile
5. Rohspeicherzuordnung mit manueller Handhabung
Profi
Con
quelle
Ich arbeite mit einer API mit vielen Funktionen als Eingabe a
char*
.Ich habe eine kleine Klasse erstellt, um diese Art von Problem zu lösen. Ich habe die RAII-Sprache implementiert.
Und Sie können es verwenden als:
Ich habe die Klasse aufgerufen,
DeepString
weil sie eine tiefe und eindeutige Kopie (dieDeepString
nicht kopierbar ist) einer vorhandenen Zeichenfolge erstellt.quelle
c_str()
wie von verwendetstd
wird eine Abkürzung für "C-String" nicht "const String" und gibtstr()
immer einstd::basic_string
, nichtchar*
(zum Beispielstd::stringstream::str()
)quelle
Sehen Sie sich das an:
Beachten Sie jedoch, dass dies a zurückgibt
const char *
.char *
Verwendenstrcpy
Sie für a , um es in ein andereschar
Array zu kopieren .quelle
Versuche dies
quelle