Frage Wie konvertiert man eine std :: string in const char * oder char *?


Wie kann ich ein std::string zu einem char* oder ein const char*?


777
2017-12-07 19:30


Ursprung


Antworten:


Wenn du einfach nur ein passierst std::string zu einer Funktion, die benötigt wird const char* Sie können verwenden

std::string str;
const char * c = str.c_str();

Wenn Sie eine beschreibbare Kopie erhalten möchten, wie char *das kannst du damit machen:

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 ausnahmslos sicher ist. Wenn irgendetwas zwischen den new rufen und die delete Anrufwürfe, Sie werden Speicher verlieren, da nichts rufen wird delete für dich automatisch. 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 Bereich 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

Std :: Vektor

Dies ist der Standardweg (benötigt keine externe Bibliothek). Sie nutzen std::vector, die den Speicher für Sie vollständig 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()

932
2017-12-07 19:31



Gegeben gesagt ...

std::string x = "hello";

 Ein `char *` oder `const char *` von einer `Zeichenkette` erhalten

Wie bekomme ich einen Zeichenzeiger, der gültig ist während? x bleibt im Umfang und wird nicht weiter modifiziert

C ++ 11 vereinfacht die Dinge; die folgenden geben Zugriff auf den gleichen internen Zeichenfolgenpuffer:

const char* p_c_str = x.c_str();
const char* p_data  = x.data();
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...

Alle oben genannten Zeiger halten die gleicher Wert - die Adresse des ersten Zeichens im Puffer. Sogar eine leere Zeichenkette hat ein "erstes Zeichen im Puffer", weil C ++ 11 garantiert, dass immer ein zusätzliches NUL / 0-Abschlusszeichen nach dem explizit zugewiesenen Zeichenketteninhalt (z. std::string("this\0that", 9) wird einen Puffer halten "this\0that\0").

Gegeben eine 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 die Nicht-const Zeiger von &x[0]:

p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                 // i.e. don't overwrite the implementation maintained NUL

Schreiben eines NUL an anderer Stelle in der Zeichenfolge nicht ändere das stringist es size(); string's dürfen eine beliebige Anzahl von NUL enthalten - sie werden nicht gesondert behandelt std::string (Gleiches in C ++ 03).

Im C ++ 03Die Dinge waren wesentlich komplizierter (Hauptunterschiede) markiert):

  • x.data()

    • kehrt zurück const char* in den internen Puffer der Zeichenfolge was vom Standard nicht verlangt wurde, um mit einem NUL abzuschließen (d. h. könnte sein ['h', 'e', 'l', 'l', 'o'] gefolgt von nicht initialisierten oder Müllwerten, mit zufälligen Zugriffen darauf undefiniertes Verhalten).
      • x.size() Zeichen sind sicher zu lesen, d.h. x[0] durch x[x.size() - 1]
      • Für leere Strings ist garantiert ein Non-NULL-Zeiger, auf den 0 sicher hinzugefügt werden kann (hurra!), aber Sie sollten den Zeiger nicht dereferenzieren.
  • &x[0]

    • bei leeren Strings hat dies ein undefiniertes Verhalten (21.3.4)
      • z.B. gegeben f(const char* p, size_t n) { if (n == 0) return; ...whatever... } Du darfst nicht anrufen f(&x[0], x.size()); wann x.empty() - benutz einfach f(x.data(), ...).
    • ansonsten, wie x.data() aber:
      • für nichtconst  x Dies ergibt eine nichtconst  char* Zeiger; Sie können den String-Inhalt überschreiben
  • x.c_str()

    • kehrt zurück const char* zu einer ASCIIZ (NUL-terminierten) Darstellung des Wertes (d. h. ['h', 'e', ​​'l', 'l', 'o', '\ 0']).
    • Obwohl nur wenige Implementierungen dies taten, wurde der C ++ 03 - Standard so formuliert, dass er der String - Implementierung die Freiheit gibt, einen eindeutiger NUL-terminierter Puffer  im laufenden Betrieb, von dem potentiell nicht NUL-terminierten Puffer "exponiert" 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 rechtliche Indizes

Unabhängig davon, wie Sie einen Zeiger erhalten, dürfen Sie vom Zeiger nicht weiter auf den Speicher zugreifen als auf die Zeichen, die in den obigen Beschreibungen garantiert sind. Versuche, dies zu tun haben undefiniertes Verhalten, mit einer sehr realen Chance von Anwendungsabstürzen und Müll-Ergebnissen sogar für Lesevorgänge, und zusätzlich Großhandelsdaten, Stack-Korruption und / oder Sicherheitslücken für Schreibvorgänge.

Wann werden diese Zeiger ungültig gemacht?

