1088 Stimmen

Wie man einen std::string in const char* oder char* konvertiert

Wie kann ich einen std::string in einen char* oder ein const char* umwandeln?

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());

1294voto

Johannes Schaub - litb Punkte 479831

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.

53 Stimmen

Einfach char* result = strdup(str.c_str()); verwenden.

76 Stimmen

Du könntest es, aber strdup ist keine Standardfunktion in C oder C++, es stammt von POSIX :)

16 Stimmen

Was ich wahrscheinlich im Allgemeinen bevorzugen würde, ist std :: vector beschreibbar (str.begin(), str.end()); beschreibbar.push_back ('\ 0'); char * c = & beschreibbar [0];

225voto

Tony Delroy Punkte 98528

Gesetzt den Fall...

std::string x = "hello";

Abrufen eines `char *` oder `const char*` aus einem `string`

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()

    • gibt zurück. 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]
      • für leere Zeichenketten, sind Sie garantiert einige nicht-NULL-Zeiger, auf die 0 sicher hinzugefügt werden kann (hurra!), aber Sie sollten nicht dereferenzieren, dass Zeiger.
  • &x[0]

    • für leere Zeichenfolgen hat dies ein undefiniertes Verhalten (21.3.4)
      • z.B. gegeben 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(), ...) .
    • andernfalls, je nach x.data() sondern:
      • für Nicht const x ergibt sich ein Nicht const char* Zeiger; Sie können den Inhalt der Zeichenkette überschreiben
  • x.c_str()

    • gibt zurück. const char* in eine ASCIIZ-Darstellung (NUL-terminiert) des Wertes (d. h. ['h', 'e', 'l', 'l', 'o', ' \0 ']).
    • obwohl sich nur wenige Implementierungen dafür entschieden haben, wurde der C++03-Standard so formuliert, dass die String-Implementierung die Freiheit hat, eine eindeutiger Puffer mit NUL-Ende spontan aus dem potenziell nicht NUL-terminierten Puffer, der durch x.data() y &x[0]
    • x.size() + 1 Zeichen sind sicher zu lesen.
    • garantiert sicher auch für leere Zeichenketten ([' \0 ']).

Folgen des Zugriffs auf fremde Rechtsverzeichnisse

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.

Wann werden diese Zeiger ungültig?

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

Also, was ist mejor zu verwenden?

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:

  • mit .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.
  • Nur C++03: Es besteht eine geringe Chance, dass Ihr 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).

Wie erhält man einen Zeichenzeiger auch nach x den Geltungsbereich verlässt oder weiter verändert wird

Sie 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);

Weitere Gründe für die Anschaffung eines 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...

  • dem "C"-Code Zugriff auf die C++ string Text, wie in printf("x is '%s'", x.c_str());
  • kopieren. 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; )
  • anhängen. 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 )
  • ein zurückgeben 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)
    • darauf achten, dass kein Zeiger zurückgegeben wird, der vom Aufrufer nach einem lokalen Ereignis dereferenziert werden kann string die Variable, auf die dieser Zeiger zeigte, den Anwendungsbereich verlassen hat
    • einige Projekte mit gemeinsam genutzten Objekten, die für verschiedene Länder kompiliert/verlinkt wurden std::string Implementierungen (z.B. STLport und Compiler-native) können Daten als ASCIIZ übergeben, um Konflikte zu vermeiden

4 Stimmen

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

39voto

Mark Ransom Punkte 283960

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.

1 Stimmen

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.

1 Stimmen

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

2 Stimmen

Technisch wird der std::string-Speicher nur in C++0x zusammenhängend sein.

24voto

Pixelchemist Punkte 22777

C++17

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();

C++11

CharT const * aus std::basic_string

std::string str = { "..." };
str.c_str();

CharT * aus std::basic_string

Ab C++11 besagt der Standard:

  1. Die char-ähnlichen Objekte in einem basic_string-Objekt sollen kontinuierlich gespeichert werden. Das heißt, für jedes basic_string-Objekt s soll die Identität &*(s.begin() + n) == &*s.begin() + n für alle Werte von n, sodass 0 <= n < s.size(), gelten.

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    Gibt zurück: *(begin() + pos), wenn pos < size(), ansonsten eine Referenz auf ein Objekt des Typs CharT mit dem Wert CharT(); der referenzierte Wert darf nicht verändert werden.


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    Gibt zurück: Ein Zeiger p, sodass p + i == &operator[](i) für jedes i in [0, size()].

Es gibt mehrere Möglichkeiten, um einen nicht-const Zeiger auf Zeichen zu erhalten.

1. Verwendung des zusammenhängenden Speichers von C++11

std::string foo{"text"};
auto p = &*foo.begin();

Pro

  • Einfach und kurz
  • Schnell (nur Methode ohne Kopie)

Contra

  • Endgültiges '\0' darf nicht geändert werden / ist nicht unbedingt Teil des nicht-const Speichers.

2. Verwendung von std::vector

std::string foo{"text"};
std::vector fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

Pro

  • Einfach
  • Automatische Speicherverwaltung
  • Dynamisch

Contra

  • Erfordert Kopie des Strings

3. Verwenden von std::array, wenn N zur Kompilierzeit konstant (und klein genug) ist

std::string foo{"text"};
std::array fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

Pro

  • Einfach
  • Speicherverwaltung auf dem Stack

Contra

  • Statisch
  • Erfordert Kopie des Strings

4. Rohspeicherallokation mit automatischer Speicherbereinigung

std::string foo{ "text" };
auto p = std::make_unique(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

Pro

  • Kleiner Speicherbedarf
  • Automatische Bereinigung
  • Einfach

Contra

  • Erfordert Kopie des Strings
  • Statisch (dynamische Verwendung erfordert viel mehr Code)
  • Weniger Funktionen als Vektor oder Array

5. Rohspeicherallokation mit manueller Verwaltung

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

  • Maximale 'Kontrolle'

Contra

  • Erfordert Kopie des Strings
  • Maximale Haftung / Anfälligkeit für Fehler
  • Komplex

10voto

Alessandro Teruzzi Punkte 3879

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.

3 Stimmen

Ich würde diese Benennungskonvention meiden. c_str() , wie es von std verwendet wird, steht für "C-String" und nicht für "konstanten String", und str() gibt immer einen std::basic_string zurück, nicht char* (zum Beispiel std::stringstream::str()).

CodeJaeger.com

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.

Powered by:

X