c++ - conversion - string to const char arduino




Wie konvertiert man eine std:: string in const char*oder char*? (6)

C ++ 17

C ++ 17 (kommender Standard) ändert die Zusammenfassung der Vorlage basic_string fügt eine nicht basic_string Ü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 sollen zusammenhängend gespeichert werden. Das heißt, für jedes basic_string Objekt s muss 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ückgabewerte: *(begin() + pos) wenn pos < size() , andernfalls eine Referenz auf ein Objekt vom Typ 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;

    Rückgabe: Ein Zeiger p, so dass p + i == &operator[](i) für jedes i in [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

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

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

Profi

  • Einfach
  • Automatische Speicherverwaltung
  • Dynamisch

Nachteile

  • Erfordert Zeichenfolge kopieren

3. Verwenden Sie std::array<CharT, N> wenn N Kompilierzeitkonstante ist (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

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


Gegeben gesagt ...

std::string x = "hello";

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

Wie erhält man einen Zeichenzeiger, der gültig ist, während x im Gültigkeitsbereich bleibt und nicht weiter modifiziert wird?

C ++ 11 vereinfacht 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 haben den gleichen Wert - die Adresse des ersten Zeichens im Puffer. Auch ein leerer String hat ein "erstes Zeichen im Puffer", da C ++ 11 garantiert, dass nach dem explizit zugewiesenen String-Inhalt immer ein zusätzliches NUL / 0-Abschlusszeichen erhalten std::string("this\0that", 9) (zB std::string("this\0that", 9) wird einen Puffer haben, der "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 den 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

Das Schreiben einer NUL an einer anderen Stelle in der Zeichenfolge ändert nicht die size() der string size() . string dürfen eine beliebige Anzahl von NULs enthalten - sie werden von std::string (dasselbe in C ++ 03).

In C ++ 03 waren die Dinge wesentlich komplizierter (Hauptunterschiede hervorgehoben ):

  • x.data()

    • gibt const char* an den internen Puffer der Zeichenfolge zurück, der vom Standard nicht benötigt wurde, um mit einer NUL zu schließen (dh ['h', 'e', 'l', 'l', 'o'] gefolgt von nicht initialisiert) oder Müllwerte, wobei zufällige Zugriffe darauf undefiniertes Verhalten haben ).
      • x.size() Zeichen sind sicher zu lesen, dh x[0] bis 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 undefiniertes Verhalten (21.3.4)
      • zB gegeben f(const char* p, size_t n) { if (n == 0) return; ...whatever... } f(const char* p, size_t n) { if (n == 0) return; ...whatever... } Sie dürfen nicht f(&x[0], x.size()); wenn x.empty() - benutze einfach f(x.data(), ...) .
    • ansonsten, wie in x.data() aber:
      • Für nicht const x ergibt dies einen nichtkonstanten char* -Zeiger; Sie können den String-Inhalt überschreiben
  • x.c_str()

    • gibt const char* an eine ASCIIZ (NUL-terminated) Darstellung des Wertes zurück (zB ['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 gab, aus dem potenziell nicht-NUL-terminierten Puffer, der durch x.data() "exponiert" wurde, einen eigenen NULL-terminierten Puffer zu erzeugen 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 ein undefiniertes Verhalten mit einer sehr realen Wahrscheinlichkeit von Anwendungsabstürzen und Müll-Ergebnissen sogar für Lesevorgänge und zusätzlich Großhandelsdaten, Stapel-Korruption und / oder Sicherheitsschwachstellen für Schreibvorgänge.

Wann werden diese Zeiger ungültig gemacht?

Wenn Sie eine string Member-Funktion aufrufen, die die string ändert oder weitere Kapazität reserviert, werden alle Zeigerwerte ungültig, die zuvor von einer der oben genannten Methoden zurückgegeben wurden. Sie können diese Methoden erneut verwenden, um einen anderen Zeiger zu erhalten. (Die Regeln sind die gleichen wie für Iteratoren in string s).

Siehe auch So erhalten Sie einen Zeichenzeiger, der gültig ist, auch nachdem x Bereich verlassen hat oder weiter unten geändert wird ....

Also, welche ist besser zu benutzen?

Verwenden Sie in C ++ 11 .c_str() für ASCIIZ-Daten und .data() für "binäre" Daten (wird weiter unten erläutert).

Verwenden .c_str() in C ++ 03 .c_str() sofern nicht .data() ist, dass .data() ausreichend ist, und bevorzugen Sie .data() über &x[0] da es für leere Strings sicher ist.

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

Das Zeichen ASCII NUL '\ 0', das durch .c_str() garantiert wird, wird von vielen Funktionen als Sentinel-Wert verwendet, der das Ende relevanter und sicher zuzugreifender Daten bezeichnet. Dies gilt sowohl für C ++ - only-Funktionen wie zB fstream::fstream(const char* filename, ...) als auch für shared-with-C-Funktionen wie strchr() und printf() .

Gegeben C ++ .c_str() 's Garantien über den zurückgegebenen Puffer sind ein Super-Set von .data() ' s, können Sie immer sicher verwenden. .c_str() , aber Menschen manchmal nicht, weil:

  • die Verwendung von .data() kommuniziert mit anderen Programmierern, die den Quellcode lesen, dass die Daten nicht ASCIIZ sind (stattdessen verwenden Sie die Zeichenfolge, um einen Datenblock zu speichern (der manchmal nicht einmal wirklich textuell ist)) oder dass Sie Übergeben Sie es an eine andere 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 Ihre string Implementierung zusätzliche Speicherzuweisungen und / oder Datenkopien durchführen muss, um den NUL-terminierten Puffer vorzubereiten

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

Wie erhält man einen Zeichenzeiger, der gültig ist, nachdem x Gültigkeitsbereich verlassen hat oder weiter modifiziert wird?

Sie müssen den Inhalt der string x in einen neuen Speicherbereich außerhalb von x kopieren . Dieser externe Puffer könnte sich an vielen Stellen befinden, etwa an einer anderen string oder Character-Array-Variablen, da er sich in einem anderen Bereich befindet (z. B. Namespace, Global, Static, Heap, Shared Memory, Memory Mapped) Datei).

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

// 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 char* oder const char* aus einem string generiert werden sollen

Also, oben haben Sie gesehen, wie man ein ( const ) char* bekommt und wie man eine Kopie des Textes unabhängig von der ursprünglichen string , aber was kann man damit machen ? Eine zufällige Auswahl an Beispielen ...

  • Geben Sie "C" -Code-Zugriff auf den Text der C ++ - string , wie in printf("x is '%s'", x.c_str());
  • Kopieren Sie den Text von x in einen Puffer, der vom Aufrufer Ihrer Funktion angegeben wurde (z. B. strncpy(callers_buffer, callers_buffer_size, x.c_str()) ) oder flüchtigen Speicher für Geräte-E / A (zB for (const char* p = x.c_str(); *p; ++p) *p_device = *p; )
  • füge den x -Text an ein Zeichen-Array an, das bereits ASCIIZ-Text enthält (z. B. strcat(other_buffer, x.c_str()) ) - strcat(other_buffer, x.c_str()) den Puffer nicht zu überschreiten (in vielen Situationen musst du strncat )
  • Rückgabe eines const char* oder char* aus einer Funktion (vielleicht aus historischen Gründen - Client verwendet Ihre vorhandene API - oder für C-Kompatibilität wollen Sie keine std::string , aber wollen die Daten Ihrer string kopieren irgendwo für den Anrufer)
    • Achten Sie darauf, keinen Zeiger zurückzugeben, der vom Aufrufer dereferenziert werden kann, nachdem eine lokale string , auf die der Zeiger gezeigt hat, den Bereich verlassen hat
    • Einige Projekte mit gemeinsamen Objekten, die für verschiedene std::string Implementierungen (z. B. STLport und Compiler-native) kompiliert / verknüpft wurden, können Daten als ASCIIZ übergeben, um Konflikte zu vermeiden

Sieh dir das an:

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

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


Versuche dies

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

Wenn Sie nur eine std::string an eine Funktion übergeben möchten, die const char* , können Sie verwenden

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

Wenn Sie eine schreibbare Kopie wie char * möchten, können Sie das 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 ausnahmesicher ist. Wenn irgendetwas zwischen dem new Anruf und dem delete auftritt, werden Sie Speicher verlieren, da nichts automatisch für Sie automatisch delete . 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 verwenden std::vector , das 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()

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






const