Au lieu de: char * writable = new char [str.size () + 1]; Vous pouvez utiliser char writable [str.size () + 1]; Ensuite, vous n'avez pas à vous soucier de la suppression de la gestion des écritures ou des exceptions.
7
Vous ne pouvez pas utiliser str.size () à moins que la taille ne soit connue au moment de la compilation, cela pourrait également déborder votre pile si la valeur de taille fixe est énorme.
@cegprakash strcpyet ce mallocn'est pas vraiment la façon C ++.
boycy
4
Non, mais ce char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)serait un langage C ++ plus idiomatique. strcpy()et malloc()ne sont pas faux ou problématiques, mais il semble incohérent d'utiliser une chaîne C ++ et des installations de bibliothèque C avec des équivalents C ++ dans le même bloc de code.
boycy
Réponses:
1057
Si vous voulez simplement passer a std::stringà une fonction qui en a besoin, const char*vous pouvez utiliser
std::string str;constchar* c = str.c_str();
Si vous voulez obtenir une copie accessible en écriture char *, vous pouvez le faire avec ceci:
std::string str;char* writable =newchar[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 itdelete[] writable;
Edit : Notez que ce qui précède n'est pas une exception à la sécurité. Si quelque chose entre l' newappel et l' deleteappel est lancé, vous perdrez de la mémoire, car rien ne vous appellera deleteautomatiquement. Il existe deux façons immédiates de résoudre ce problème.
boost :: scoped_array
boost::scoped_array supprimera la mémoire pour vous lorsque vous serez hors de portée:
std::string str;
boost::scoped_array<char> writable(newchar[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 :: vecteur
Il s'agit de la méthode standard (ne nécessite aucune bibliothèque externe). Vous utilisez std::vector, qui gère complètement 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()
Utilisez simplement char * result = strdup (str.c_str ());
Jasper Bekkers
63
vous pourriez, mais strdup n'est pas une fonction standard ac ou c ++, c'est de posix :)
Johannes Schaub - litb
14
ce que je préférerais probablement en général est std :: vector <char> accessible en écriture (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & inscriptible [0];
Johannes Schaub - litb
17
std :: copy est la manière c ++ de le faire, sans avoir besoin d'accéder au pointeur de chaîne. J'essaie d'éviter d'utiliser autant que possible les fonctions C.
Johannes Schaub - litb
16
Depuis C ++ 17, std::string::data()retourne maintenant un CharT*au lieu d'un const CharT*. Ce pourrait être une bonne idée de mettre à jour cette réponse :)
Rakete1111
192
Étant donné dire ...
std::string x ="hello";
Obtenir un `char *` ou `const char *` à partir d'une `chaîne`
Comment obtenir un pointeur de caractère valide alors qu'il xreste dans la portée et n'est pas modifié davantage
C ++ 11 simplifie les choses; les éléments suivants donnent tous accès au même tampon de chaîne interne:
constchar* p_c_str = x.c_str();constchar* p_data = x.data();char* p_writable_data = x.data();// for non-const x from C++17 constchar* p_x0 =&x[0];char* p_x0_rw =&x[0];// compiles iff x is not const...
Tous les pointeurs ci-dessus contiendront 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", car C ++ 11 garantit de toujours garder un caractère de terminaison NUL / 0 supplémentaire après le contenu de la chaîne explicitement attribué (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()]
Uniquement pour le non constpointeur p_writable_dataet 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 ne change pas le strings size(); stringsont autorisés à contenir un nombre illimité de NUL - ils ne reçoivent aucun traitement spécial parstd::string (idem en C ++ 03).
En C ++ 03 , les choses étaient considérablement plus compliquées (les principales différences ont été 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 (c'est-à-dire qu'il 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 un comportement non défini ).
x.size()les caractères sont sûrs à lire, c.- x[0]à-d.x[x.size() - 1]
pour les chaînes vides, vous avez la garantie d'un pointeur non NULL auquel 0 peut être ajouté en toute sécurité (hourra!), mais vous ne devez pas déréférencer ce pointeur.
&x[0]
pour les chaînes vides, cela a un comportement indéfini (21.3.4)
par exemple, étant donné que f(const char* p, size_t n) { if (n == 0) return; ...whatever... }vous ne devez pas appeler f(&x[0], x.size());quand x.empty()- utilisez simplement f(x.data(), ...).
sinon, selon x.data() mais:
pour non constxcela donne un non constchar*pointeur; vous pouvez remplacer le contenu de la chaîne
x.c_str()
renvoie const char*à une représentation ASCIIZ (terminée par NUL) de la valeur (c'est-à-dire ['h', 'e', 'l', 'l', 'o', '\ 0']).
bien que peu ou pas d'implémentations aient choisi de le faire, la norme C ++ 03 a été formulée pour permettre à l'implémentation de chaîne de créer à la volée un tampon distinct terminé par NUL , à partir du tampon potentiellement non-NUL "exposé" par etx.data()&x[0]
x.size() + 1 caractères sont lisibles en toute sécurité.
sécurité garantie même pour les chaînes vides (['\ 0']).
Conséquences de l'accès à des indices juridiques externes
Quelle que soit la façon dont vous obtenez un 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 un comportement indéfini , avec une chance très réelle de plantages d'application et de résultats inutiles même pour les lectures, et en outre des données en gros, une corruption de pile et / ou des vulnérabilités de sécurité pour les écritures.
Quand ces pointeurs sont-ils invalidés?
Si vous appelez une stringfonction membre qui modifie stringou réserve une capacité supplémentaire, toutes les valeurs de pointeur renvoyées au préalable par l'une des méthodes ci-dessus sont invalidées . Vous pouvez à nouveau utiliser ces méthodes pour obtenir un autre pointeur. (Les règles sont les mêmes que pour les itérateurs en strings).
Voir aussi Comment obtenir un pointeur de caractère valide même après avoir xquitté la portée ou modifié plus loin ci-dessous ....
Alors, quel est le meilleur à utiliser?
A partir de C ++ 11, utilisez .c_str()pour les données ASCIIZ et .data()pour les données "binaires" (expliquées plus loin ci-dessous).
En C ++ 03, utilisez à .c_str()moins que ce .data()soit suffisant, et préférez .data()plutôt &x[0]que c'est sûr pour les chaînes vides ....
... essayez de comprendre suffisamment le programme pour l'utiliser le data()cas échéant, ou vous ferez probablement d'autres erreurs ...
Le caractère ASCII NUL '\ 0' garanti par .c_str()est utilisé par de nombreuses fonctions comme valeur sentinelle indiquant la fin des données pertinentes et sécurisées d'accès. Cela s'applique à la fois aux fonctions C ++ - uniquement comme les fonctions say fstream::fstream(const char* filename, ...)et shared-with-C comme strchr(), etprintf() .
Étant donné que les .c_str()garanties de C ++ 03 sur le tampon retourné sont un super-ensemble de .data(), vous pouvez toujours les utiliser en toute sécurité .c_str(), mais les gens ne le font parfois pas parce que:
l'utilisation .data()communique à d'autres programmeurs en lisant 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 les transmettez à une autre fonction qui le traite comme un bloc de données "binaires". Cela peut être un élément crucial pour garantir que les modifications de code des autres programmeurs continuent de gérer correctement les données.
C ++ 03 uniquement: il est possible que votre stringimplémentation doive effectuer une allocation de mémoire supplémentaire et / ou une copie des données afin de préparer le tampon terminé par NUL
Comme autre indice, si les paramètres d'une fonction nécessitent le ( const) char*mais n'insistent pas pour l'obtenir x.size(), la fonction a probablement besoin d'une entrée ASCIIZ, c'est donc .c_str()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 le cas) un paramètre séparé, il ne peut s'agir que d'une convention comme un préfixe de longueur ou une sentinelle ou une certaine longueur attendue fixe).
Comment obtenir un pointeur de caractère valide même après avoir xquitté la portée ou être modifié davantage
Vous devrez copier le contenu du stringxdans une nouvelle zone de mémoire à l'extérieur x. Ce tampon externe peut être à de nombreux endroits, comme une autre stringvariable de tableau de caractères, il peut ou non avoir une durée de vie différente de celle xdue à être 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 xdans 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) LENGTHchar* 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 SAFETYchar* y =newchar[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 SAFETYchar* y = strdup(x.c_str());// use y...
free(y);
Autres raisons de vouloir un char*ou const char*généré à partir d'unstring
Donc, ci-dessus, vous avez vu comment obtenir un ( const) char*et comment faire une copie du texte indépendamment de l'original string, mais que pouvez-vous en faire ? Une poignée d'exemples aléatoires ...
donne accès au code "C" au stringtexte du C ++ , comme dansprintf("x is '%s'", x.c_str());
copier xle texte 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 du périphérique (par exemple for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
ajouter xle texte à un tableau de caractères contenant déjà du texte ASCIIZ (par exemple strcat(other_buffer, x.c_str())) - attention à ne pas surcharger le tampon (dans de nombreuses situations, vous devrez peut-être utiliser strncat)
renvoyer un const char*ou à char*partir d'une fonction (peut-être pour des raisons historiques - le client utilise votre API existante - ou pour la compatibilité C, vous ne voulez pas retourner un std::string, mais vous voulez copier vos stringdonnées quelque part pour l'appelant)
veillez à ne pas renvoyer un pointeur qui pourrait être déréférencé par l'appelant après qu'une stringvariable locale vers laquelle ce pointeur pointé a laissé la portée
certains projets avec des objets partagés compilés / liés pour différentes std::stringimplémentations (par exemple STLport et natif du compilateur) peuvent transmettre des données en ASCIIZ pour éviter les conflits
Joli. Une autre raison de vouloir un char * (non const) est de fonctionner avec la diffusion MPI. Cela semble plus agréable si vous n'avez pas à copier dans les deux sens. J'aurais personnellement offert un getter char * const à string. Pointeur Const, mais chaîne modifiable. Bien qu'il puisse avoir gâché la conversion implicite de const char * en chaîne ...
bartgol
33
Utilisez la .c_str()méthode pour const char *.
Vous pouvez utiliser &mystring[0]pour obtenir un char *pointeur, mais il y en a deux: vous n'obtiendrez pas nécessairement une chaîne terminée par zéro et vous ne pourrez pas modifier 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 ou 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'au C ++ 11, mais dans la pratique, toutes les implémentations connues de std::stringfonctionnaient de cette façon de toute façon; voir «& s [0]» pointe-t-il vers des caractères contigus dans une chaîne std ::? .
Notez que de nombreuses stringfonctions membres réalloueront le tampon interne et invalideront tous les pointeurs que vous pourriez avoir enregistrés. Mieux vaut les utiliser immédiatement, puis les jeter.
vous devez noter que data () retourne const char * :) ce que vous voulez dire est & str [0], qui retourne une chaîne terminée par un caractère contigu, mais pas nécessaire.
Johannes Schaub - litb
1
@litb, Argh! C'est ce que j'obtiens en essayant de préparer une réponse rapide. J'ai utilisé votre solution dans le passé, je ne sais pas pourquoi ce n'est pas la première chose qui m'est venue à l'esprit. J'ai édité ma réponse.
Mark Ransom
2
Techniquement, le stockage std :: string sera contigu uniquement en C ++ 0x.
MSalters
1
@MSalters, merci - je ne le savais pas. J'aurais du mal à trouver une implémentation où ce n'était pas le cas, cependant.
C ++ 17 (norme à venir) modifie le synopsis du modèle en basic_stringajoutant une surcharge non constante de data():
charT* data() noexcept;
Renvoie: Un pointeur p tel que p + i == & opérateur pour chaque i dans [0, taille ()].
CharT const * de std::basic_string<CharT>
std::string const cstr ={"..."};charconst* 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 basic_stringobjet doivent être stockés de manière contiguë. Autrement dit, pour tout basic_stringobjet s, l'identité &*(s.begin() + n) == &*s.begin() + ndoit être valable pour toutes les valeurs de ce ntype 0 <= n < s.size().
Renvoie: *(begin() + pos)if pos < size(), sinon une référence à un objet de type CharTavec valeur CharT(); la valeur référencée ne doit pas être modifiée.
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 DeepStringparce qu'elle crée une copie profonde et unique (la DeepStringn'est pas copiable) d'une chaîne existante.
J'éviterais cette convention de dénomination. c_str()tel qu'utilisé par stdest une abréviation pour "C-string" pas "const string" et str()renvoie toujours a std::basic_string, not char*(par exemple std::stringstream::str())
bcrist
8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
semble sophistiqué mais vraiment difficile à comprendre ... Simple est le meilleur IMO
Naeem A. Malik
4
strcpy (), malloc (), length () et c_str () sont des fonctions de base et il n'y a rien de compliqué à cela. Il suffit d'allouer de la mémoire et de copier.
cegprakash
5
oui les fonctions sont basiques mais vous les avez tordues et pliées pour ressembler à un bol de spaghetti ou à un paquebot de Frankenstein :)
Naeem A. Malik
4
Oui les fonctions sont basiques mais ... vous rappelez-vous quand vous commencez à traiter avec un langage de programmation? Quelques lignes de plus à expliquer et cela aidera vraiment un néophyte à comprendre pourquoi par exemple est différent ou meilleur que cette réponse :)
Hastur
2
@cegprakash: Chaque fois qu'il y a un malloc (), il doit aussi y avoir un free (). Sinon, le code perd de la mémoire, tout comme la solution dans votre réponse. Allouer de la mémoire sans au moins faire allusion à la désallocation requise est une mauvaise pratique pour de telles questions.
Bonjour, ce que vous avez publié a déjà été dit plusieurs fois, avec plus de détails, dans d'autres réponses à la question de 5 ans. C'est bien de répondre à des questions plus anciennes, mais seulement si vous ajoutez de nouvelles informations. Sinon, c'est juste du bruit.
strcpy
et cemalloc
n'est pas vraiment la façon C ++.char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)
serait un langage C ++ plus idiomatique.strcpy()
etmalloc()
ne sont pas faux ou problématiques, mais il semble incohérent d'utiliser une chaîne C ++ et des installations de bibliothèque C avec des équivalents C ++ dans le même bloc de code.Réponses:
Si vous voulez simplement passer a
std::string
à une fonction qui en a besoin,const char*
vous pouvez utiliserSi vous voulez obtenir une copie accessible en écriture
char *
, vous pouvez le faire avec ceci:Edit : Notez que ce qui précède n'est pas une exception à la sécurité. Si quelque chose entre l'
new
appel et l'delete
appel est lancé, vous perdrez de la mémoire, car rien ne vous appelleradelete
automatiquement. Il existe deux façons immédiates de résoudre ce problème.boost :: scoped_array
boost::scoped_array
supprimera la mémoire pour vous lorsque vous serez hors de portée:std :: vecteur
Il s'agit de la méthode standard (ne nécessite aucune bibliothèque externe). Vous utilisez
std::vector
, qui gère complètement la mémoire pour vous.la source
std::string::data()
retourne maintenant unCharT*
au lieu d'unconst CharT*
. Ce pourrait être une bonne idée de mettre à jour cette réponse :)Étant donné dire ...
Obtenir un `char *` ou `const char *` à partir d'une `chaîne`
Comment obtenir un pointeur de caractère valide alors qu'il
x
reste dans la portée et n'est pas modifié davantageC ++ 11 simplifie les choses; les éléments suivants donnent tous accès au même tampon de chaîne interne:
Tous les pointeurs ci-dessus contiendront 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", car C ++ 11 garantit de toujours garder un caractère de terminaison NUL / 0 supplémentaire après le contenu de la chaîne explicitement attribué (par exemple,
std::string("this\0that", 9)
aura un tampon contenant"this\0that\0"
).Étant donné l'un des pointeurs ci-dessus:
Uniquement pour le non
const
pointeurp_writable_data
et de&x[0]
:L'écriture d'un NUL ailleurs dans la chaîne ne change pas le
string
ssize()
;string
sont autorisés à contenir un nombre illimité de NUL - ils ne reçoivent aucun traitement spécial parstd::string
(idem en C ++ 03).En C ++ 03 , les choses étaient considérablement plus compliquées (les principales différences ont été mises en évidence ):
x.data()
const char*
au tampon interne de la chaîne qui n'était pas requis par la norme pour conclure avec un NUL (c'est-à-dire qu'il 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 un comportement non défini ).x.size()
les caractères sont sûrs à lire, c.-x[0]
à-d.x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
vous ne devez pas appelerf(&x[0], x.size());
quandx.empty()
- utilisez simplementf(x.data(), ...)
.x.data()
mais:const
x
cela donne un nonconst
char*
pointeur; vous pouvez remplacer le contenu de la chaînex.c_str()
const char*
à une représentation ASCIIZ (terminée par NUL) de la valeur (c'est-à-dire ['h', 'e', 'l', 'l', 'o', '\ 0']).x.data()
&x[0]
x.size()
+ 1 caractères sont lisibles en toute sécurité.Conséquences de l'accès à des indices juridiques externes
Quelle que soit la façon dont vous obtenez un 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 un comportement indéfini , avec une chance très réelle de plantages d'application et de résultats inutiles même pour les lectures, et en outre des données en gros, une corruption de pile et / ou des vulnérabilités de sécurité pour les écritures.
Quand ces pointeurs sont-ils invalidés?
Si vous appelez une
string
fonction membre qui modifiestring
ou réserve une capacité supplémentaire, toutes les valeurs de pointeur renvoyées au préalable par l'une des méthodes ci-dessus sont invalidées . Vous pouvez à nouveau utiliser ces méthodes pour obtenir un autre pointeur. (Les règles sont les mêmes que pour les itérateurs enstring
s).Voir aussi Comment obtenir un pointeur de caractère valide même après avoir
x
quitté la portée ou modifié plus loin ci-dessous ....Alors, quel est le meilleur à utiliser?
A partir de C ++ 11, utilisez
.c_str()
pour les données ASCIIZ et.data()
pour les données "binaires" (expliquées plus loin ci-dessous).En C ++ 03, utilisez à
.c_str()
moins que ce.data()
soit suffisant, et préférez.data()
plutôt&x[0]
que c'est sûr pour les chaînes vides ....... essayez de comprendre suffisamment le programme pour l'utiliser le
data()
cas échéant, ou vous ferez probablement d'autres erreurs ...Le caractère ASCII NUL '\ 0' garanti par
.c_str()
est utilisé par de nombreuses fonctions comme valeur sentinelle indiquant la fin des données pertinentes et sécurisées d'accès. Cela s'applique à la fois aux fonctions C ++ - uniquement comme les fonctions sayfstream::fstream(const char* filename, ...)
et shared-with-C commestrchr()
, etprintf()
.Étant donné que les
.c_str()
garanties de C ++ 03 sur le tampon retourné sont un super-ensemble de.data()
, vous pouvez toujours les utiliser en toute sécurité.c_str()
, mais les gens ne le font parfois pas parce que:.data()
communique à d'autres programmeurs en lisant 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 les transmettez à une autre fonction qui le traite comme un bloc de données "binaires". Cela peut être un élément crucial pour garantir que les modifications de code des autres programmeurs continuent de gérer correctement les données.string
implémentation doive effectuer une allocation de mémoire supplémentaire et / ou une copie des données afin de préparer le tampon terminé par NULComme autre indice, si les paramètres d'une fonction nécessitent le (
const
)char*
mais n'insistent pas pour l'obtenirx.size()
, la fonction a probablement besoin d'une entrée ASCIIZ, c'est donc.c_str()
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 le cas) un paramètre séparé, il ne peut s'agir que d'une convention comme un préfixe de longueur ou une sentinelle ou une certaine longueur attendue fixe).Comment obtenir un pointeur de caractère valide même après avoir
x
quitté la portée ou être modifié davantageVous devrez copier le contenu du
string
x
dans une nouvelle zone de mémoire à l'extérieurx
. Ce tampon externe peut être à de nombreux endroits, comme une autrestring
variable de tableau de caractères, il peut ou non avoir une durée de vie différente de cellex
due à être 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:Autres raisons de vouloir un
char*
ouconst char*
généré à partir d'unstring
Donc, ci-dessus, vous avez vu comment obtenir un (
const
)char*
et comment faire une copie du texte indépendamment de l'originalstring
, mais que pouvez-vous en faire ? Une poignée d'exemples aléatoires ...string
texte du C ++ , comme dansprintf("x is '%s'", x.c_str());
x
le texte dans un tampon spécifié par l'appelant de votre fonction (par exemplestrncpy(callers_buffer, callers_buffer_size, x.c_str())
), ou une mémoire volatile utilisée pour les E / S du périphérique (par exemplefor (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
le texte à un tableau de caractères contenant déjà du texte ASCIIZ (par exemplestrcat(other_buffer, x.c_str())
) - attention à ne pas surcharger le tampon (dans de nombreuses situations, vous devrez peut-être utiliserstrncat
)const char*
ou àchar*
partir d'une fonction (peut-être pour des raisons historiques - le client utilise votre API existante - ou pour la compatibilité C, vous ne voulez pas retourner unstd::string
, mais vous voulez copier vosstring
données quelque part pour l'appelant)string
variable locale vers laquelle ce pointeur pointé a laissé la portéestd::string
implémentations (par exemple STLport et natif du compilateur) peuvent transmettre des données en ASCIIZ pour éviter les conflitsla source
Utilisez la
.c_str()
méthode pourconst char *
.Vous pouvez utiliser
&mystring[0]
pour obtenir unchar *
pointeur, mais il y en a deux: vous n'obtiendrez pas nécessairement une chaîne terminée par zéro et vous ne pourrez pas modifier 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 ou 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'au C ++ 11, mais dans la pratique, toutes les implémentations connues de
std::string
fonctionnaient de cette façon de toute façon; voir «& s [0]» pointe-t-il vers des caractères contigus dans une chaîne std ::? .Notez que de nombreuses
string
fonctions membres réalloueront le tampon interne et invalideront tous les pointeurs que vous pourriez avoir enregistrés. Mieux vaut les utiliser immédiatement, puis les jeter.la source
C ++ 17
C ++ 17 (norme à venir) modifie le synopsis du modèle en
basic_string
ajoutant une surcharge non constante dedata()
:CharT const *
destd::basic_string<CharT>
CharT *
destd::basic_string<CharT>
C ++ 11
CharT const *
destd::basic_string<CharT>
CharT *
destd::basic_string<CharT>
À partir de C ++ 11, la norme dit:
Il existe plusieurs façons possibles d'obtenir un pointeur de caractère non constant.
1. Utilisez le stockage contigu de C ++ 11
Pro
Les inconvénients
'\0'
ne doit pas être modifié / ne fait pas nécessairement partie de la mémoire non const.2. Utilisation
std::vector<CharT>
Pro
Les inconvénients
3. Utilisez
std::array<CharT, N>
ifN
est une constante de temps de compilation (et suffisamment petite)Pro
Les inconvénients
4. Allocation de mémoire brute avec suppression automatique du stockage
Pro
Les inconvénients
5. Allocation de mémoire brute avec traitement manuel
Pro
Con
la source
Je travaille avec une API avec beaucoup de fonctions en entrée
char*
.J'ai créé une petite classe pour faire face à ce genre de problème, j'ai implémenté l'idiome RAII.
Et vous pouvez l'utiliser comme:
J'ai appelé la classe
DeepString
parce qu'elle crée une copie profonde et unique (laDeepString
n'est pas copiable) d'une chaîne existante.la source
c_str()
tel qu'utilisé parstd
est une abréviation pour "C-string" pas "const string" etstr()
renvoie toujours astd::basic_string
, notchar*
(par exemplestd::stringstream::str()
)la source
Voyez juste ceci:
Cependant, notez que cela renverra a
const char *
.Pour a
char *
, utilisezstrcpy
pour le copier dans un autrechar
tableau.la source
Essaye ça
la source