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


894

Come posso convertire un std::stringin un char*o un const char*?


2
Invece di: char * writable = new char [str.size () + 1]; Puoi usare char writable [str.size () + 1]; Quindi non devi preoccuparti di eliminare la scrittura o la gestione delle eccezioni.

7
Non puoi usare str.size () a meno che la dimensione non sia nota al momento della compilazione, inoltre potrebbe sovraccaricare lo stack se il valore della dimensione fissa è enorme.
paulm,

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

7
@cegprakash strcpye mallocnon sono proprio in C ++.
Boycy,

4
No, ma char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)sarebbe C ++ più idiomatico. strcpy()e malloc()non sono sbagliati o problematici, ma sembra incoerente utilizzare una stringa C ++ e funzionalità di libreria C con equivalenti C ++ nello stesso blocco di codice.
Boycy,

Risposte:


1057

Se si desidera semplicemente passare std::stringa una funzione che è necessario, const char*è possibile utilizzare

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

Se vuoi ottenere una copia scrivibile, ad esempio char *, puoi farlo con questo:

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;

Modifica : si noti che quanto sopra non è sicuro. Se viene emesso qualcosa tra la newchiamata e la deletechiamata, perderai memoria, poiché nulla ti chiamerà deleteautomaticamente. Ci sono due modi immediati per risolvere questo.

boost :: scoped_array

boost::scoped_array cancellerà la memoria per te al di fuori dell'ambito:

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

Questo è il modo standard (non richiede alcuna libreria esterna). Si utilizza std::vector, che gestisce completamente la memoria per te.

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()

41
Usa semplicemente char * result = strdup (str.c_str ());
Jasper Bekkers,

63
potresti, ma strdup non è una funzione standard ac o c ++, è di posix :)
Johannes Schaub - litb

14
ciò che probabilmente preferirei generalmente è std :: vector <char> scrivibile (str.begin (), str.end ()); writable.push_back ( '\ 0'); char * c = & writable [0];
Johannes Schaub - litb

17
std :: copy è il modo c ++ per farlo, senza la necessità di accedere al puntatore di stringa. Cerco di evitare di usare le funzioni C il più possibile.
Johannes Schaub - litb

16
A partire da C ++ 17, std::string::data()ora restituisce a CharT*anziché a const CharT*. Potrebbe essere una buona idea aggiornare questa risposta :)
Rakete1111

192

Dato dire ...

std::string x = "hello";

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

Come ottenere un puntatore a caratteri valido mentre xrimane nell'ambito e non viene ulteriormente modificato

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

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

Tutti i puntatori sopra avranno 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 terminazione NUL / 0 aggiuntivo dopo il contenuto della stringa assegnato in modo esplicito (ad esempio std::string("this\0that", 9), avrà un buffer "this\0that\0").

Dato uno dei puntatori sopra:

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

Solo per il non constpuntatore p_writable_datae da &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

Scrivi NUL altrove nella stringa non non cambia il strings' size(); stringpossono contenere un numero qualsiasi di NUL - non ricevono alcun trattamento speciale da std::string(lo stesso in C ++ 03).

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

  • 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 valori non inizializzati o di immondizia, con accessi accidentali ad esso con comportamento indefinito ).
      • x.size()i personaggi sono sicuri da leggere, cioè x[0]attraversox[x.size() - 1]
      • per stringhe vuote, hai la garanzia di un puntatore diverso da NULL a cui è possibile aggiungere in modo sicuro 0 (evviva!), ma non dovresti dedurre tale puntatore.
  • &x[0]

    • per stringhe vuote questo ha un comportamento indefinito (21.3.4)
      • ad es. dato f(const char* p, size_t n) { if (n == 0) return; ...whatever... }che non devi chiamare f(&x[0], x.size());quando x.empty(): basta usare f(x.data(), ...).
    • altrimenti, come da x.data()ma:
      • in caso contrario, const xsi ottiene un non const char*puntatore; puoi sovrascrivere il contenuto delle stringhe
  • x.c_str()

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

Conseguenze dell'accesso a indici legali esterni

Indipendentemente dal modo in cui si ottiene un puntatore, non è necessario accedere alla memoria oltre il puntatore rispetto ai caratteri garantiti presenti nelle descrizioni sopra. I tentativi di farlo hanno un comportamento indefinito , con una reale possibilità di arresti anomali dell'applicazione e risultati di immondizia anche per le letture e inoltre dati all'ingrosso, corruzione dello stack e / o vulnerabilità di sicurezza per le scritture.

Quando vengono invalidati quei puntatori?

Se si chiama una stringfunzione membro che modifica stringo riserva ulteriore capacità, tutti i valori del puntatore restituiti in anticipo da uno dei metodi sopra indicati vengono invalidati . È possibile utilizzare nuovamente questi metodi per ottenere un altro puntatore. (Le regole sono le stesse per gli iteratori in strings).

