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


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


777
2017-12-07 19:30


origine


Réponses:


Si vous voulez juste passer un std::string à une fonction qui 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, 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;

modifier: Notez que ce qui précède n'est pas une exception sûre. Si quelque chose entre le new appeler et le delete appel jette, vous fuira la mémoire, comme rien ne l'appellera delete pour vous automatiquement. Il y a deux façons immédiates de résoudre cela.

boost :: scoped_array

boost::scoped_array va supprimer 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

std :: vecteur

C'est la manière standard (ne nécessite aucune bibliothèque externe). Tu utilises 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()

932
2017-12-07 19:31



Donné dire ...

std::string x = "hello";

 Obtenir un `char *` ou un `const char *` à partir d'une `chaîne`

Comment obtenir un pointeur de caractère valide x reste dans le champ d'application et n'est pas modifié davantage

C ++ 11 simplifie les choses; tous les suivants donnent 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...

Tous les pointeurs ci-dessus tiendront le 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 conserver toujours un caractère de terminaison NUL / 0 supplémentaire après le contenu de chaîne explicitement affecté (par ex. std::string("this\0that", 9) aura une réserve de tampon "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 les non-membresconst 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 la stringde size(); stringsont autorisés à contenir un nombre quelconque de NUL - ils ne reçoivent aucun traitement spécial par std::string (même chose en C ++ 03).

Dans C ++ 03, les choses étaient considérablement plus compliquées (différences clés a souligné):

  • x.data()

    • résultats const char* au tampon interne de la chaîne qui n'était pas exigé par la norme pour conclure avec un NUL (c'est-à-dire pourrait être ['h', 'e', 'l', 'l', 'o'] suivi par des valeurs non initialisées ou déchets, avec des accès accidentels à ceux-ci comportement indéfini).
      • x.size() les caractères sont sûrs à lire, c'est-à-dire x[0] par 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 devriez 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. donné f(const char* p, size_t n) { if (n == 0) return; ...whatever... } tu ne dois pas appeler f(&x[0], x.size()); quand x.empty() - juste utiliser f(x.data(), ...).
    • sinon, selon x.data() mais:
      • pour nonconst  x cela donne un nonconst  char* aiguille; vous pouvez remplacer le contenu de la chaîne
  • x.c_str()

    • résultats 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 à la chaîne de mise en œuvre la liberté de créer un tampon distinct à terminaison NUL  à la volée, à partir du tampon à terminaison potentiellement non NUL "exposé" par x.data() et &x[0]
    • x.size() + 1 caractères sont sûrs à lire.
    • 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 comportement indéfini, avec une chance très réelle de plantage d'application et des résultats de poubelle même pour des lectures, et en outre des données en gros, la corruption de pile et / ou des failles de sécurité pour des écritures.

Quand ces pointeurs sont-ils invalidés?

Si vous appelez string fonction membre qui modifie le string ou réserve une capacité supplémentaire, toutes les valeurs de pointeur retournées au préalable par l'une des méthodes ci-dessus sont invalidé. Vous pouvez réutiliser ces méthodes pour obtenir un autre pointeur. (Les règles sont les mêmes que pour les itérateurs strings).

Voir également Comment obtenir un pointeur de caractère valide même après x laisse la portée ou est modifié plus loin au dessous de....

Donc, qui est meilleur utiliser?

À 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() à moins que certain .data() est adéquat, et préfère .data() plus de &x[0] comme c'est sûr pour les chaînes vides ....

... essayez de comprendre le programme assez à utiliser data() le 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 une valeur sentinelle indiquant la fin des données pertinentes et sécurisées. Cela s'applique à la fois aux fonctions C ++ et seulement fstream::fstream(const char* filename, ...) et partagé avec des fonctions C comme strchr(), et printf().

Étant donné C ++ 03 .c_str()Les garanties sur le tampon retourné sont un super ensemble de .data(), vous pouvez toujours utiliser en toute sécurité .c_str(), mais parfois les gens ne le font pas 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 pour stocker un bloc de données (qui n'est parfois même pas vraiment textuel)), ou que vous transmettez à un autre fonction qui le traite comme un bloc de données "binaires". Cela peut être un point de vue crucial pour s'assurer que les modifications de code d'autres programmeurs continuent à gérer correctement les données.
  • C ++ 03 seulement: il y a une petite chance que votre string l'implémentation devra faire une allocation de mémoire supplémentaire et / ou une copie de données afin de préparer le tampon terminé NUL

Comme un 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 a besoin d'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 s'il ne s'agit pas d'un paramètre séparé, il peut s'agir d'une convention comme un préfixe de longueur ou une sentinelle fixe).

