Come posso convertire una std::string
in un char*
o un const char*
?
Se volete solo passare una std::string
a una funzione che ha bisogno di const char*
potete usare
std::string str;
const char * c = str.c_str();
Se volete ottenere una copia scrivibile, come char *
, potete 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: Notate che quanto sopra non è sicuro delle eccezioni. Se qualcosa tra la chiamata new
e la chiamata delete
lancia, perderai memoria, poiché niente chiamerà delete
per te automaticamente. Ci sono due modi immediati per risolvere questo problema.
boost::scoped_array
boost::scoped_array` cancellerà la memoria per voi quando uscirete dallo scope:
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). Usate std::vector
, che gestisce completamente la memoria per voi.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Dato dire...
std::string x = "hello";
Ottenere un char *
o const char*
da una stringa
Come ottenere un puntatore a carattere che sia valido mentre x
rimane nello scope e non viene ulteriormente modificato
C++11 semplifica le cose; i seguenti danno tutti accesso allo stesso buffer di stringa 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 di cui sopra terranno lo stesso valore - l'indirizzo del primo carattere nel buffer. Anche una stringa vuota ha un "primo carattere nel buffer", perché il C++11 garantisce di mantenere sempre un carattere terminatore NUL/0 extra dopo il contenuto della stringa esplicitamente assegnato (ad esempio std::string("questo\0quello", 9)
avrà un buffer che contiene "questo\0quello"
).
Dato uno qualsiasi dei puntatori di cui 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 puntatore non const
p_writable_data
e 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
Scrivere un NUL altrove nella stringa non cambia la stringa
's size()
; le stringhe
'possono contenere qualsiasi numero 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 highlighted):
x.data()
const char*
al buffer interno della stringa che non era richiesto dallo Standard per concludere con un NUL (cioè poteva essere ['h', 'e', 'l', 'l', 'l', 'o']
seguito da valori non inizializzati o garbage, con accessi accidentali che avevano un comportamento non definito).x.size()
sono sicuri da leggere, cioè da x[0]
a x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...qualunque cosa... }
non dovete chiamare f(&x[0], x.size());
quando x.empty()
- usate semplicemente f(x.data(), ...)
.x.data()
ma:const
x
questo produce un puntatore non-const
char*
; potete sovrascrivere il contenuto della stringax.c_str()
const char*
ad una rappresentazione ASCIIZ (NUL-terminata) del valore (cioè ['h', 'e', 'l', 'l', 'o', '0']).x.data()
e &x[0]
x.size()
+ 1 caratteri sono sicuri da leggere.stringa
che modifica la stringa
o riserva ulteriore capacità, qualsiasi valore di puntatore restituito in precedenza da uno qualsiasi dei metodi di cui sopra è invalidato. Potete usare di nuovo quei metodi per ottenere un altro puntatore. (Le regole sono le stesse degli iteratori nelle stringhe
).
Vedi anche Come ottenere un puntatore a carattere valido anche dopo che x
lascia lo scope o viene modificato ulteriormente sotto....
Quindi, qual è meglio da usare?Dal C++11, usate .c_str()
per dati ASCIIZ, e .data()
per dati "binari" (spiegato più avanti).
In C++03, usate .c_str()
a meno che non siate certi che .data()
sia adeguato, e preferite .data()
rispetto a &x[0]
poiché è sicuro per le stringhe vuote....
...cercate di capire il programma abbastanza da usare data()
quando è appropriato, o probabilmente farete altri errori...
Il carattere ASCII NUL '\0' garantito da .c_str()
è usato da molte funzioni come valore sentinella che denota la fine dei dati rilevanti e sicuri per l'accesso. Questo si applica sia alle funzioni C++-only come ad esempio fstream::fstream(const char* filename, ...)
che alle funzioni condivise con C come strchr()
, e printf()
.
Dato che C++03's .c_str()
's garanzie sul buffer restituito sono un super-insieme di .data()
's, si può sempre usare tranquillamente .c_str()
, ma la gente a volte non lo fa perché:
.data()
si comunica agli altri programmatori che leggono il codice sorgente che i dati non sono ASCIIZ (piuttosto, si sta usando la stringa per memorizzare un blocco di dati (che a volte non è nemmeno realmente testuale)), o che la si sta passando ad un'altra funzione che la tratta come un blocco di dati "binari". Questo può essere un'intuizione cruciale per assicurare che gli altri programmatori continuino a gestire correttamente i dati.stringa
abbia bisogno di fare qualche allocazione di memoria extra e/o copia di dati per preparare il buffer terminato con 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 termina il testo in qualche modo, quindi se non è un parametro separato può essere solo una convenzione come un prefisso di lunghezza o una sentinella o qualche lunghezza fissa prevista).
Come ottenere un puntatore a carattere valido anche dopo che x
lascia lo scope o viene ulteriormente modificatoAvrete bisogno di copiare il contenuto della stringa
x
in una nuova area di memoria esterna a x
. Questo buffer esterno potrebbe essere in molti posti come un'altra stringa
o una variabile dell'array di caratteri, potrebbe o meno avere una durata diversa da x
a causa dell'essere in uno scopo diverso (ad esempio namespace, globale, statico, heap, memoria condivisa, file mappato in memoria).
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);
char*
o const char*
generato da una `stringaQuindi, sopra avete visto come ottenere un char*
(const
), e come fare una copia del testo indipendente dalla stringa
originale, ma cosa si può fare con esso? Un'infarinatura casuale di esempi...
stringa
del C++, come in printf("x è '%s'", x.c_str());
x
in un buffer specificato dal chiamante della funzione (ad esempio, strncpy(callers_buffer, callers_buffer_size, x.c_str())
), o nella memoria volatile usata per l'I/O del dispositivo (ad esempio, for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
'a un array di caratteri che contiene già del testo ASCIIZ (ad es. strcat(other_buffer, x.c_str())
) - fare attenzione a non superare il buffer (in molte situazioni potrebbe essere necessario usare strncat
)const char*
o char*
da una funzione (forse per ragioni storiche - il cliente usa la vostra API esistente - o per compatibilità C non volete restituire una std::string
, ma volete copiare i dati della vostra string
da qualche parte per il chiamante)string
a cui puntava quel puntatore ha lasciato lo scopestd::string
(ad esempio STLport e compiler-native) possono passare i dati come ASCIIZ per evitare conflittiUsate il metodo .c_str()
per il const char *
.
Potete usare &mystring[0]
per ottenere un puntatore a char *
, ma ci sono un paio di intoppi: non otterrete necessariamente una stringa con terminazione zero, e non sarete in grado di cambiare la dimensione della stringa. In particolare bisogna fare attenzione a non aggiungere caratteri oltre la fine della stringa o si otterrà un buffer overrun (e probabile crash).
Non c'era alcuna garanzia che tutti i caratteri facessero parte dello stesso buffer contiguo fino al C++11, ma in pratica tutte le implementazioni note di std::string
funzionavano comunque in questo modo; vedi Does "&s[0]" point to contiguous characters in a std::string?.
Notate che molte funzioni membro di string
riallocano il buffer interno e invalidano qualsiasi puntatore che potreste aver salvato. Meglio usarle immediatamente e poi scartarle.