Vedi anche Come ottenere un puntatore a caratteri valido anche dopo aver xlasciato l'ambito o modificato ulteriormente di seguito ....

Quindi, quale è meglio usare?

Da C ++ 11, utilizzare .c_str()per i dati ASCIIZ e .data()per i dati "binari" (spiegato più avanti).

In C ++ 03, utilizzare a .c_str()meno che non .data()sia adeguato, e preferire .data()oltre &x[0]perché è sicuro per stringhe vuote ....

... cerca di capire il programma abbastanza da usare data()quando appropriato, o probabilmente commetterai altri errori ...

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

Date le .c_str()garanzie di C ++ 03 sul buffer restituito sono un super set di .data(), puoi sempre usare in sicurezza .c_str(), ma le persone a volte no perché:

  • l'utilizzo .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 realmente testuale)), o che lo stai passando a un'altra funzione che lo considera come un blocco di dati "binari". Questa può essere una visione cruciale per garantire che le modifiche al codice di altri programmatori continuino a gestire correttamente i dati.
  • Solo C ++ 03: esiste una leggera possibilità che l' stringimplementazione necessiti di allocazione di memoria aggiuntiva e / o copia dei dati per preparare il buffer terminato NUL

Come ulteriore suggerimento, se i parametri di una funzione richiedono il ( 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 deve sapere dove il testo termina in qualche modo, quindi se non lo è un parametro separato può essere solo una convenzione come un prefisso di lunghezza o sentinella o una lunghezza prevista fissa).

Come ottenere un puntatore a caratteri valido anche dopo aver xlasciato l'ambito o modificato ulteriormente

Dovrai copiare il contenuto di string xuna nuova area di memoria all'esterno x. Questo buffer esterno potrebbe trovarsi in molti punti come un altro stringo una variabile di array di caratteri, potrebbe avere o meno una durata diversa da quella xdi essere in un ambito diverso (ad esempio spazio dei nomi, globale, statico, heap, memoria condivisa, file mappato in memoria) .

Per copiare il testo da std::string xin una matrice 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 volere un char*o const char*generato da unstring

Quindi, sopra hai visto come ottenere un ( const) char*e come rendere una copia del testo indipendente dall'originale string, ma cosa puoi farci ? Una sbandata casuale di esempi ...

  • dare accesso al codice "C" al testo del C ++ string, come inprintf("x is '%s'", x.c_str());
  • copia xil testo in un buffer specificato dal chiamante della tua funzione (ad es. strncpy(callers_buffer, callers_buffer_size, x.c_str())) o memoria volatile utilizzata per l'I / O del dispositivo (ad es. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • aggiungi xil testo a una matrice di caratteri che contiene già del testo ASCIIZ (ad es. strcat(other_buffer, x.c_str())) - fai attenzione a non sovraccaricare il buffer (in molte situazioni potresti dover usare strncat)
  • restituire una const char*o char*da una funzione (forse per motivi storici - il client utilizza l'API esistente - o per compatibilità C non si desidera restituire una std::string, ma si desidera copiare i propri stringdati da qualche parte per il chiamante)
    • fare attenzione a non restituire un puntatore che può essere dereferenziato dal chiamante dopo una stringvariabile locale a cui quel puntatore ha puntato ha lasciato l'ambito
    • alcuni progetti con oggetti condivisi compilati / collegati per diverse std::stringimplementazioni (ad es. STLport e nativo del compilatore) possono trasmettere dati come ASCIIZ per evitare conflitti

4
Ben fatto. Un altro motivo per volere un carattere * (non const) è quello di operare con la trasmissione MPI. Sembra più bello se non devi copiare avanti e indietro. Personalmente avrei offerto un carattere char * const alla stringa. Puntatore costante, ma stringa modificabile. Anche se potrebbe aver incasinato la conversione implicita da const char * a string ...
bartgol

33

Utilizzare il .c_str()metodo per const char *.

Puoi usare &mystring[0]per ottenere un char *puntatore, ma ci sono un paio di gotcha: non otterrai necessariamente una stringa con terminazione zero e non sarai in grado di cambiare la dimensione della stringa. In particolare devi stare attento a non aggiungere caratteri oltre la fine della stringa o otterrai un sovraccarico del buffer (e probabile arresto anomalo).

Non vi era alcuna garanzia che tutti i personaggi facessero parte dello stesso buffer contiguo fino a C ++ 11, ma in pratica tutte le implementazioni conosciute std::stringfunzionavano comunque in quel modo; vedi “& s [0]” punta a caratteri contigui in una stringa std ::? .

Si noti che molte stringfunzioni membro riallocano il buffer interno e invalidano tutti i puntatori che è possibile salvare. Meglio usarli immediatamente e poi scartarli.


1
dovresti notare che data () restituisce const char * :) quello che vuoi dire è & str [0], che restituisce una stringa terminata nulla contigua, ma non necassaria.
Johannes Schaub - litb