Comment obtenir un pointeur de caractère valide même après x laisse la portée ou est modifié plus loin

Vous aurez besoin de copie le contenu du string  x à une nouvelle zone de mémoire à l'extérieur x. Ce tampon externe pourrait être dans de nombreux endroits comme un autre string ou variable de tableau de caractères, il peut avoir ou non une durée de vie différente de x en raison d'ê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:

// 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);

D'autres raisons de vouloir un char* ou const char* généré à partir d'un string

Donc, au-dessus de vous avez vu comment obtenir un (const) char*, et comment faire une copie du texte indépendant de l'original stringmais qu'est-ce que tu peux faire avec ça? Une poignée d'exemples aléatoires ...

  • donner l'accès au code "C" au C ++ stringle texte, comme dans printf("x is '%s'", x.c_str());
  • copie xle texte dans un tampon spécifié par l'appelant de votre fonction (par ex. strncpy(callers_buffer, callers_buffer_size, x.c_str())), ou une mémoire volatile utilisée pour les E / S de périphérique (par ex. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • ajouter xle texte d'un texte à un tableau de caractères contenant déjà du texte ASCIIZ (par ex. strcat(other_buffer, x.c_str())) - faites attention à ne pas dépasser le tampon (dans de nombreuses situations, vous devrez peut-être utiliser strncat)
  • retourner 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 je veux copier votre stringdes données quelque part pour l'appelant)
    • faites attention de ne pas renvoyer un pointeur qui peut être déréférencé par l'appelant après un appel local string variable à laquelle ce pointeur point a quitté la portée
    • certains projets avec des objets partagés compilés / liés pour différents std::string implémentations (par exemple STLport et compiler-native) peuvent transmettre des données en ASCIIZ pour éviter les conflits

173
2018-01-12 15:53



Utilisez le .c_str() méthode pour const char *.

Vous pouvez utiliser &mystring[0] pour obtenir un char * pointeur, mais il y a quelques getcha: vous n'obtiendrez pas forcément une chaîne terminée par zéro, et vous ne pourrez pas changer la taille de la chaîne. Vous devez en particulier faire attention à ne pas ajouter de caractères après la fin de la chaîne ou vous obtiendrez un dépassement de mémoire 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 travaillé de cette façon de toute façon; voir Est-ce que "& s [0]" pointe vers des caractères contigus dans une chaîne std :: string?.

Notez que beaucoup string Les fonctions membres réalloueront le tampon interne et invalideront tous les pointeurs que vous pourriez avoir sauvegardés. Le mieux est de les utiliser immédiatement, puis de les jeter.


31
2018-03-29 13:32



C ++ 17

C ++ 17 (norme à venir) change le synopsis du modèle basic_string l'ajout d'une surcharge non const de data():

charT* data() noexcept;

Retourne: 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>

A partir de C ++ 11, la norme dit:

  1. Les objets char-like dans un basic_string l'objet doit être stocké de manière contiguë. C'est, pour tout basic_string objet s, l'identité &*(s.begin() + n) == &*s.begin() + n doit tenir pour toutes les valeurs de ntel que 0 <= n < s.size().

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

    Résultats: *(begin() + pos) si pos < size(), sinon une référence à un objet de type CharT avec 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;

    Retourne: Un pointeur p tel que p + i == &operator[](i) pour chaque i dans [0,size()].

Il existe plusieurs manières possibles d'obtenir un pointeur de caractère non const.

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

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

Les inconvénients

  • Nécessite une copie de chaîne

3. Utilisation std::array<CharT, N> si N est la constante de temps de compilation (et assez petite)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

Pro

  • Simple
  • Empiler la gestion de la mémoire

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

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 gestion 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

  • Maximum de «contrôle»

Con

  • Nécessite une copie de chaîne
  • Responsabilité maximale / susceptibilité aux erreurs
  • Complexe

18
2017-07-12 12:06



Je travaille avec une API avec beaucoup de fonctions en entrée une char*.

J'ai créé une petite classe pour faire face à ce genre de problème, j'ai mis en place 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'il crée une copie profonde et unique (le DeepString n'est pas copiable) d'une chaîne existante.


9
2018-05-12 08:18



char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

7
2018-02-17 16:45



Voyez juste ceci:

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

Cependant, notez que cela retournera un const char *.Pour un char *, utilisation strcpy pour le copier dans un autre char tableau.


7



Essaye ça

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

-4