wake-up-neo.com

Wie konvertiere ich einen std :: string nach const char * oder char *?

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

853
user37875

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

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
_

std :: vector

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

Gegeben, sagen ...

std::string x = "hello";

Holen Sie sich ein `char *` oder `const char * *` aus einem `string`

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

    • gibt 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]
      • bei leeren Zeichenfolgen ist garantiert, dass ein Zeiger nicht NULL ist, zu dem ohne Bedenken 0 hinzugefügt werden kann (Hurra!). Sie sollten diesen Zeiger jedoch nicht dereferenzieren.
  • &x[0]

    • für leere Zeichenketten hat dies undefiniertes Verhalten (21.3.4)
      • z.B. Wenn 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(), ...).
    • ansonsten laut x.data() aber:
      • für nonconstx ergibt dies einen nonconstchar*-Zeiger; Sie können Zeichenfolgeninhalte überschreiben
  • x.c_str()

    • gibt const char* an eine ASCIIZ-Darstellung (NUL-terminiert) des Werts zurück (d. h. ['h', 'e', ​​'l', 'l', 'o', '\ 0']).
    • obwohl es nur wenige Implementierungen gab, die dies wählten, wurde der C++ 03-Standard formuliert, um der String-Implementierung die Freiheit zu geben, einen eindeutigen NUL-terminierten Pufferon the fly, aus dem möglicherweise nicht NUL-terminierten Puffer, der von x.data() und &x[0] "exponiert" wurde
    • x.size() + 1 Zeichen sind sicher zu lesen.
    • garantiert sicher, auch für leere Zeichenfolgen (['\ 0']).

Folgen des Zugriffs auf fremde Rechtsindizes

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.

Wann werden diese Zeiger ungültig?

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

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

Also, welches ist besser zu verwenden?

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:

  • mit .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.
  • Nur C++ 03: Es besteht eine geringe Wahrscheinlichkeit, dass Ihre string -Implementierung zusätzliche Speicherzuweisungen und/oder Datenkopien vornehmen muss, um den NUL-terminierten Puffer vorzubereiten

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

So erhalten Sie einen Zeichenzeiger, der auch dann gültig ist, wenn x den Gültigkeitsbereich verlässt oder weiter geändert wird

Sie müssen copy den Inhalt von stringx 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);

Andere Gründe, warum ein char* oder const char* aus einem string generiert werden soll

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

  • "C" -Code-Zugriff auf den Text von C++ string gewähren, wie in printf("x is '%s'", x.c_str());
  • kopieren Sie den Text von 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;).
  • fügen Sie den Text von 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).
  • einen 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 )
    • achten Sie darauf, keinen Zeiger zurückzugeben, der vom Aufrufer dereferenziert werden kann, nachdem eine lokale string -Variable, auf die dieser Zeiger zeigte, den Gültigkeitsbereich verlassen hat
    • einige Projekte mit gemeinsam genutzten Objekten, die für verschiedene std::string-Implementierungen kompiliert/verknüpft wurden (z. B. STLport und Compiler-native), übergeben möglicherweise Daten als ASCIIZ, um Konflikte zu vermeiden
186
Tony Delroy

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

33
Mark Ransom

C++ 17

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

C++ 11

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:

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

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

    Rückgabe: *(begin() + pos) if pos < size(), andernfalls ein Verweis auf ein Objekt vom Typ CharT mit dem Wert CharT(); Der angegebene Wert darf nicht geändert werden.


  1. 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 jedes i in [0,size()].

Es gibt verschiedene Möglichkeiten, einen nicht konstanten Zeichenzeiger zu erhalten.

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

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

Pro

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

Nachteile

  • Final '\0' darf nicht verändert werden/ist nicht notwendigerweise Teil des nicht konstanten Speichers.

2. Verwenden Sie std::vector<CharT>

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

Pro

  • Einfach
  • Automatisches Speicherhandling
  • Dynamisch

Nachteile

  • Erfordert eine Stringkopie

3. Verwenden Sie 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

  • Einfach
  • Stapelspeicher-Handling

Nachteile

  • Statisch
  • Erfordert eine Stringkopie

4. Raw Memory Allocation mit automatischem Löschen des Speichers

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

  • Geringer Speicherbedarf
  • Automatisches Löschen
  • Einfach

Nachteile

  • Erfordert eine Stringkopie
  • Statisch (dynamische Nutzung erfordert viel mehr Code)
  • Weniger Features als Vektor oder Array

5. Raw Memory Allocation 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;
}

Pro

  • Maximale "Kontrolle"

Con

  • Erfordert eine Stringkopie
  • Maximale Haftung/Fehleranfälligkeit
  • Komplex
20
Pixelchemist

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.

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

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.

7
devsaw