Wenn Sie jemanden anrufen string Member-Funktion, die die ändert string oder reserviert weitere Kapazität, sind alle Zeigerwerte, die zuvor durch eine der obigen Methoden zurückgegeben wurden ungültig gemacht. Sie können diese Methoden erneut verwenden, um einen anderen Zeiger zu erhalten. (Die Regeln sind die gleichen wie für Iteratoren in strings).

Siehe auch Wie bekomme ich einen Zeichenzeiger, der auch danach gültig ist? x verlässt den Geltungsbereich oder wird weiter modifiziert unten....

Also, was ist besser benutzen?

Ab C ++ 11 verwenden .c_str() für ASCIIZ Daten und .data() für "binäre" Daten (weiter unten erläutert).

In C ++ 03, verwenden .c_str() außer wenn das sicher ist .data() ist ausreichend und bevorzugen .data() Über &x[0] wie es für leere Saiten sicher ist ....

... versuchen Sie das Programm ausreichend zu verstehen data() wenn es angemessen ist, oder Sie werden wahrscheinlich andere Fehler machen ...

Das ASCII NUL '\ 0' Zeichen wird durch garantiert .c_str() wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und sicher zugänglicher Daten bezeichnet. Dies gilt für beide C ++ - nur Funktionen wie sagen fstream::fstream(const char* filename, ...) und Shared-with-C Funktionen wie strchr(), und printf().

Gegeben C ++ 03's .c_str()Die Garantien über den zurückgegebenen Puffer sind ein Super-Set von .data()'s kannst du immer sicher benutzen .c_str(), aber die Leute manchmal nicht, weil:

  • verwenden .data() kommuniziert mit anderen Programmierern, die den Quellcode lesen, dass die Daten nicht ASCIIZ sind (stattdessen verwenden Sie die Zeichenkette, um einen Datenblock zu speichern (der manchmal nicht einmal wirklich textuell ist), oder dass Sie ihn an einen anderen übergeben Funktion, die es als einen Block von "binären" Daten behandelt. Dies kann entscheidend dazu beitragen, dass Codeänderungen anderer Programmierer weiterhin ordnungsgemäß mit den Daten umgehen.
  • Nur C ++ 03: Es besteht eine geringe Chance, dass Sie string Die Implementierung muss einige zusätzliche Speicherzuweisungen und / oder Daten kopieren, um den NUL-terminierten Puffer vorzubereiten

Als weiteren Hinweis, wenn die Parameter einer Funktionconst) char* aber darauf nicht bestehen x.size(), die Funktion wahrscheinlich braucht eine ASCIIZ-Eingabe, also .c_str() ist eine gute Wahl (die Funktion muss wissen, wo der Text irgendwie endet, wenn es also kein separater Parameter ist, kann es nur eine Konvention wie ein Längenpräfix oder Sentinel oder eine bestimmte erwartete Länge sein).

Wie bekomme ich einen Zeichenzeiger, der auch danach gültig ist? x verlässt den Geltungsbereich oder wird weiter modifiziert

Das musst du Kopieren der Inhalt des string  x zu einem neuen Speicherbereich außerhalb x. Dieser externe Puffer könnte an vielen anderen Stellen vorhanden sein string oder Character-Array-Variable, kann es oder kann nicht eine andere Lebensdauer haben als x aufgrund dessen, dass es sich in einem anderen Bereich befindet (z. B. Namespace, global, statisch, Heap, Shared Memory, Memory-Mapped-Datei).

Um den Text zu kopieren std::string x in ein unabhängiges Zeichen-Array:

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

Andere Gründe, ein zu wollen char* oder const char* erzeugt von a string