1
@litb, Argh! Questo è quello che ottengo provando a dare una rapida risposta. Ho usato la tua soluzione in passato, non so perché non è stata la prima cosa che mi è venuta in mente. Ho modificato la mia risposta.
Mark Ransom,

2
Tecnicamente, l'archiviazione std :: string sarà contigua solo in C ++ 0x.
MSalters l'

1
@MSalters, grazie - Non lo sapevo. Sarebbe difficile trovare un'implementazione in cui non è stato il caso, però.
Mark Ransom,

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

21

C ++ 17

C ++ 17 (prossimo standard) modifica la sinossi del modello basic_stringaggiungendo un sovraccarico non const di data():

charT* data() noexcept;

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


CharT const * a partire dal std::basic_string<CharT>

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

CharT * a partire dal std::basic_string<CharT>

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

C ++ 11

CharT const * a partire dal std::basic_string<CharT>

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

CharT * a partire dal std::basic_string<CharT>

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

  1. Gli oggetti char-like in un basic_stringoggetto devono essere memorizzati contigui. Cioè, per qualsiasi basic_stringoggetto s, l'identità &*(s.begin() + n) == &*s.begin() + ndeve valere per tutti i valori di ntale 0 <= n < s.size().

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

    Restituisce: *(begin() + pos)if pos < size(), altrimenti un riferimento a un oggetto di tipo CharTcon 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 ciascuno iin [0,size()].

Esistono diversi modi per ottenere un puntatore di carattere non const.

1. Utilizzare l'archiviazione contigua di C ++ 11

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

professionista

  • Semplice e breve
  • Veloce (solo metodo senza copia coinvolta)

Contro

  • Final '\0'non deve essere modificato / non 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 copia stringa

3. Usa std::array<CharT, N>se Nè costante 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
  • Gestione della memoria dello stack

Contro

  • Statico
  • Richiede copia stringa

4. Allocazione di memoria grezza 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

  • Ingombro di memoria ridotto
  • Cancellazione automatica
  • Semplice

Contro

  • Richiede copia stringa
  • Statico (l'utilizzo dinamico richiede molto più codice)
  • Meno funzioni rispetto a vettori o array

5. Allocazione di memoria grezza 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 copia stringa
  • Massima responsabilità / suscettibilità per errori
  • Complesso

9

Sto lavorando con un'API con molte funzioni come input a char*.

Ho creato una piccola classe per affrontare questo tipo di problema, ho implementato il linguaggio RAII.

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

E puoi usarlo come:

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

Ho chiamato la classe DeepStringperché sta creando una copia profonda e univoca ( DeepStringnon è copiabile) di una stringa esistente.


3
Eviterei questa convenzione di denominazione. c_str()come usato da stdè un'abbreviazione per "C-string" non "const string" e str()restituisce sempre a std::basic_string, non char*(per esempio std::stringstream::str())
bcrist

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

1
sembra elegante ma davvero difficile da capire ... Semplice è il migliore IMO
Naeem A. Malik,

4
strcpy (), malloc (), length () e c_str () sono funzioni di base e non c'è nulla di difficile in questo. Basta allocare memoria e copiare.
cegprakash,

5
sì, le funzioni sono basilari, ma le hai distorte e piegate per sembrare una ciotola di spaghetti o una fodera del mostro di Frankenstein :)
Naeem A. Malik,

4
Sì, le funzioni sono basilari ma ... ti sei ricordato quando hai iniziato a gestire un linguaggio di programmazione? Qualche riga in più da spiegare e aiuterà davvero un neofita a capire perché, ad esempio, è diverso o migliore di questa risposta :)
Hastur,

2
@cegprakash: ogni volta che c'è un malloc (), deve esserci anche un free (). Altrimenti il ​​codice perde memoria, e così fa la soluzione nella tua risposta. Allocare memoria senza almeno accennare alla deallocazione richiesta è una cattiva pratica per tali domande.
Striezel,

7

Guarda questo:

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

Tuttavia, si noti che questo restituirà a const char *.

Per a char *, usare strcpyper copiarlo in un altro chararray.


23
Ciao, ciò che hai pubblicato è già stato detto più volte, con maggiori dettagli, in altre risposte alla domanda di 5 anni. Va bene rispondere a domande più vecchie, ma solo se aggiungi nuove informazioni. Altrimenti, è solo rumore.
Mat

7
Personalmente, apprezzo la semplicità.
TankorSmash,

-4

Prova questo

std::string s(reinterpret_cast<const char *>(Data), Size);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.