Comment convertir un std::string
en un char*
ou un const char*
?
Si vous voulez juste 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 accessible en écriture, telle que _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 : Notez que ce qui précède n'est pas sans exception. Si quelque chose entre l'appel new
et l'appel delete
est émis, vous perdrez de la mémoire, car rien n'appellera automatiquement delete
. Il y a deux manières immédiates de résoudre ce problème.
boost::scoped_array
effacera la mémoire pour vous en sortant de la 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()] = '\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 (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()
_
Étant donné ...
std::string x = "hello";
Comment obtenir un pointeur de caractère valide alors que x
reste dans la portée et n'est plus modifié
C++ 11 simplifie les choses; les éléments 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();
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 auront la même valeur - l'adresse du premier caractère de la mémoire tampon. Même une chaîne vide a un "premier caractère dans le tampon", car C++ 11 garantit de toujours conserver un caractère de terminaison NUL/0 supplémentaire après le contenu de chaîne explicitement attribué (par exemple, std::string("this\0that", 9)
aura un tampon contenant "this\0that\0"
) .
Compte tenu de 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
Écrire une NUL ailleurs dans la chaîne ne ne modifie pas les string
's size()
; Les variables string
peuvent contenir n'importe quel nombre de NUL. Aucun traitement spécial n'est donné par std::string
(idem en C++ 03).
Dans C++ , les choses étaient considérablement plus compliquées (différences principales mises en évidence ):
x.data()
const char*
dans le tampon interne de la chaîne qui n'était pas requis par la norme pour conclure par un NUL (c'est-à-dire qu'il pourrait s'agir de ['h', 'e', 'l', 'l', 'o']
suivies de valeurs non initialisées ou parasites, avec des accès accidentels ayant un comportement indéfini ). x.size()
peuvent être lus en toute sécurité, à savoir 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: const
x
, on obtient un pointeur non -const
char*
; vous pouvez écraser le contenu d'une chaînex.c_str()
const char*
en une représentation ASCIIZ (terminée par NUL) de la valeur (c'est-à-dire ['h', 'e', 'l', 'l', 'l', 'o', '\ 0']).x.data()
et &x[0]
x.size()
+ 1 caractères sont sûrs à lire.Quelle que soit la manière 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 en ce sens ont un comportement indéfini , avec un risque réel de plantages de l'application et des résultats erronés, même pour les lectures, ainsi que des vulnérabilités de données, de corruption de pile et/ou de sécurité en gros pour les écritures.
Si vous appelez une fonction membre string
modifiant le string
ou réservant une capacité supplémentaire, les valeurs de pointeur renvoyées auparavant par l'une des méthodes ci-dessus sont invalidées . Vous pouvez utiliser ces méthodes à nouveau 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 que x
ait quitté la portée ou soit modifié ci-dessous ....
A partir de C++ 11, utilisez .c_str()
pour les données ASCIIZ et .data()
pour les données "binaires" (expliquées plus en détail ci-dessous).
En C++ 03, utilisez .c_str()
sauf si vous êtes certain que .data()
est adéquate et préférez .data()
à &x[0]
car il est sans danger pour les chaînes vides ....
... essayez de comprendre suffisamment le programme pour utiliser data()
le cas échéant, sinon 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 accessibles en toute sécurité. Cela s'applique à la fois aux fonctions C++ - comme par exemple fstream::fstream(const char* filename, ...)
et aux fonctions partagées avec C comme strchr()
et printf()
.
Étant donné les garanties de .c_str()
de C++ 03 concernant le tampon renvoyé sont un super ensemble de .data()
, vous pouvez toujours utiliser .c_str()
en toute sécurité, mais certaines personnes ne le font pas parfois car:
.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 transmettez à une autre fonction qui le traite comme un bloc de données "binaires". Cela peut constituer un élément crucial pour garantir que les modifications de code des autres programmeurs continuent à gérer les données correctement.string
nécessitera une allocation supplémentaire de mémoire et/ou une copie des données afin de préparer le tampon de terminaison NUL.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, alors .c_str()
est un bon choix (la fonction doit savoir où le texte se termine, 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 sentinelle ou une longueur attendue fixe).
x
ait quitté la portée ou ait été modifiéVous aurez besoin de copier le contenu de string
x
dans une nouvelle zone mémoire en dehors de x
. Ce tampon externe peut se trouver à de nombreux endroits, comme une autre variable string
ou un tableau de caractères, sa durée de vie peut être différente de celle de x
en raison de sa portée différente (espace de noms, global, statique, etc.). , 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 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épendamment de l'original string
, mais que pouvez-vous faire avec elle? Une sélection aléatoire d'exemples ...
string
, 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 par la mémoire volatile utilisée pour les entrées/sorties du périphérique (par exemple, for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
à un tableau de caractères contenant déjà du texte ASCIIZ (par exemple, strcat(other_buffer, x.c_str())
) - veillez à ne pas saturer le tampon (dans de nombreux cas, vous devrez peut-être utiliser strncat
).const char*
ou char*
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 renvoyer un std::string
, mais vous voulez copier vos données string
quelque part pour l'appelant) string
à laquelle ce pointeur a pointéstd::string
(par exemple, STLport et le compilateur natif) peuvent transmettre des données en tant que ASCIIZ pour éviter les conflitsUtilisez la méthode .c_str()
pour const char *
.
Vous pouvez utiliser &mystring[0]
pour obtenir un pointeur char *
, mais il y a quelques pièges: 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. Faites particulièrement attention de ne pas ajouter de caractères après la fin de la chaîne, sinon vous obtiendrez un dépassement de la mémoire tampon (et un crash probable).
Rien ne garantissait que tous les caractères feraient partie du même tampon contigu avant C++ 11, mais en pratique, toutes les implémentations connues de std::string
fonctionnaient de cette façon. voir “& s [0]” pointe-t-il sur des caractères contigus dans un std :: string? .
Notez que de nombreuses fonctions membres string
réaffecteront le tampon interne et invalideront les pointeurs que vous avez éventuellement enregistrés. Le mieux est de les utiliser immédiatement et de les jeter.
C++ 17 (norme à venir) modifie le synopsis du modèle _basic_string
_ en ajoutant une surcharge non constante de data()
:
charT* data() noexcept;
Retourne: 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();
_
CharT const *
_ de _std::basic_string<CharT>
__std::string str = { "..." };
str.c_str();
_
CharT *
_ de _std::basic_string<CharT>
_A partir de C++ 11, le standard dit:
- Les objets de type caractère dans un objet _
basic_string
_ doivent être stockés de manière contiguë. Autrement dit, pour tout objet _basic_string
s
, 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 la valeurCharT()
; la valeur de référence 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 existe différentes manières possibles d'obtenir un pointeur de caractère non const.
_std::string foo{"text"};
auto p = &*foo.begin();
_
Pro
inconvénients
'\0'
_ ne doit pas être modifié/ne fait pas nécessairement partie de la mémoire non const.std::vector<CharT>
__std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
_
Pro
inconvénients
std::array<CharT, N>
_ si N
est une 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
inconvénients
_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
inconvénients
_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
Con
Je travaille avec une API avec de nombreuses fonctions qui reçoivent en entrée un char*
.
J'ai créé une petite classe pour faire face à ce genre de problème, j'ai implémenté 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
car 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 ceci:
string str1("stackoverflow");
const char * str2 = str1.c_str();
Cependant, notez que ceci retournera un const char *
. Pour un char *
, utilisez strcpy
pour le copier dans un autre tableau char
.