[C++] Come convertire uno std :: string in const char * o char *?


Answers

Detto ciò ...

std::string x = "hello";

Ottenere un `char *` o `const char *` da una `stringa`

Come ottenere un puntatore di caratteri valido mentre x rimane nell'ambito e non viene ulteriormente modificato

C ++ 11 semplifica le cose; i seguenti danno accesso allo stesso buffer di stringhe interno:

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

Tutti i puntatori precedenti manterranno lo stesso valore - l'indirizzo del primo carattere nel buffer. Anche una stringa vuota ha un "primo carattere nel buffer", poiché C ++ 11 garantisce di mantenere sempre un carattere di terminatore NUL / 0 in più dopo il contenuto di stringa assegnato esplicitamente (ad es. std::string("this\0that", 9) avrà un buffer contenente "this\0that\0" ).

Considerato uno qualsiasi dei suggerimenti sopra riportati:

char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]

Solo per il puntatore non const di &x[0] :

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

Scrivere un NUL altrove nella stringa non cambia la size() della string size() ; string possono contenere un numero qualsiasi di NUL - non viene dato loro alcun trattamento speciale da std::string (lo stesso in C ++ 03).

In C ++ 03 , le cose erano notevolmente più complicate ( evidenziate le differenze principali ):

  • x.data()

    • ritorna const char* al buffer interno della stringa che non era richiesto dallo Standard per concludere con un NUL (cioè potrebbe essere ['h', 'e', 'l', 'l', 'o'] seguito da non inizializzato o valori di immondizia, con accessi accidentali ad essi con comportamento indefinito ).
      • x.size() sono sicuri da leggere, ovvero da x[0] a x[x.size() - 1]
      • per le stringhe vuote, è garantito un puntatore non NULL a cui è possibile aggiungere 0 in modo sicuro (evviva!), ma non è necessario dereferenziare quel puntatore.
  • &x[0]

    • per le stringhe vuote ha un comportamento indefinito (21.3.4)
      • per esempio dato f(const char* p, size_t n) { if (n == 0) return; ...whatever... } f(const char* p, size_t n) { if (n == 0) return; ...whatever... } si deve chiamare f(&x[0], x.size()); quando x.empty() - usa solo f(x.data(), ...) .
    • in caso contrario, come per x.data() ma:
      • per non- const x questo produce un puntatore non- const char* ; è possibile sovrascrivere il contenuto di stringhe
  • x.c_str()

    • ritorna const char* a una rappresentazione ASCIIZ (NUL-terminated) del valore (cioè ['h', 'e', ​​'l', 'l', 'o', '\ 0']).
    • sebbene poche o poche implementazioni abbiano scelto di farlo, lo standard C ++ 03 è stato formulato per consentire all'implementazione della stringa la libertà di creare al volo un distinto buffer NUL distinto , dal buffer terminato potenzialmente non NUL "esposto" da x.data() e &x[0]
    • x.size() + 1 caratteri sono sicuri da leggere.
    • garantito sicuro anche per stringhe vuote (['\ 0']).

Conseguenze dell'accesso a indici legali esterni

In qualsiasi modo si ottenga un puntatore, non è necessario accedere alla memoria più lontano dal puntatore rispetto ai caratteri garantiti presenti nelle descrizioni sopra. I tentativi di fare ciò hanno un comportamento indefinito , con una possibilità molto reale di crash dell'applicazione e risultati spazzatura anche per le letture, oltre a dati all'ingrosso, vulnerabilità dello stack e / o vulnerabilità della sicurezza per le scritture.

Quando vengono invalidati quei puntatori?

Se si chiama una funzione membro della string che modifica la string o riserva ulteriore capacità, qualsiasi valore puntatore restituito in precedenza da uno dei metodi sopra indicati viene invalidato . È possibile utilizzare nuovamente questi metodi per ottenere un altro puntatore. (Le regole sono le stesse degli iteratori nella string s).

Vedi anche Come ottenere un puntatore di caratteri valido anche dopo che x lascia lo scope o viene modificato ulteriormente sotto ....

Quindi, che è meglio usare?

Da C ++ 11, utilizzare .c_str() per i dati ASCIIZ e .data() per i dati "binari" (spiegato di seguito).

In C ++ 03, usa .c_str() meno che certi .data() sia adeguato, e preferisca .data() over &x[0] dato che è sicuro per le stringhe vuote ....

... prova a capire il programma abbastanza da usare i data() quando appropriato, o probabilmente farai altri errori ...

Il carattere ASCII NUL '\ 0' garantito da .c_str() è utilizzato da molte funzioni come un valore sentinella che indica la fine dei dati rilevanti e sicuri per l'accesso. Questo vale sia per C ++ - solo funzioni come say fstream::fstream(const char* filename, ...) e funzioni shared-with-C come strchr() e printf() .