Also, oben haben Sie gesehen, wie man ein (const) char*und wie man eine Kopie des Textes unabhängig vom Original erstellt stringAber was kannst du? machen damit? Eine zufällige Auswahl an Beispielen ...

  • Geben Sie "C" -Code-Zugriff auf C ++ string's Text, wie in printf("x is '%s'", x.c_str());
  • Kopieren xText an einen Puffer, der vom Aufrufer Ihrer Funktion angegeben wurde (z. strncpy(callers_buffer, callers_buffer_size, x.c_str())) oder flüchtiger Speicher, der für Geräte-E / A verwendet wird (z. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • anhängen xText in ein Zeichen-Array, das bereits ASCIIZ-Text enthält (z. strcat(other_buffer, x.c_str())) - Achten Sie darauf, den Puffer nicht zu überschreiten (in vielen Situationen, die Sie möglicherweise verwenden müssen) strncat)
  • zurückgeben const char* oder char* von einer Funktion (vielleicht aus historischen Gründen - Client verwendet Ihre vorhandene API - oder für C-Kompatibilität, die Sie nicht zurückgeben möchten a std::string, aber möchte deine kopieren string's Daten irgendwo für den Anrufer)
    • Achten Sie darauf, keinen Zeiger zurückzugeben, der vom Aufrufer nach einem lokalen dereferenziert werden könnte string Variable, auf die der Zeiger gezeigt hat, hat den Bereich verlassen
    • einige Projekte mit geteilten Objekten, die für verschiedene kompiliert / verknüpft wurden std::string Implementierungen (z. B. STLport und Compiler-native) können Daten als ASCIIZ übergeben, um Konflikte zu vermeiden

173
2018-01-12 15:53



Benutze die .c_str() Methode für const char *.

Sie können verwenden &mystring[0] einen bekommen char * Zeiger, aber es gibt ein paar Fehler: Sie werden nicht unbedingt eine Null abgeschlossene Zeichenfolge erhalten, und Sie werden nicht in der Lage sein, die Größe der Zeichenfolge zu ändern. Sie müssen besonders darauf achten, dass Sie keine Zeichen über das Ende der Zeichenfolge hinaus hinzufügen, sonst erhalten Sie einen Pufferüberlauf (und einen möglichen Absturz).

Es gab keine Garantie, dass alle Zeichen bis C ++ 11 Teil desselben zusammenhängenden Puffers sein würden, aber in der Praxis alle bekannten Implementierungen von std::string hat auf diese Weise trotzdem funktioniert; sehen Zeigt "& s [0]" auf zusammenhängende Zeichen in einer std :: string?.

Beachte das viele string member-Funktionen werden den internen Puffer neu zuordnen und Zeiger, die Sie möglicherweise gespeichert haben, ungültig machen. Am besten, sie sofort zu verwenden und dann zu verwerfen.


31
2018-03-29 13:32



C ++ 17

C ++ 17 (bevorstehender Standard) ändert die Zusammenfassung der Vorlage basic_string Hinzufügen einer nicht konstanten Ü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 = { "..." };
char const * 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 lautet der Standard:

  1. Die char-like Objekte in einem basic_string Objekt soll zusammenhängend gespeichert werden. Das heißt für jeden basic_string Objekt s, Die Identität &*(s.begin() + n) == &*s.begin() + n gilt für alle Werte von nso dass 0 <= n < s.size().

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

    Kehrt zurück: *(begin() + pos) ob pos < size()sonst eine Referenz auf ein Objekt vom Typ CharT mit Wert CharT(); Der referenzierte Wert darf nicht verändert werden.


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

    Rückgabe: Ein Zeiger p so, dass p + i == &operator[](i) für jede i im [0,size()].

Es gibt abtrennbare Möglichkeiten, um einen nicht-konstanten Zeichenzeiger zu erhalten.

1. Verwenden Sie den zusammenhängenden Speicher von C ++ 11

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

Profi

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

Nachteile

  • Finale '\0' ist nicht zu ändern / nicht notwendigerweise Teil des nicht-const Gedächtnisses.

2. Verwenden std::vector<CharT>

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

Profi

  • Einfach
  • Automatische Speicherverwaltung
  • Dynamisch

Nachteile

  • Erfordert Zeichenfolge kopieren

3. Verwenden std::array<CharT, N> ob N ist Kompilierzeitkonstante (und klein genug)

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

Profi

  • Einfach
  • Stapelspeicherbehandlung

Nachteile

  • Statisch
  • Erfordert Zeichenfolge kopieren

4. Raw-Speicherzuweisung mit automatischem Speicher löschen

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

Profi

  • Geringer Speicherbedarf
  • Automatisches Löschen
  • Einfach

Nachteile

  • Erfordert Zeichenfolge kopieren
  • Statisch (dynamische Verwendung erfordert viel mehr Code)
  • Weniger Features als Vektor oder Array

5. Rohspeicherzuweisung mit manueller Handhabung

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;
}

Profi

  • Maximale "Kontrolle"

Con

  • Erfordert Zeichenfolge kopieren
  • Maximale Haftung / Fehleranfälligkeit
  • Komplex

18
2017-07-12 12:06



Ich arbeite mit einer API mit vielen Funktionen als Eingabe ein char*.

Ich habe eine kleine Klasse geschaffen, 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 angerufen DeepString weil es eine tiefe und einzigartige Kopie schafft (die DeepString nicht kopierbar) einer vorhandenen Zeichenfolge.


9
2018-05-12 08:18



char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

7
2018-02-17 16:45



Sieh dir das an:

string str1("stackoverflow");
const char * str2 = str1.c_str();

Beachten Sie jedoch, dass dies a zurückgibt const char *.Für ein char *, benutzen strcpy um es in ein anderes zu kopieren char Array.


7



Versuche dies

std::string s(reinterpret_cast<const char *>(Data), Size);

-4