Comment puis-je convertir une std::string
en un char*
ou un const char*
?
Si vous voulez simplement passer une std::string
à une fonction qui a besoin de const char*
, vous pouvez utiliser
std::string str;
const char * c = str.c_str();
Si vous voulez obtenir une copie inscriptible, comme char *
, vous pouvez le faire avec ceci :
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;
Edit : Remarquez que ce qui précède n'est pas sans danger pour les exceptions. Si quelque chose entre l'appel new
et l'appel delete
se lève, vous allez perdre de la mémoire, car rien n'appellera delete
pour vous automatiquement. Il y a deux façons immédiates de résoudre ce problème.
Boost::scoped_array` supprimera la mémoire pour vous lorsque vous sortirez du champ d'application :
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
C'est la méthode standard (elle ne nécessite aucune bibliothèque externe). Vous utilisez std::vector
, qui gère entièrement la mémoire pour vous.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Étant donné que...
std::string x = "hello";
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...
Tous les pointeurs ci-dessus contiennent la même valeur - l'adresse du premier caractère du tampon. Même une chaîne vide a un "premier caractère dans le tampon" ;, parce que C++11 garantit de toujours garder un caractère de terminaison NUL/0 supplémentaire après le contenu de la chaîne explicitement assigné (par exemple, std::string("this\0that" ;, 9)
aura un tampon contenant "this\0that\0"
).
Étant donné l'un des pointeurs ci-dessus :
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Seulement pour le pointeur non const
p_writable_data
et à partir de &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
L'écriture d'un NUL ailleurs dans la chaîne de caractères ne modifie pas la taille de la chaîne de caractères. Les chaînes de caractères peuvent contenir n'importe quel nombre de NULs - elles ne reçoivent aucun traitement spécial de la part de std::string
(comme en C++03).
En C++03, les choses étaient considérablement plus compliquées (les principales différences sont soulignées***) :
x.data()
const char*
au tampon interne de la chaîne de caractères qui n'était pas tenu par la norme de se terminer par un NUL (c'est-à-dire qu'il pouvait être ['h' ;, 'e' ;, 'l' ;, 'l' ;, 'o' ;]
suivi de valeurs non initialisées ou inutilisables, les accès accidentels à ces valeurs ayant un comportement non défini).x.size()
sont sûrs à lire, c'est-à-dire de x[0]
à x[x.size() - 1]
.&x[0]
f(const char* p, size_t n) { if (n == 0) return ; ...whatever... }
vous ne devez pas appeler f(&x[0], x.size());
lorsque x.empty()
- utilisez simplement f(x.data(), ...)
.x.data()
mais :x
non const
, cela donne un pointeur char*
non const
; vous pouvez écraser le contenu des chaînes de caractèresx.c_str()
const char*
vers une représentation ASCIIZ (terminaison NUL) de la valeur (c'est-à-dire ['h' ;, 'e' ;, 'l' ;, 'l' ;, 'o' ;, '\0' ;]).x.data()
et &x[0]
.x.size()
+ 1 caractères est sûre.string
qui modifie la string
ou réserve une capacité supplémentaire, toutes les valeurs de pointeur renvoyées auparavant par l'une des méthodes ci-dessus sont invalidées. Vous pouvez utiliser à nouveau ces méthodes pour obtenir un autre pointeur. (Les règles sont les mêmes que pour les itérateurs dans les string
s).
Voir aussi Comment obtenir un pointeur de caractère valide même après que x
ait quitté le champ d'application ou ait été modifié ultérieurement ci-dessous.....
Alors, qu'est-ce qui est le meilleur à utiliser ?Depuis C++11, utilisez .c_str()
pour les données ASCIIZ, et .data()
pour les données "binaires" ; (expliqué plus loin).
En C++03, utilisez .c_str()
à moins d'être certain que .data()
est adéquat, et préférez .data()
à &x[0]
car il est sûr pour les chaînes vides.....
...essayez de comprendre suffisamment le programme pour utiliser data()
quand c'est approprié, ou vous ferez probablement d'autres erreurs....
Le caractère ASCII NUL '\0' ; garanti par .c_str()
est utilisé par de nombreuses fonctions comme une valeur sentinelle indiquant la fin des données pertinentes et sûres d'accès. Cela s'applique à la fois aux fonctions exclusivement C++ comme fstream::fstream(const char* filename, ...)
et aux fonctions partagées avec le C comme strchr()
, et printf()
.
Étant donné que les garanties du C++03's .c_str()
'concernant le tampon retourné sont un super-ensemble de .data()
's, vous pouvez toujours utiliser sans risque .c_str()
, mais les gens ne le font parfois pas parce que :
.data()
communique aux autres programmeurs qui lisent le code source que les données ne sont pas ASCIIZ (vous utilisez plutôt la chaîne pour stocker un bloc de données (qui parfois n'est même pas vraiment textuel)), ou que vous la passez à une autre fonction qui la traite comme un bloc de données "binaires" ;. Cela peut être un élément crucial pour s’assurer que les changements de code des autres programmeurs continuent à traiter les données correctement.string
doive faire une allocation de mémoire supplémentaire et/ou copier des données afin de préparer le tampon terminé par NUL.
Une autre astuce : si les paramètres d'une fonction requièrent le (const
) char*
mais n'insistent pas pour obtenir x.size()
, la fonction a probablement besoin d'une entrée ASCIIZ, donc .c_str()
est un bon choix (la fonction doit savoir où le texte se termine d'une manière ou d'une autre, donc si ce n'est pas un paramètre séparé, cela ne peut être qu'une convention comme un préfixe de longueur ou une sentinelle ou une longueur fixe attendue).
Comment faire en sorte qu'un pointeur de caractère soit valide même après que x
ait quitté la portée ou qu'il ait été modifié à nouveau ?Vous devrez copier le contenu de la chaîne
x
dans une nouvelle zone mémoire en dehors de x
. Ce tampon externe peut se trouver à plusieurs endroits, comme une autre string
ou une variable de tableau de caractères, il peut ou non avoir une durée de vie différente de celle de x
parce qu'il se trouve dans une portée différente (par exemple, espace de noms, global, statique, tas, mémoire partagée, fichier mappé en mémoire).
Pour copier le texte de std::string x
dans un tableau de caractères indépendant :
// 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*
ou un const char*
généré à partir d'une string
Donc, ci-dessus vous avez vu comment obtenir un char*
(const
), et comment faire une copie du texte indépendant de la string
originale, mais que pouvez-vous faire avec ? Quelques exemples au hasard...
chaîne
C++, comme dans printf("x is '%s'" ;, x.c_str());
x
'dans un tampon spécifié par l'appelant de votre fonction (par exemple strncpy(callers_buffer, callers_buffer_size, x.c_str())
), ou une mémoire volatile utilisée pour les E/S de périphériques (par exemple for (const char* p = x.c_str() ; *p ; ++p) *p_device = *p;
)x
's à un tableau de caractères contenant déjà du texte ASCIIZ (par exemple, strcat(other_buffer, x.c_str())
) - attention à ne pas dépasser le tampon (dans de nombreuses situations, vous devrez utiliser strncat
)const char*
ou un char*
à partir d'une fonction (peut-être pour des raisons historiques - le client utilise votre API existante - ou pour des raisons de compatibilité C, vous ne voulez pas retourner un std::string
, mais vous voulez copier les données de votre string
'quelque part pour l'appelant)string
sur laquelle ce pointeur pointait ait quitté la portée.std::string
(par exemple STLport et compilateur-natif) peuvent passer les données en ASCIIZ pour éviter les conflits.Utilisez la méthode .c_str()
pour const char *
.
Vous pouvez utiliser &mystring[0]
pour obtenir un pointeur char *
, mais il y a quelques problèmes : vous n'obtiendrez pas nécessairement une chaîne à terminaison zéro, et vous ne pourrez pas changer la taille de la chaîne. Vous devez surtout faire attention à ne pas ajouter de caractères après la fin de la chaîne, sinon vous obtiendrez un dépassement de tampon (et un plantage probable).
Il n'y avait aucune garantie que tous les caractères feraient partie du même tampon contigu jusqu'à C++11, mais en pratique, toutes les implémentations connues de std::string
fonctionnent de cette façon de toute façon ; voir Does "&s[0]" point to contiguous characters in a std::string ?.
Notez que de nombreuses fonctions membres de string
réalloueront le tampon interne et invalideront les pointeurs que vous auriez pu sauvegarder. Le mieux est de les utiliser immédiatement puis de les jeter.