IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Conversion d'un String en Char*


Sujet :

Langage C++

  1. #1
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut Conversion d'un String en Char*
    Bonjour à toutes et à tous,

    Je cherche à convertir un String en char* avec la méthode suivante, ça compile, mais apparemment, cela ne fonctionne pas. Quel peut être le problème ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    char* strToChar(String s) {
      char buf[32];
      int n = s.length();
      for (int i = 0; i < n; i++)
        buf[i] = s[i];
      s[n] = '\0';
      return buf;
    }
    NOTA : développant pour un système Alduino, je n'ai pas accès à la méthode : string.c_str()

    Merci de votre aide.

    Pierre

  2. #2
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Bonjour

    buf est créée dans la fonction sur la pile et est donc supprimée à la fin de la fonction. Le pointeur (char *) retourné pointe donc sur un espace mémoire invalide. L'accès à cette adresse provoque un comportement indéterminé (le plus souvent une erreur de segmentation).

    Comme tu n'a pas accès aux méthodes c_str ou data (il devrait y avoir un équivalent...), tu peux :
    - passer ton tableau buf en paramètres de la fonction
    - faire une allocation dynamique dans la fonction
    - écrire ta propre classe string et jetter celle que tu utilises qui à l'air toute pourrie (ce que je recommande le plus)

    Actuellement ta fonction est buggée :
    - que ce passe-t-il si la taille de la chaîne de caractère est supérieure ou égale à 32 ?

    Un petit lien utile autour de l'allocation dynamique : FAQ C++ - Gestion dynamique de la mémoire

    Edit : Si tu as les méthodes begin et end, tu peux recopier ton string en vector<char> avec le constructeur et récupérer un char * const depuis le vector (attention à \0).

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Tu veux dire Arduino? Convertir une String Arduino en tableau de char?

    Je n'ai jamais essayé cette plate-forme, mais en voyant la méthode toCharArray(), j'essaierais ceci:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char* strToChar(String s) {
    	unsigned int bufSize = s.length() + 1; //String length + null terminator
    	char* ret = new char[bufSize];
    	s.toCharArray(ret, bufSize);
    	return ret;
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Je vous remercie pour ces réponses. Pour autant, je me pose des questions.

    La méthode proposée par Medinoc fonctionne. Or, Ehonn a dit :
    Citation Envoyé par Ehonn Voir le message
    ... buf est créée dans la fonction sur la pile et est donc supprimée à la fin de la fonction. Le pointeur (char *) retourné pointe donc sur un espace mémoire invalide. L'accès à cette adresse provoque un comportement indéterminé (le plus souvent une erreur de segmentation). ...
    Cela veut donc dire que, dans ma méthode proposée par Medinoc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char* ret = new char[bufSize];
    n'est pas supprimé quand la fonction se termine.

    Alors, si j'appelle 10000 fois la fonction, autant de fois est créée la variable "ret". Ce qui risque, à la longue, de me saturer la mémoire ???

    Cordialement.

    Pierre

  5. #5
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    En passant : «Oui».
    Grosso modo, un new, un delete.

    Avec ce genre de fonction c'est la fonction qui fait le new, et l'utilisateur qui fait le delete :s
    C'est assez sale en C++. On préfère : «celui qui fait le new, fait le delete». Tu peux te renseigner sur le RAII.

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Oui, une fois que tu n'as plus besoin du buffer, il faut le supprimer avec delete[].

    Un new, un delete, et un new[], un delete[].

    ..Sinon, il doit y avoir l'option de faire la même chose avec un std::vector<char> au lieu d'un tableau dynamique; le nettoyage sera automatique.

    Probablement un truc de ce genre (non-testé!)
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    vector<char> strToChar(String s) {
    	unsigned int bufSize = s.length() + 1; //String length + null terminator
    	vector<char> ret(bufSize);
    	s.toCharArray(&ret[0], bufSize);
    	return ret;
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Oui, une fois que tu n'as plus besoin du buffer, il faut le supprimer avec delete[].

    Un new, un delete, et un new[], un delete[]. ..
    Oui, mais si j'ai fait cette remarque, c'est parce que je ne vois pas comment faire un delete sur la variable "ret" de l'extérieur de la fonction. Et si je le fais de l'intérieur, je retombe sur la remarque de Ehonn ??

    Cordialement.

    Pierre

  8. #8
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    La vrai question c'est pourquoi as-tu besoin d'un char* ?

    D'une certaine manière le mieux c'est de déléguer à la couche du dessus la création et la destruction du tableau de char* :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Precondition : La taille de tab doit être égale a s.length()+1.
    void StringToChar(const String& s, char* tab)
    {
      int i;
      for(i=0; i < s.length(); ++i)
        tab[i] = s[i];
      tab[i] = '\0';
    }
     
    // Quelque part dans ton code.
    String s;
    // Utilise s...
    char *tab = new char[s.length() + 1];
    StringToChar(s, tab);
    // Utilise tab
    delete[] tab;
    Le problème c'est que si tu oublies de faire le delete tu vas avoir des fuites mémoires. Si ton compilateur le supporte (et ça semble être le cas) tu peux faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char tab[s.length() + 1];
    s.toCharArray(tab, s.length);
    C'est la solution la plus élégante dans ton cas je pense.

    Si tu as accès à la STL (essaye d'include #include <string> dans ton code pour juste voir si ça compile), tu peux faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #include <string>
     
    std::string s;
    // utilise s
    const char* str = s.c_str();
    // ...
    Ou encore si tu peux utiliser std::string mais que tu dois tout de même posséder la classe String :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    std::string StringToSTLString(const String& s)
    {
      std::string s2(s.length());
      for(int i=0; i < s.length(); ++i)
        tab[i] = s[i];
      return s2;
    }
     
    // Quelque part dans ton code.
    String s;
    // Utilise s...
    std::string s2 = StringToSTLString(s);
    // Utilise s2
    s2.c_str();
    Mais bon, ça m'étonnerait sinon tu y aurais déjà pensé. Tu peux toujours faire ta propre string (pour éviter d'oublier des delete[] mais également pour la brièveté du code résultant).

    Regarde aussi cette lib qui peut être une option : https://github.com/maniacbug/Standar...ster/README.md

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    La première question à se poser, c'est : Que comptes tu faire de ton char* ?

    S'il te sert à appeler une fonction qui ne va rien modifier du contenu de la chaîne, juste le lire, et a une interface en char*, et que tu n'as plus besoin de ce char* une fois que l'appel de la fonction est terminé (cas le plus courant), tu n'as pas besoin de posséder les char* de ta chaîne, juste de les observer : utilise simplement toCharArray().

    Sinon, par exemple parce qu'il te sert à récupérer un buffer que tu comptes conserver longtemps, ou modifier (mais les modifications ne seront pas reportées automatiquement dans ta chaîne, à toi de la faire manuellement),... Alors tu vas devoir posséder le buffer, et gérer plus ou moins manuellement la mémoire allouée. Les solutions précédemment données peuvent alors s'appliquer...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  10. #10
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    @JolyLoic : Le problème c'est que toCharArray ne fait pas la même chose que le c_str(), il prend un argument de type char* et copie le String dedans… Donc on est bien obligé de posséder cette chaine... Surtout que &s[0] ne marchera pas vu que s[0] retourne une copie du char. En gros, j'ai trouvé aucun moyen d'accéder à la mémoire sous-jacente...

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par ChPr Voir le message
    Oui, mais si j'ai fait cette remarque, c'est parce que je ne vois pas comment faire un delete sur la variable "ret" de l'extérieur de la fonction. Et si je le fais de l'intérieur, je retombe sur la remarque de Ehonn ??
    Euh...


    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    String s("Toto");
    char *s2 = strToChar(s);
     
    //Utiliser s2
     
    delete[] s2;
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Oups, j'ai parlé trop vite alors... Et effectivement, cette classe a l'air assez rustique...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  13. #13
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Euh...


    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    String s("Toto");
    char *s2 = strToChar(s);
     
    //Utiliser s2
     
    delete[] s2;
    Le problème est que des appels comme cela, j'en ai à la pelle. Je ne vais pas répéter ce morceaux de code à chaque fois que j'en ai besoin. Donc je le mets dans une fonction et dès lors, se repose le problème du "delete".

    La solution étant de déclarer le buffer à l'extérieur de la fonction comme l'a proposé Trademark.

    Mais quitte à faire cela à l'extérieur, autant créer un variable globale buffer qui existera tout au long du programme, que je n'aurai pas à créer et supprimer à chaque fois que je m'en sers.

    Cordialement.

    Pierre

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Sauf que ce buffer n'aura pas forcément tout le temps la bonne longueur...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #15
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    RAII :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    String s("Toto");
    std::unique_ptr<char[]> s2 = strToChar(s);
     
    //Utiliser s2.get()
    // Pas besoin d'effacer
    Si tu n'as pas accès à std::unique_ptr, tu peux te bricoler aisément une classe qui fasse pareil dans ton cas particulier.

    Un buffer unique est vraiment une fausse bonne idée. En plus du problème de taille, tu as des soucis en cas de parallélisme, ou tout simplement de réentrance...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  16. #16
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Sauf que ce buffer n'aura pas forcément tout le temps la bonne longueur...
    C'est vrai, mais dans mon cas particulier, cela ne pose pas de problème. Toutes mes chaînes ont une longueur bien déterminée.

    Cordialement.

    Pierre

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. conversion d'un string en char*?
    Par melleb dans le forum C#
    Réponses: 4
    Dernier message: 02/09/2008, 10h06
  2. conversion d'une string en char
    Par vulkanosaure dans le forum C++
    Réponses: 12
    Dernier message: 02/11/2007, 19h55
  3. Conversion de type string en char ?
    Par CoachMac dans le forum C
    Réponses: 18
    Dernier message: 08/08/2006, 10h20
  4. conversion d'un string en char *
    Par ferry.jules dans le forum C++Builder
    Réponses: 15
    Dernier message: 12/05/2006, 22h12
  5. (Problème avec...) conversion de std::string en char
    Par crossbowman dans le forum SL & STL
    Réponses: 7
    Dernier message: 05/03/2006, 19h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo