Wie kann ich einen std::string
in einen char*
oder einen const char*
konvertieren?
Wenn Sie nur std::string
an eine Funktion übergeben möchten, die _const char*
_ benötigt, können Sie diese verwenden
_std::string str;
const char * c = str.c_str();
_
Wenn Sie eine beschreibbare Kopie wie _char *
_ erhalten möchten, können Sie dies folgendermaßen tun:
_std::string str;
char * writable = new char[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 it
delete[] writable;
_
Bearbeiten : Beachten Sie, dass das oben Genannte nicht ausnahmesicher ist. Wenn irgendetwas zwischen dem Aufruf new
und dem Aufruf delete
ausgelöst wird, verlieren Sie Speicher, da nichts automatisch delete
für Sie aufruft. Es gibt zwei unmittelbare Möglichkeiten, dies zu lösen.
boost::scoped_array
löscht den Speicher für Sie, wenn Sie den Gültigkeitsbereich verlassen:
_std::string str;
boost::scoped_array<char> writable(new char[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
_
Dies ist die Standardmethode (erfordert keine externe Bibliothek). Sie verwenden std::vector
, das den Speicher vollständig für Sie verwaltet.
_std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.Push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
_
Gegeben, sagen ...
std::string x = "hello";
So erhalten Sie einen Zeichenzeiger, der gültig ist, während x
im Gültigkeitsbereich bleibt und nicht weiter geändert wird
C++ 11 vereinfacht die Dinge; Die folgenden Befehle geben Zugriff auf denselben internen Zeichenfolgenpuffer:
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 obigen 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 verbleibt (z. B. enthält std::string("this\0that", 9)
einen Puffer "this\0that\0"
).
Ausgehend von einem der obigen 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 -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 einer NUL an einer anderen Stelle in der Zeichenfolge bewirkt nicht eine Änderung der size()
von string
; string
dürfen beliebig viele NULs enthalten - sie werden von std::string
(wie in C++ 03) nicht speziell behandelt.
In C++ 03 waren die Dinge wesentlich komplizierter (Hauptunterschiede hervorgehoben):
x.data()
const char*
in den internen Puffer des Strings zurück was vom Standard nicht erforderlich war, um mit einer NUL abzuschließen (dh möglicherweise ['h', 'e', 'l', 'l', 'o']
gefolgt von nicht initialisierten oder Garbage-Werten) mit versehentlichen Zugriffen darauf mit ndefiniertes Verhalten). x.size()
Zeichen können sicher gelesen werden, d. h. x[0]
bis x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
angegeben ist, dürfen Sie f(&x[0], x.size());
nicht aufrufen, wenn x.empty()
- verwenden Sie einfach f(x.data(), ...)
.x.data()
aber: const
x
ergibt dies einen nonconst
char*
-Zeiger; Sie können Zeichenfolgeninhalte überschreibenx.c_str()
const char*
an eine ASCIIZ-Darstellung (NUL-terminiert) des Werts zurück (d. h. ['h', 'e', 'l', 'l', 'o', '\ 0']).x.data()
und &x[0]
"exponiert" wurdex.size()
+ 1 Zeichen sind sicher zu lesen.Unabhängig davon, wie Sie einen Zeiger erhalten, dürfen Sie nicht weiter vom Zeiger entfernt auf den Speicher zugreifen als die in den obigen Beschreibungen garantierten Zeichen. Versuche dies zu tun haben ndefiniertes Verhalten, mit einer sehr realen Wahrscheinlichkeit von Anwendungsabstürzen und Speicherbereinigungen, selbst bei Lesevorgängen, und zusätzlich Daten, Stapelbeschädigungen und/oder Sicherheitslücken für Schreibvorgänge.
Wenn Sie eine Memberfunktion string
aufrufen, die string
ändert oder weitere Kapazität reserviert, sind alle zuvor von einer der oben genannten Methoden zurückgegebenen Zeigerwerte ngültig. Sie können diese Methoden erneut verwenden, um einen anderen Zeiger abzurufen. (Die Regeln sind die gleichen wie für Iteratoren in string
s).
Siehe auch So erhalten Sie einen Zeichenzeiger, der auch dann gültig ist, wenn x
den Gültigkeitsbereich verlässt oder weiter geändert wird unten ....
Verwenden Sie in C++ 11 .c_str()
für ASCIIZ-Daten und .data()
für "binäre" Daten (weiter unten erläutert).
Verwenden Sie in C++ 03 .c_str()
, sofern nicht sichergestellt ist, dass .data()
ausreicht, und ziehen Sie .data()
&x[0]
vor, da dies für leere Zeichenfolgen sicher ist ....
... versuchen Sie, das Programm so gut zu verstehen, dass Sie data()
verwenden können, sonst machen Sie wahrscheinlich andere Fehler ...
Das Zeichen ASCII NUL '\ 0', das durch .c_str()
garantiert wird, wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und zugriffssicherer Daten kennzeichnet. Dies gilt für beide C++ - nur Funktionen wie say fstream::fstream(const char* filename, ...)
und shared-with-C-Funktionen wie strchr()
und printf()
.
Da die .c_str()
- Garantien für den zurückgegebenen Puffer in C++ 03 eine Übermenge von .data()
sind, können Sie .c_str()
immer ohne Bedenken verwenden, andere Benutzer jedoch manchmal nicht da:
.data()
wird anderen Programmierern mitgeteilt, die den Quellcode lesen, dass die Daten nicht ASCIIZ sind dass Sie es an eine andere Funktion übergeben, die es als Block "binärer" Daten behandelt. Dies kann eine wichtige Erkenntnis sein, um sicherzustellen, dass die Codeänderungen anderer Programmierer weiterhin ordnungsgemäß mit den Daten umgehen.string
-Implementierung zusätzliche Speicherzuweisungen und/oder Datenkopien vornehmen muss, um den NUL-terminierten Puffer vorzubereitenEin weiterer Hinweis: Wenn die Parameter einer Funktion den (const
) char*
erfordern, aber nicht darauf bestehen, x.size()
zu erhalten, benötigt die Funktion wahrscheinlich eine ASCIIZ-Eingabe .c_str()
ist eine gute Wahl (die Funktion muss wissen, wo der Text auf irgendeine Weise endet. Wenn es sich also nicht um einen separaten Parameter handelt, kann es sich nur um eine Konvention wie ein Längenpräfix oder ein Sentinel oder eine bestimmte erwartete Länge handeln).
x
den Gültigkeitsbereich verlässt oder weiter geändert wirdSie müssen copy den Inhalt von string
x
in einen neuen Speicherbereich außerhalb von x
kopieren. . Dieser externe Puffer kann sich an vielen Stellen befinden, z. B. an einer anderen string
oder einer anderen Zeichenarrayvariablen. Möglicherweise hat er eine andere Lebensdauer als x
, da er sich in einem anderen Bereich befindet (z. B. Namespace, global, statisch) , Haufen, gemeinsamer Speicher, zugeordnete Speicherdatei).
So kopieren Sie den Text von 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*
oder const char*
aus einem string
generiert werden sollOben haben Sie also gesehen, wie Sie einen (const
) char*
erhalten und wie Sie eine Kopie des Texts unabhängig vom Original string
erstellen, aber was können Sie tun = damit? Eine zufällige Auswahl an Beispielen ...
string
gewähren, wie in printf("x is '%s'", x.c_str());
x
in einen vom Aufrufer Ihrer Funktion angegebenen Puffer (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
an ein Zeichenfeld 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 möglicherweise strncat
verwenden).const char*
oder char*
von einer Funktion zurückgeben (möglicherweise aus historischen Gründen - der Client verwendet Ihre vorhandene API - oder aus Gründen der C-Kompatibilität möchten Sie keinen std::string
zurückgeben, sondern die Daten Ihres string
irgendwo für den Aufrufer kopieren ) string
-Variable, auf die dieser Zeiger zeigte, 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 vermeidenVerwenden Sie die Methode .c_str()
für const char *
.
Sie können &mystring[0]
verwenden, um einen char *
-Zeiger abzurufen, aber es gibt ein paar Fallstricke: Sie erhalten nicht unbedingt eine Zeichenfolge mit Nullabschluss, und Sie können die Größe der Zeichenfolge nicht ändern. Sie müssen besonders darauf achten, keine Zeichen nach dem Ende der Zeichenfolge einzufü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 funktionierten alle bekannten Implementierungen von std::string
trotzdem so. see Zeigt "& s [0]" auf zusammenhängende Zeichen in einer std :: string? .
Beachten Sie, dass viele string
Member-Funktionen den internen Puffer neu zuordnen und alle Zeiger ungültig machen, die Sie möglicherweise gespeichert haben. Am besten sofort verwenden und dann wegwerfen.
C++ 17 (kommender Standard) ändert die Synopse der Vorlage basic_string
und fügt eine nicht konstante Überladung von data()
hinzu:
charT* data() noexcept;
Rückgabe: Ein Zeiger p mit dem Operator p + i == & für jedes i in [0, size ()].
CharT const *
aus std::basic_string<CharT>
std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()
CharT *
aus std::basic_string<CharT>
std::string str = { "..." };
char * p = str.data();
CharT const *
aus std::basic_string<CharT>
std::string str = { "..." };
str.c_str();
CharT *
aus std::basic_string<CharT>
Ab C++ 11 heißt es im Standard:
- Die char-ähnlichen Objekte in einem
basic_string
-Objekt sollen zusammenhängend gespeichert werden. Das heißt, für jedesbasic_string
Objekts
gilt die Identität&*(s.begin() + n) == &*s.begin() + n
für alle Werte vonn
, so dass0 <= n < s.size()
.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Rückgabe:
*(begin() + pos)
ifpos < size()
, andernfalls ein Verweis auf ein Objekt vom TypCharT
mit dem WertCharT()
; Der angegebene Wert darf nicht geändert werden.
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Rückgabe: Ein Zeiger p mit dem Wert
p + i == &operator[](i)
für jedesi
in[0,size()]
.
Es gibt verschiedene Möglichkeiten, einen nicht konstanten Zeichenzeiger zu erhalten.
std::string foo{"text"};
auto p = &*foo.begin();
Pro
Nachteile
'\0'
darf nicht verändert werden/ist nicht notwendigerweise Teil des nicht konstanten Speichers.std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
Nachteile
std::array<CharT, N>
, wenn N
die Kompilierungszeitkonstante ist (und klein genug ist)std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
Pro
Nachteile
std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
Pro
Nachteile
std::string foo{ "text" };
char * p = nullptr;
try
{
p = new char[foo.size() + 1u];
std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
// handle stuff with p
delete[] p;
}
catch (...)
{
if (p) { delete[] p; }
throw;
}
Pro
Con
Ich arbeite mit einer API mit vielen Funktionen, die als Eingabe einen char*
bekommen.
Ich habe eine kleine Klasse erstellt, um diese Art von Problem zu lösen. Ich habe das RAII-Idiom implementiert.
class DeepString
{
DeepString(const DeepString& other);
DeepString& operator=(const DeepString& other);
char* internal_;
public:
explicit DeepString( const 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 Sie können es verwenden als:
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 DeepString
aufgerufen, weil sie eine tiefe und eindeutige Kopie (die DeepString
ist nicht kopierbar) einer vorhandenen Zeichenfolge erstellt.
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
Seht euch das an:
string str1("stackoverflow");
const char * str2 = str1.c_str();
Beachten Sie jedoch, dass dies einen const char *
zurückgibt. Verwenden Sie strcpy
für einen char *
, um ihn in ein anderes char
Array zu kopieren.