Wie kann ich einen std::string
in einen char*
oder ein const char*
umwandeln?
Einfach char* result = strdup(str.c_str()); verwenden.
Wie kann ich einen std::string
in einen char*
oder ein const char*
umwandeln?
Wenn Sie einfach nur ein std::string
an eine Funktion übergeben möchten, die const char *
benötigt, können Sie .c_str()
verwenden:
std::string str;
const char * c = str.c_str();
Und wenn Sie ein nicht-const char *
benötigen, rufen Sie .data()
auf:
std::string str;
char * c = str.data();
.data()
wurde in C++17 hinzugefügt. Vorher können Sie &str[0]
verwenden.
Beachten Sie, dass wenn der std::string
const
ist, .data()
stattdessen const char *
zurückgibt, ähnlich wie .c_str()
.
Der Zeiger wird ungültig, wenn der String zerstört wird oder Speicher neu zugewiesen wird.
Der Zeiger zeigt auf einen nullterminierten String, und der Terminator zählt nicht gegen str.size()
. Sie dürfen keinen Nicht-Null-Zeichen dem Terminator zuweisen.
Du könntest es, aber strdup ist keine Standardfunktion in C oder C++, es stammt von POSIX :)
Was ich wahrscheinlich im Allgemeinen bevorzugen würde, ist std :: vector beschreibbar (str.begin(), str.end()); beschreibbar.push_back ('\ 0'); char * c = & beschreibbar [0];
Gesetzt den Fall...
std::string x = "hello";
Wie erhält man einen Zeichenzeiger, der gültig ist, während x
bleibt im Geltungsbereich und wird nicht weiter geändert
C++11 vereinfacht die Dinge; die folgenden geben alle Zugriff auf denselben internen String-Puffer:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Alle oben genannten Zeiger enthalten die gleicher Wert - die Adresse des ersten Zeichens im Puffer. Auch eine leere Zeichenkette hat ein "erstes Zeichen im Puffer", da C++11 garantiert, dass nach dem explizit zugewiesenen Zeichenketteninhalt immer ein zusätzliches NUL/0-Terminatorzeichen folgt (z.B. std::string("this\0that", 9)
hat einen Puffer, der "this\0that\0"
).
Wenn einer der oben genannten Hinweise zutrifft:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Nur für die nicht const
Zeiger p_writable_data
und von &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 der Zeichenkette bewirkt no ändern Sie die string
's size()
; string
dürfen eine beliebige Anzahl von NULs enthalten - sie werden nicht besonders behandelt von std::string
(wie in C++03).
Sur C++03 waren die Dinge wesentlich komplizierter (wesentliche Unterschiede hervorgehoben ):
x.data()
const char*
in den internen Puffer des Strings die nach dem Standard nicht mit einem NUL abzuschließen war (d.h. könnte sein ['h', 'e', 'l', 'l', 'o']
gefolgt von uninitialisierten Werten oder Garbage-Werten, auf die versehentlich zugegriffen werden kann undefiniertes Verhalten ).
x.size()
Zeichen sicher zu lesen sind, d.h. x[0]
über x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
Sie dürfen nicht anrufen f(&x[0], x.size());
wenn x.empty()
- verwenden Sie einfach f(x.data(), ...)
.x.data()
sondern:
const
x
ergibt sich ein Nicht const
char*
Zeiger; Sie können den Inhalt der Zeichenkette überschreibenx.c_str()
const char*
in eine ASCIIZ-Darstellung (NUL-terminiert) des Wertes (d. h. ['h', 'e', 'l', 'l', 'o', ' \0 ']).x.data()
y &x[0]
x.size()
+ 1 Zeichen sind sicher zu lesen.Unabhängig davon, wie Sie einen Zeiger erhalten, dürfen Sie nicht auf Speicher zugreifen, der weiter vom Zeiger entfernt ist als die Zeichen, die in den obigen Beschreibungen garantiert sind. Versuche, dies zu tun, haben undefiniertes Verhalten Dies birgt die Gefahr, dass die Anwendung abstürzt und selbst bei Lesezugriffen Daten im Müll landen, während bei Schreibzugriffen zusätzlich große Datenmengen, beschädigte Stacks und/oder Sicherheitslücken auftreten können.
Wenn Sie einige string
Mitgliedsfunktion, die die string
oder weitere Kapazitäten reserviert, werden alle Zeigerwerte, die zuvor von einer der oben genannten Methoden zurückgegeben wurden ungültig gemacht . Sie können diese Methoden erneut verwenden, um einen anderen Zeiger zu erhalten. (Die Regeln sind dieselben wie für Iteratoren in string
s).
Siehe auch Wie erhält man einen Zeichenzeiger auch nach x
den Geltungsbereich verlässt oder weiter verändert wird unten....
Ab C++11, verwenden Sie .c_str()
für ASCIIZ-Daten, und .data()
für "binäre" Daten (weiter unten erklärt).
In C++03, verwenden Sie .c_str()
wenn nicht sicher ist, dass .data()
angemessen ist, und bevorzugen .data()
über &x[0]
da es für leere Strings sicher ist....
...versuchen Sie, das Programm so weit zu verstehen, dass Sie es benutzen können data()
wenn es angebracht ist, oder Sie werden wahrscheinlich andere Fehler machen...
Das ASCII-NUL ' \0 ' Zeichen garantiert durch .c_str()
wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und zugriffssicherer Daten anzeigt. Dies gilt sowohl für reine C++-Funktionen wie say fstream::fstream(const char* filename, ...)
und shared-with-C-Funktionen wie strchr()
et printf()
.
Angesichts der C++03's .c_str()
Die Garantien für den zurückgegebenen Puffer sind eine Obermenge von .data()
können Sie immer sicher verwenden .c_str()
aber die Leute tun das manchmal nicht, weil:
.data()
teilt anderen Programmierern, die den Quellcode lesen, mit, dass es sich bei den Daten nicht um ASCIIZ handelt (vielmehr verwenden Sie die Zeichenkette, um einen Datenblock zu speichern (der manchmal nicht einmal wirklich aus Text besteht)), oder dass Sie die Zeichenkette an eine andere Funktion weitergeben, die sie als einen Block "binärer" Daten behandelt. Dies kann eine entscheidende Erkenntnis sein, um sicherzustellen, dass die Codeänderungen anderer Programmierer die Daten weiterhin richtig behandeln.string
Die Implementierung wird einige zusätzliche Speicherzuweisungen und/oder Datenkopien vornehmen müssen, um den mit NUL beendeten Puffer vorzubereiten.Ein weiterer Hinweis: Wenn die Parameter einer Funktion das ( const
) char*
aber bestehen Sie nicht darauf, dass Sie x.size()
die Funktion wahrscheinlich benötigt eine ASCIIZ-Eingabe, also .c_str()
ist eine gute Wahl (die Funktion muss irgendwie wissen, wo der Text endet, wenn es also kein separater Parameter ist, kann es nur eine Konvention wie ein Längen-Präfix oder Sentinel oder eine feste erwartete Länge sein).
x
den Geltungsbereich verlässt oder weiter verändert wirdSie müssen kopieren. den Inhalt des string
x
in einen neuen Speicherbereich außerhalb x
. Dieser externe Puffer kann sich an vielen Orten befinden, z. B. an einem anderen string
oder eine Zeichenfeldvariable, kann sie eine andere Lebensdauer haben als x
weil sie sich in einem anderen Bereich befinden (z. B. Namespace, global, statisch, Heap, Shared Memory, Memory-Mapped-Datei).
So kopieren Sie den Text aus std::string x
in ein unabhängiges Zeichenfeld:
// 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) LENGTH
char* 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 SAFETY
char* y = new char[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 SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);
char*
o const char*
generiert aus einer string
Oben haben Sie also gesehen, wie man eine ( const
) char*
und wie man eine Kopie des Textes unabhängig vom Original erstellt string
aber was können Sie faire mit ihm? Ein paar zufällige Beispiele...
string
Text, wie in printf("x is '%s'", x.c_str());
x
in einen vom Aufrufer der Funktion angegebenen Puffer (z. B. strncpy(callers_buffer, callers_buffer_size, x.c_str())
), oder flüchtiger 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
in ein Zeichenfeld, das bereits einen ASCIIZ-Text enthält (z. B. strcat(other_buffer, x.c_str())
) - achten Sie darauf, dass der Puffer nicht überläuft (in vielen Situationen müssen Sie die strncat
)const char*
o char*
aus einer Funktion heraus (vielleicht aus historischen Gründen - der Client verwendet Ihre bestehende API - oder aus Gründen der C-Kompatibilität wollen Sie nicht eine std::string
aber Sie möchten Ihre string
Daten irgendwo für den Anrufer)
string
die Variable, auf die dieser Zeiger zeigte, den Anwendungsbereich verlassen hatstd::string
Implementierungen (z.B. STLport und Compiler-native) können Daten als ASCIIZ übergeben, um Konflikte zu vermeiden
Schön. Ein weiterer Grund, warum man einen char* (nicht const) haben möchte, ist die Verwendung von MPI-Broadcast. Es sieht besser aus, wenn man nicht ständig hin und her kopieren muss. Ich persönlich hätte einen char* const Getter für string angeboten. Konstanter Zeiger, aber editierbarer String. Obwohl es eventuell die implizite Konvertierung von const char* zu string beeinträchtigt hätte...
Verwenden Sie die Methode .c_str()
für const char *
.
Sie können &mystring[0]
verwenden, um einen char *
Zeiger zu erhalten, aber es gibt ein paar Dinge, auf die Sie achten müssen: 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 über das Ende der Zeichenfolge hinzuzufügen, da sonst ein Pufferüberlauf (und wahrscheinlicher Absturz) auftreten kann.
Bis C++11 gab es keine Garantie dafür, dass alle Zeichen Teil des gleichen zusammenhängenden Puffers sind, aber in der Praxis arbeiteten jedoch alle bekannten Implementierungen von std::string
sowieso auf diese Weise; siehe Zeigt "&s[0]" auf zusammenhängende Zeichen in einem std::string?.
Beachten Sie, dass viele string
Memberfunktionen den internen Puffer neu zuweisen und alle Zeiger ungültig machen können, die Sie möglicherweise gespeichert haben. Am besten verwenden Sie sie sofort und verwerfen sie dann.
Du solltest beachten, dass data() const char* zurückgibt :) Was du meinst ist &str[0], das einen zusammenhängenden, aber nicht unbedingt nullterminierten String zurückgibt.
@litb, Argh! Das ist das, was ich bekomme, wenn ich versuche, schnell eine Antwort zu erstellen. Ich habe deine Lösung in der Vergangenheit verwendet, weiß nicht, warum sie mir nicht als Erstes eingefallen ist. Ich habe meine Antwort bearbeitet.
C++17 (nächstes Standard) ändert die Übersicht des Templates basic_string
und fügt eine nicht-const Überladung von data()
hinzu:
charT* data() noexcept;
Gibt zurück: Ein Zeiger p, sodass p + i == &operator für jedes i in [0, size()].
CharT const *
aus std::basic_string
std::string const cstr = { "..." };
char const * p = cstr.data(); // oder .c_str()
CharT *
aus std::basic_string
std::string str = { "..." };
char * p = str.data();
CharT const *
aus std::basic_string
std::string str = { "..." };
str.c_str();
CharT *
aus std::basic_string
Ab C++11 besagt der Standard:
- Die char-ähnlichen Objekte in einem
basic_string
-Objekt sollen kontinuierlich gespeichert werden. Das heißt, für jedesbasic_string
-Objekts
soll die Identität&*(s.begin() + n) == &*s.begin() + n
für alle Werte vonn
, sodass0 <= n < s.size()
, gelten.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Gibt zurück:
*(begin() + pos)
, wennpos < size()
, ansonsten eine Referenz auf ein Objekt des TypsCharT
mit dem WertCharT()
; der referenzierte Wert darf nicht verändert werden.
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Gibt zurück: Ein Zeiger p, sodass
p + i == &operator[](i)
für jedesi
in[0, size()]
.
Es gibt mehrere Möglichkeiten, um einen nicht-const Zeiger auf Zeichen zu erhalten.
std::string foo{"text"};
auto p = &*foo.begin();
Pro
Contra
'\0'
darf nicht geändert werden / ist nicht unbedingt Teil des nicht-const Speichers.std::vector
std::string foo{"text"};
std::vector fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
Contra
std::array
, wenn N
zur Kompilierzeit konstant (und klein genug) iststd::string foo{"text"};
std::array fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
Pro
Contra
std::string foo{ "text" };
auto p = std::make_unique(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
Pro
Contra
std::string foo{ "text" };
char * p = nullptr;
try
{
p = new char[foo.size() + 1u];
std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
// etwas mit p machen
delete[] p;
}
catch (...)
{
if (p) { delete[] p; }
throw;
}
Pro
Contra
Ich arbeite mit einer API, die viele Funktionen verwendet, die ein char*
als Eingabe erhalten.
Ich habe eine kleine Klasse erstellt, um mit dieser Art von Problem umzugehen, und ich habe das RAII-Idiom implementiert.
class DeepString
{
DeepString(const DeepString& other);
DeepString& operator=(const DeepString& other);
char* internal_;
public:
explicit DeepString(const std::string& toCopy):
internal_(new char[toCopy.size()+1])
{
strcpy(internal_,toCopy.c_str());
}
~DeepString() { delete[] internal_; }
char* str() const { return internal_; }
const char* c_str() const { return internal_; }
};
Und du kannst es verwenden:
void aFunctionAPI(char* input);
// andere Sachen
aFunctionAPI("Foo"); // Dieser Aufruf ist nicht sicher. Wenn die Funktion den
// Literalstring ändert, wird das Programm abstürzen
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); // dies kompiliert nicht
aFunctionAPI(const_cast(myFoo.c_str())); // dies ist nicht sicher, std::string
// implementiert Referenzzählung und
// es kann den Wert anderer
// Strings ändern.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); // das ist in Ordnung
Ich habe die Klasse DeepString
genannt, weil sie eine tiefe und eindeutige Kopie (die DeepString
ist nicht kopierbar) eines vorhandenen Strings erstellt.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
2 Stimmen
Anstatt: char * writable = new char[str.size() + 1]; Sie können char writable[str.size() + 1]; verwenden. Dann brauchen Sie sich keine Gedanken über das Löschen von beschreibbar oder die Ausnahmebehandlung zu machen.
13 Stimmen
Du kannst str.size() nicht verwenden, es sei denn, die Größe ist zur Kompilierzeit bekannt. Außerdem könnte es deinen Stapel überlaufen, wenn der feste Größenwert riesig ist.
1 Stimmen
Char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
10 Stimmen
@cegprakash
strcpy
undmalloc
sind nicht wirklich der C++-Weg.0 Stimmen
@boycy: Meinst du, dass sie imaginär sind?
8 Stimmen
Nein, aber
char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)
wäre idiomatischeres C++.strcpy()
undmalloc()
sind nicht falsch oder problematisch, aber es scheint inkonsistent zu sein, eine C++-Zeichenkette und C-Bibliothekseinrichtungen mit C++-Äquivalenten im selben Codeblock zu verwenden.0 Stimmen
stringName.c_str()
solange es sich um einstd::string
,std::wstring
oder etwas Ähnliches wie einwinrt::hstring
handelt.