Comment convertir un std::string en const char* ou char*?
Comment puis-je convertir un std::string
en un char*
ou un const char*
?
8 réponses
si vous voulez simplement passer un std::string
à une fonction qui nécessite 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()] = '"151910920"'; // don't forget the terminating 0
// don't forget to free the string after finished using it
delete[] writable;
Edit : notez que ce qui précède n'est pas une exception sûre. Si quelque chose entre l'appel new
et l'appel delete
sort, vous perdrez de la mémoire, car rien appellera delete
pour vous automatiquement. Il y a deux façons de résoudre ce problème.
boost:: scoped_array
boost::scoped_array
supprimera la mémoire pour vous en sortant de portée:
std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '"151920920"'; // 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
c'est la méthode standard (ne nécessite pas de bibliothèque externe). Vous utilisez std::vector
, qui gère complètement de la mémoire pour vous.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('"151930920"');
// get the char* using &writable[0] or &*writable.begin()
donné say...
std::string x = "hello";
l'Obtention d'un `char *` ou de `const char*` d'un `string`
Comment obtenir un pointeur de caractère valide alors que x
reste dans la portée et n'est pas modifié plus loin
C++11 simplifie les choses; les suivants donnent tous accès au même tampon de chaîne interne:
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...
Tout ce qui précède les pointeurs tiendront la même valeur - l'adresse du premier caractère dans le tampon. Même une chaîne vide a un "premier caractère dans le tampon", parce que C++11 garantit de garder toujours un caractère de terminateur NUL/0 supplémentaire après le contenu de chaîne explicitement assigné (par exemple std::string("this"151960920"that", 9)
aura un tampon contenant "this"151970920"that"151970920""
).
compte tenu de l'un des points ci-dessus:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
uniquement pour les non - const
pointeur de &x[0]
:
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Écrire un NUL ailleurs dans la chaîne ne pas changer le string
's size()
; string
's sont autorisés à contenir un nombre quelconque de NULs - ils sont donnés aucun traitement spécial par std::string
(même en C++03).
dans c++03 , les choses étaient beaucoup plus compliquées (différences clés mises en évidence ):
-
x.data()
- retourne
const char*
au tampon interne de la chaîne "qui n'était pas requis par la norme pour conclure avec un NUL (i.e. pourrait être['h', 'e', 'l', 'l', 'o']
suivi de valeurs non initialisées ou de déchets, avec des accès accidentels à celui-ci ayant comportement non défini ).-
x.size()
les caractères sont faciles à lire, c.-à-d.x[0]
àx[x.size() - 1]
- pour les chaînes vides, vous êtes assurés de pouvoir ajouter 0 en toute sécurité (hourra!), mais vous ne devriez pas déréférence ce pointeur.
-
- retourne
-
&x[0]
- pour vide les chaînes de ce qui a un comportement indéterminé (21.3.4)
- p.ex. donné
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
vous ne devez pas appelerf(&x[0], x.size());
quandx.empty()
- il suffit d'utiliserf(x.data(), ...)
.
- p.ex. donné
- autrement, selon
x.data()
mais:- non
const
x
cela aboutit à un non-const
char*
pointeur; vous pouvez remplacer la chaîne de contenu
- non
- pour vide les chaînes de ce qui a un comportement indéterminé (21.3.4)
-
x.c_str()
- retourne
const char*
à une représentation ASCIIZ (nul-terminated) de la valeur (i.e. ['h', 'e', 'l', 'L', 'o', '\0']). - bien que peu ou aucune implémentation n'ait choisi de le faire, la norme C++03 a été formulée pour permettre à l'implémentation de la chaîne de caractères la liberté de créer un tampon distinct nul-terminated à la volée , potentiellement non-NUL résilié tampon "exposé" par
x.data()
et&x[0]
-
x.size()
+ 1 caractères peuvent être lus en toute sécurité. - garanti sûr, même pour les cordes vides (['\0']).
- retourne
Conséquences de l'accès à l'extérieur juridique indices
pointeur, vous ne devez pas accéder à la mémoire plus loin du pointeur que les caractères garantis présents dans les descriptions ci-dessus. Les tentatives de le faire ont comportement non défini , avec un risque très réel d'accidents d'application et des résultats de déchets, même pour les lectures, et en outre des données de gros, la corruption de pile et/ou des vulnérabilités de sécurité pour les Écritures.
quand est-ce que ces pointeurs sont invalidés?
Si vous appelez string
fonction de membre qui modifie la string
ou réserve plus de capacité, toutes les valeurs de pointeur retournées au préalable par l'une des méthodes ci-dessus sont invalidé . Vous pouvez utiliser ces méthodes pour obtenir un autre pointeur. (Les règles sont les mêmes que pour les itérateurs dans string
s).
Voir aussi Comment obtenir un pointeur de caractère valide même après x
champ d'application de feuilles ou est de nouveau modifié dessous....
donc, qui est meilleur à utiliser?
à Partir de C++11, utiliser .c_str()
pour ASCIIZ de données, et .data()
pour "binaire" des données (expliqué ci-dessous).
En C++03, utiliser .c_str()
à moins certain que la .data()
est adéquate, et préfèrent .data()
plus &x[0]
comme il est sans danger pour les cordes à vide....
...essayez de comprendre le programme assez pour utiliser data()
le cas échéant, ou vous allez probablement faire d'autres erreurs...
le caractère ASCII NUL '\0' garanti par .c_str()
est utilisé par de nombreuses fonctions comme valeur sentinelle dénotant la fin des données pertinentes et sûres d'accès. Cela s'applique à la fois aux fonctions C++comme say fstream::fstream(const char* filename, ...)
et aux fonctions partagées avec C comme strchr()
et printf()
.
étant donné les garanties de C++03 .c_str()
A propos du buffer retourné sont un super-ensemble de .data()
's, vous pouvez toujours utiliser en toute sécurité .c_str()
, mais les gens ne le font pas parfois parce que:
- en utilisant
.data()
communique à d'autres programmeurs lisant le code source que les données ne sont pas ASCIIZ (plutôt, vous utilisez la chaîne de caractères pour stocker un bloc de données (qui parfois n'est même pas vraiment textuel)), ou que vous le passez à une autre fonction qui le traite comme un bloc de données "binaires". Cela peut être un aperçu crucial pour s'assurer que les changements de code d'autres programmeurs continuent à traiter les données correctement. - C++03 seulement: il y a une légère chance que votre implémentation
string
ait besoin de faire une allocation de mémoire supplémentaire et/ou une copie de données pour préparer le tampon nul terminé
comme indice supplémentaire, si les paramètres d'une fonction nécessitent le ( const
) char*
mais n'insistez pas pour obtenir x.size()
, la fonction probablement nécessite une entrée ASCIIZ, donc .c_str()
est un bon choix (la fonction doit savoir où le texte se termine d'une façon ou d'une autre, donc si ce n'est pas un paramètre séparé, il ne peut s'agir que d'une convention comme un préfixe de longueur ou une longueur attendue fixe).
comment obtenir un pointeur de caractère valide même après x
sort du champ d'application ou est modifié plus loin
vous aurez besoin de copie le contenu de la string
x
à une nouvelle zone de mémoire à l'extérieur x
. Cette mémoire tampon externe peut être à de nombreux endroits comme une autre variable string
ou un tableau de caractères, elle peut avoir ou ne pas avoir une durée de vie différente de x
en raison d'être dans une portée différente (par exemple namespace, global, static, heap, mémoire partagée, mémoire cartographiée).
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"151940920"cd" -> 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"151940920"->Hel"151940920")
char y[N + 1];
strncpy(y, x.c_str(), N); // copy at most N, zero-padding if shorter
y[N] = '"151940920"'; // 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);
autres raisons de vouloir un char*
ou const char*
généré à partir d'un string
donc , ci-dessus vous avez vu comment obtenir un ( const
) char*
, et comment faire une copie du texte indépendant de l'original string
, mais que pouvez-vous faire avec elle? Aléatoire d'une poignée d'exemples...
- donnez le code" C" accès au texte de C++
string
, comme dansprintf("x is '%s'", x.c_str());
- copier "le texte de 151950920" dans un tampon spécifié par l'appelant de votre fonction( par exemple
strncpy(callers_buffer, callers_buffer_size, x.c_str())
), ou mémoire volatile utilisée pour l'entrée / sortie du périphérique (par exemplefor (const char* p = x.c_str(); *p; ++p) *p_device = *p;
) - ajoute "le texte de 151950920" à 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 peut-être utiliserstrncat
) - retourner un
const char*
ouchar*
à partir d'une fonction (peut - être pour des raisons historiques - client utilisant votre API existante-ou pour la compatibilité C vous ne voulez pas retourner unstd::string
, mais voulez copier les données de votrestring
quelque part pour l'appelant)- attention de ne pas retourner un pointeur qui pourrait être déréférencé par l'appelant après une variable locale
string
à laquelle ce pointeur pointé a laissé une portée - certains projets avec des objets partagés compilés / liés pour différentes implémentations
std::string
(par exemple STLport et compilateur-natif) peuvent passer des données ASCIIZ pour éviter des conflits
- attention de ne pas retourner un pointeur qui pourrait être déréférencé par l'appelant après une variable locale
utilisez la méthode .c_str()
pour const char *
.
vous pouvez utiliser &mystring[0]
pour obtenir un pointeur char *
, mais il y a un couple de gotcha: vous n'obtiendrez pas nécessairement une chaîne terminée à 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 de caractères ou vous obtiendrez un dépassement de tampon (et un crash probable).
il n'y avait aucune garantie que tous les caractères ferait partie du même buffer contigu jusqu'à C++11, mais en pratique toutes les implémentations connues de std::string
fonctionnaient de toute façon de cette façon; voir est-ce que "&s[0]" pointe vers des caractères contigus dans une chaîne std::? .
notez que de nombreuses fonctions de membre string
vont réattribuer le buffer interne et invalider tous les pointeurs que vous auriez pu enregistrer. Mieux les utiliser immédiatement et puis jetez-la.
C++17
C++17 (norme à venir) change le synopsis du modèle basic_string
ajoutant une surcharge non const de data()
:
charT* data() noexcept;
renvoie: un pointeur p tel que p + i == &opérateur pour chaque i dans [0,size()].
CharT const *
de std::basic_string<CharT>
std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()
CharT *
de std::basic_string<CharT>
std::string str = { "..." };
char * p = str.data();
C++11
CharT const *
de std::basic_string<CharT>
std::string str = { "..." };
str.c_str();
CharT *
de std::basic_string<CharT>
à partir de C++11, la norme dit:
- les objets de type char dans un objet
basic_string
doivent être stockés de façon continue. C'est, pour toutbasic_string
objets
, l'identité&*(s.begin() + n) == &*s.begin() + n
doit être conservée pour toutes les valeurs den
telles que0 <= n < s.size()
.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
renvoie:
*(begin() + pos)
sipos < size()
, sinon une référence à un objet de typeCharT
avec une valeurCharT()
; la valeur référencée ne doit pas être modifiée.
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Retourne: Un pointeur p tel que
p + i == &operator[](i)
pour chaquei
dans[0,size()]
.
il y a plusieurs façons possibles d'obtenir un pointeur de caractère non const.
1. Utiliser le stockage contigu de C++11
std::string foo{"text"};
auto p = &*foo.begin();
Pro
- Simple et court
- Rapide (seule méthode avec aucune copie impliqués)
Cons
- Finale
'"1519360920"'
ne sera pas modifié / ne faisant pas nécessairement partie de la non-const de la mémoire.
2. Use std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
- Simple
- traitement automatique de la mémoire
- dynamique
Cons
- nécessite une copie pour chaîne de caractères
3. Utilisez std::array<CharT, N>
si N
est la constante de temps de compilation (et suffisamment petite)
std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
Pro
- Simple
- Pile de gestion de la mémoire
Cons
- statique
- nécessite une copie de chaîne de caractères
4. Allocation de mémoire brute avec suppression de mémoire automatique
std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
Pro
- petite empreinte mémoire
- suppression automatique
- Simple
Cons
- nécessite une copie pour chaîne de caractères
- Statique (dynamique de l'utilisation nécessite beaucoup plus de code)
- moins de caractéristiques que vecteur ou réseau
5. La mémoire brute affectation avec manutention manuelle
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;
}
Pro
- contrôle maximal "
Con
- nécessite une copie pour chaîne de caractères
- responsabilité maximale / susceptibilité aux erreurs
- complexe
je travaille avec une API avec beaucoup de fonctions obtenir comme entrée un char*
.
j'ai créé une petite classe pour faire face à ce genre de problème, j'ai mis en œuvre l'idiome 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_; }
};
Et vous pouvez l'utiliser comme:
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
j'ai appelé la classe DeepString
parce qu'elle crée une copie profonde et unique (la DeepString
n'est pas copiable) d'une chaîne existante.
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
il suffit de voir ce :
string str1("stackoverflow");
const char * str2 = str1.c_str();
cependant , notez que cela retournera un const char *
.Pour un char *
, utilisez strcpy
pour le copier dans un autre "151940920 tableau.
Essayez cette
std::string s(reinterpret_cast<const char *>(Data), Size);