Date le .c_str() di .c_str() C ++ 03 sul buffer restituito sono un super-set di .data() , puoi sempre usare tranquillamente .c_str() , ma a volte le persone non lo fanno perché:

  • usando .data() comunica ad altri programmatori che leggono il codice sorgente che i dati non sono ASCIIZ (piuttosto, stai usando la stringa per memorizzare un blocco di dati (che a volte non è nemmeno veramente testuale), o che tu " passandolo ad un'altra funzione che la tratti come un blocco di dati "binari". Questo può essere uno spunto cruciale per garantire che le modifiche al codice degli altri programmatori continuino a gestire correttamente i dati.
  • Solo C ++ 03: c'è una piccola possibilità che l'implementazione della string debba eseguire allocazioni extra di memoria e / o copiatura dei dati per preparare il buffer terminato NUL

Come ulteriore suggerimento, se i parametri di una funzione richiedono il carattere ( const ) char* ma non insistono per ottenere x.size() , la funzione probabilmente ha bisogno di un input ASCIIZ, quindi .c_str() è una buona scelta (la funzione ha bisogno per sapere dove finisce il testo in qualche modo, quindi se non è un parametro separato può essere solo una convenzione come un prefisso o sentinella di lunghezza o una lunghezza prevista fissa).

Come ottenere un puntatore di caratteri valido anche dopo che x lascia l'ambito o viene ulteriormente modificato

Dovrai copiare il contenuto della string x in una nuova area di memoria esterna a x . Questo buffer esterno potrebbe trovarsi in molte posizioni come un'altra string o una variabile di array di caratteri, potrebbe avere o meno una durata diversa da x causa di un ambito diverso (ad esempio spazio dei nomi, globale, statico, heap, memoria condivisa, memoria mappata file).

Per copiare il testo da std::string x in un array di caratteri indipendente:

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

Altri motivi per desiderare un char* o const char* generato da una string

Quindi, sopra hai visto come ottenere un ( const ) char* , e come fare una copia del testo indipendentemente dalla string originale, ma cosa puoi fare con esso? Un'infarinatura casuale di esempi ...

  • dare accesso al codice "C" al testo della string C ++, come in printf("x is '%s'", x.c_str());
  • copia il testo di x in un buffer specificato dal chiamante della tua funzione (es. strncpy(callers_buffer, callers_buffer_size, x.c_str()) ), o memoria volatile usata per I / O del dispositivo (es. for (const char* p = x.c_str(); *p; ++p) *p_device = *p; )
  • aggiungi il testo di x a un array di caratteri che contiene già un testo ASCIIZ (ad esempio strcat(other_buffer, x.c_str()) ) - fai attenzione a non sovraccaricare il buffer (in molte situazioni potresti aver bisogno di usare strncat )
  • restituisce un const char* o char* da una funzione (forse per ragioni storiche - il client usa l'API esistente - o per la compatibilità C non vuoi restituire una std::string ma vuoi copiare i dati della string da qualche parte per il chiamante)
    • fare attenzione a non restituire un puntatore che può essere dereferenziato dal chiamante dopo una variabile di string locale a cui il puntatore puntato ha lasciato l'ambito
    • alcuni progetti con oggetti condivisi compilati / collegati per diverse implementazioni std::string (es. STLport e nativo del compilatore) possono passare dati come ASCIIZ per evitare conflitti
Question

Come posso convertire una std::string in un char* o in un const char* ?




C ++ 17

C ++ 17 (imminente standard) modifica la sinossi del modello basic_string aggiungendo un sovraccarico non basic_string di data() :

charT* data() noexcept;

Restituisce: un puntatore p tale che p + i == e operatore per ogni i in [0, size ()].

CharT const * da std::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT * da std::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C ++ 11

CharT const * da std::basic_string<CharT>

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

CharT * da std::basic_string<CharT>

Da C ++ 11 in poi, lo standard dice:

  1. Gli oggetti char-like in un oggetto basic_string devono essere memorizzati in modo contiguo. Cioè, per ogni oggetto basic_string s , l'identità &*(s.begin() + n) == &*s.begin() + n deve contenere per tutti i valori di n tali che 0 <= n < s.size() .
  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    Restituisce: *(begin() + pos) se pos < size() , altrimenti un riferimento a un oggetto di tipo CharT con valore CharT() ; il valore di riferimento non deve essere modificato.

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

    Restituisce: un puntatore p tale che p + i == &operator[](i) per ogni i in [0,size()] .

Esistono diversi modi possibili per ottenere un puntatore di caratteri non const.

1. Utilizzare la memoria contigua di C ++ 11

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

professionista

  • Semplice e breve
  • Veloce (solo metodo senza copia in questione)

Contro

  • Final '\0' non deve essere modificato / non fa necessariamente parte della memoria non const.

2. Usa std::vector<CharT>

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

professionista

  • Semplice
  • Gestione automatica della memoria
  • Dinamico

Contro

  • Richiede una copia di stringa

3. Usa std::array<CharT, N> se N è costante di tempo di compilazione (e abbastanza piccola)

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

professionista

  • Semplice
  • Gestisce la memoria dello stack

Contro

  • Statico
  • Richiede una copia di stringa

4. Assegnazione di memoria non elaborata con cancellazione automatica della memoria

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

professionista

  • Piccolo ingombro di memoria
  • Cancellazione automatica
  • Semplice

Contro

  • Richiede una copia di stringa
  • Statico (l'uso dinamico richiede molto più codice)
  • Meno funzioni del vettore o dell'array

5. Assegnazione di memoria non elaborata con gestione manuale

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

professionista

  • Massimo 'controllo'

contro

  • Richiede una copia di stringa
  • Responsabilità massima / suscettibilità per errori
  • Complesso



Basta vedere questo:

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

Tuttavia, si noti che questo restituirà un const char * . Per un char * , usare strcpy per copiarlo in un altro array di char .




Prova questo

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