Comment convertir une chaîne std :: en const char * ou char *?

895

Comment puis-je convertir un std::stringen un char*ou un const char*?

user37875
la source
2
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.
paulm
1
char * result = strcpy ((char *) malloc (str.length () + 1), str.c_str ());
cegprakash
7
@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;
const char * 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 = 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 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(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 :: 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()
Johannes Schaub - litb
la source
41
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:

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 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 const xcela donne un non const char*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 string xdans 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) 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'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
Tony Delroy
la source
4
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.

Mark Ransom
la source
1
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.
Mark Ransom
2
char * result = strcpy (malloc (str.length () + 1), str.c_str ());
cegprakash
21

C ++ 17

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 = { "..." };
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:

  1. 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().

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    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.


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    Renvoie: un pointeur p tel que p + i == &operator[](i)pour chaque ientrée [0,size()].

Il existe plusieurs façons possibles d'obtenir un pointeur de caractère non constant.

1. Utilisez le stockage contigu de C ++ 11

std::string foo{"text"};
auto p = &*foo.begin();

Pro

  • Simple et court
  • Rapide (seule méthode sans copie impliquée)

Les inconvénients

  • Final '\0'ne doit pas être modifié / ne fait pas nécessairement partie de la mémoire non const.

2. Utilisation std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

Pro

  • Facile
  • Gestion automatique de la mémoire
  • Dynamique

Les inconvénients

  • Nécessite une copie de chaîne

3. Utilisez std::array<CharT, N>if Nest 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

  • Facile
  • Gestion de la mémoire de la pile

Les inconvénients

  • Statique
  • Nécessite une copie de chaîne

4. Allocation de mémoire brute avec suppression automatique du stockage

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
  • Facile

Les inconvénients

  • Nécessite une copie de chaîne
  • Statique (l'utilisation dynamique nécessite beaucoup plus de code)
  • Moins de fonctionnalités que le vecteur ou le tableau

5. Allocation de mémoire brute avec traitement manuel

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 de chaîne
  • Responsabilité / susceptibilité maximale aux erreurs
  • Complexe
Pixelchemist
la source
9

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.

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 DeepStringparce qu'elle crée une copie profonde et unique (la DeepStringn'est pas copiable) d'une chaîne existante.

Alessandro Teruzzi
la source
3
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());
cegprakash
la source
1
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.
Striezel
7

Voyez juste ceci:

string str1("stackoverflow");
const char * str2 = str1.c_str();

Cependant, notez que cela renverra a const char *.

Pour a char *, utilisez strcpypour le copier dans un autre chartableau.

devsaw
la source
23
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.
Mat
7
Personnellement, j'apprécie la simplicité.
TankorSmash
-4

Essaye ça

std::string s(reinterpret_cast<const char *>(Data), Size);
anish
la source