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

C++ Discussion :

Retourner *proprement* un type string


Sujet :

C++

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10
    Par défaut Retourner *proprement* un type string
    Bonjour à tous, après avoir passé de nombreuses heures à "googler" sur la question, je n'ai pas eu de réponse satisfaisante, je vais donc avoir besoin d'aide...

    D'abord, en C, y-a-t-il un moyen propre de retourner un char *, dans le but d'obtenir une nouvelle chaine ?

    1er exemple :
    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
     
    char * addch (char * ch1, char * ch2) {
        char buf[256] ;
        buf[0]='\0';
        strcat(buf,ch1);
        strcat(buf,ch2);
        return buf;
    }
    void main () {
       Char ptr[256], ch1[256], ch2[256];
       strcpy(ch1,"Hello ");
       strcpy(ch2,"World !");
       ptr = addch(ch1,ch2);
       ...
    }
    Je retourne l'addresse d'une varialbe locale => pas bien. Même avec un strcpy en sortie, on est pas à l'abri de voir la zone mémoire de buf écrasée lors de l'appel à strcpy (si on a pas de chance)...
    (Ce que je viens de dire est-il correct ?)

    2eme exemple :
    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
    17
     
    char * addch (char * ch1, char * ch2) {
        char * buf;
        // je ne me souvient plus de la syntaxe exacte pour :
        <memalloc : allocation de mémoire pour buf> 
        buf[0]='\0';
        strcpy(buf,ch1);
        strcat(buf,ch2);
        return buf;
    }
    void main () {
       Char ptr[256], ch1[256], ch2[256];
       strcpy(ch1,"Hello ");
       strcpy(ch2,"World !");
       ptr = addch(ch1,ch2);
       ...
    }
    Là, il me semble que c'est mieux, sauf qu'en retour de fonction, comme ptr prennant la nouvelle valeur avait déja une allocation, celle-ci est donc "perdue" (cause de "memory leak") ?
    faut-il désallouer ptr avant l'appelle à la fonction ?
    Cette solution me semble lourde et peu pratique à appliquer, autant passer ptr comme paramètre supplémentaire (?), sauf qu'il est alors inutile de retourner une valeur, puisqu'on peux directement affecter la nouvelle chaine à ptr ...


    En C++ maintenant, avec la classe STL string, que se passe-t-il si je retourne une variable locale de type string dans l'exemple suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    string str1;
    str1 = addstr(str2,str3);
    Est ce que l'affectation de str1 avec l'opérateur = est une simple affectation de pointeur, ou est-ce que la chaine est recopiée à l'addresse adéquate dans la structure String ? Et même dans ce dernier cas, ne risquons nous pas de voir la chaine écrasée avant d'avoir été recopiée (comme dans le cas précédent en C) ?

    Etant donné que le problème semble "basic" et que je n'ai trouvé aucune réponse dans les forums/faqs, je dois avoir mal compris certains concepts en C/C++ concernant les appels aux fonctions / l'allocation mémoire / la gestion des pointeurs...

    Merci de votre aide !

  2. #2
    Rédacteur
    Avatar de bigboomshakala
    Homme Profil pro
    Consultant Web .NET
    Inscrit en
    Avril 2004
    Messages
    2 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Consultant Web .NET
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2004
    Messages : 2 077
    Par défaut
    comme tu n'utilises pas de pointeurs mais une classe (string) ce qui est retourné est un objet, donc no problemo

    Est ce que l'affectation de str1 avec l'opérateur = est une simple affectation de pointeur
    -> string str1 : pas d'étoile, pas de pointeur, pas de question d'alloc/désalloc à se poser, on affecte et c'est ok.
    un avantage du C++ face au C c'est l'objet. on peut la plupart du temps se passer de pointeurs et on n'a pas à s'inquiéter dans ce cas d'alloc/désalloc.

  3. #3
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    * C.1- Effectivement, ce qui est retrouné sera écrasé. C'est une certitude et non une question de chance. Ce quetu retournes pointe vers une zone de mémoire qui peut contenir n'importe quoi.
    Qui plus est, ptr est un tableau dans ton main, tu ne peux pas le réaffecter

    * C.2- C'est à l'appelant de réaliser la libération qui correspond à l'allocation effectuée.
    De plus, ton code est potentiellement vulnérable aux buffer overloads.

    Tu as une troisième solution : tu passes un buffer à ta fonction, ainsi que la taille du buffer. cf l'interface de strcpy.


    * C++.
    La classe de la SL (sans T) std::string a une sémantique de valeur -- en particulier, c'est copiable. Cela va se manipuler comme les nombres (int, char, long, double, ...). Quand tu retournes une variable locale de type nombre, tu n'as aucun problème. Là, c'est pareil. Ce qui sort, c'est une copie de la variable locale (il y a des cas particuliers d'optimisations possibles qui vont économiner une copie).
    Quand tu écris
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int i = 42;
    int j = 2;
    j= i;
    La dernière affectation écrase l'ancienne valeur de j. Il n'y a pas de problème. Avec les std::string, c'est pareil. Tu as une sémantique de valeur. Et la mémoire est gérée de ménière transparente pour toi.

    PS: la gestion transparente de la mémoire est orthogonale au fait que le C++ supporte la programmation OO.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  4. #4
    Membre chevronné Avatar de themadmax
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2005
    Messages : 446
    Par défaut
    Je te conseille aussi l'utilisation de std::string, mais si tu est obligé de t'en passé il est plus sage que l'appelant passe le buffer de chaine et sa taille.
    ex:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    bool catfunc(char* buff, int size, const char txt1, const char txt2)
    {
       if ( buff == NULL )
          return false; //Pourquoi pas lever une exeception ?
       strcat( buff, txt1 ); //voir strcat_s sous VC++
       strcat( buff, txt2 ); //sinon geré un debordement possible
       return true;
    }
    char buff[255];
    catfun( buff, 255, "text1", "text2" ); //on devrai tester le retour

  5. #5
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Citation Envoyé par themadmax
    mais si tu est obligé de temp passé il est plus sage que l'appelant passe le buffer de chaine et sa taille.
    Je ne comprends pas ta phrase. Il doit manquer un mot.

    PS: Ton code a toujours un potentiel buffer overflow, et il calcule deux fois la longueur de la chaîne à mettre devant.
    PS2: pour moi, ici, c'est une assertion qui convient le mieux. Car passer 0 à la chaîne destination n'a aucun intérêt. Il ne peut que s'agir d'une erreur de programmation => assert.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  6. #6
    Membre émérite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par défaut
    Citation Envoyé par Luc Hermitte
    Je ne comprends pas ta phrase. Il doit manquer un mot.
    Pas de mot qui manque, c'est que c'est mal écrit

    "mais si tu es obligé de t'en passer il est plus sage que l'appelant passe le buffer de chaine et sa taille."

  7. #7
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Arf.
    Effectivement cela a bien plus de sens. Je cherchais un mot supplémentaire qui pouvait lier ta phrase à une solution pour optimiser les traitements. Je n'y étais vraiment pas du tout.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  8. #8
    Membre chevronné Avatar de themadmax
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2005
    Messages : 446
    Par défaut
    Je n'aime pas dérriver dans les forums mais là je me sent obligé.
    Désolé pour l'ecriture j'ecris déja mal à la base mais je suis généralement au boulot tres pressé. J'ai poster quelque msg, parfois des personnes m'ont grandement aidé. Donc je prefere ecrire parfois des postes à la vavite avec un exemple si j'estime que sa peut aider qlq'un. Si s'a horripile ceux qui lisent (voir ici qu'on comprenne pas) je m'en excuse, mais l'exemple est la et parle de lui meme!

    Pour ce qui est de la regle Erreur de programmation = ASSERT, c'est vrai que c'est pas mal, mais si c une dll ! collé

    Pour ce qui est du buffer over flow, je precise dans le comentaire d'utiliser strcat_s, soit de calculer manuelement si il y a debordement, pour cela il faudrai passer la taille max des 2 autres parametres ( c cela que tu veut dire?)

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    strcat_s(), c'est le mal.
    À réserver pour les choses qui ne PEUVENT pas échouer (comme les assertions, mais en plus difficile à déboguer).

    Par contre, tu peux faire quelque chose de propre avec strncat_s(..., _TRUNCATE)
    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.

  10. #10
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Citation Envoyé par themadmax
    Pour ce qui est du buffer over flow, je precise dans le comentaire d'utiliser strcat_s, soit de calculer manuelement si il y a debordement, pour cela il faudrai passer la taille max des 2 autres parametres ( c cela que tu veut dire?)
    strcat_s n'est pas présent partout pour l'instant.
    strncat, strncpy et cie le sont et sont très bien pour éviter les dépassements sur des buffers statiques. Ils renvoient même un pointeur pour éviter de recalculer la longueur des chaînes à assembler.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10
    Par défaut
    Merci à tous pour vos réponses, je comprends mieux pourquoi il n'y a pas de sujet sur les retours de string : c'est simple comme de retourner un nombre.

    Si je comprends bien, en C le seul moyen propre pour obtenir une chaine est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    char ma_chaine[bufl]
    fonction(ma_chaine,bufl,autre_chaine,param,...) ;

    Est-ce que ça veut dire que les syntaxes du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ma_chaine = fonction(...) ;
    sont un privilège réservé au languages OO ?

    Mais alors, le BASIC qui permet de telle syntaxe serait - il un language OO ??
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    DECLARE FUNCTION test$ (a$, b$)
     
    FUNCTION test$ (a$, b$)
    test$ = a$ + b$  // Whaaa cette ligne me donne l'impression de manipuler des SL string :p 
    END FUNCTION
     
    CLS
    PRINT test$("BASIC ", "ROCKS! ;o)")
    au passage,
    *question subsidiaire*
    pourquoi le BASIC n'est-il pas considéré comme un "vrai" language (comme le pascal ou le c) ?
    A cause des types/conversions ? l'impossibilité de passer/retourner des tableaux, absence de pointeurs ?

    le fait d'utiliser $ pour les chaines, % pour les entiers etc... est pas si mal, on sait tout de suite a quel type on a à faire, sans aller vérifier dans les déclarations

    Et si on lançais un débat ?
    QBASIC vs C++

  12. #12
    Membre chevronné Avatar de Mayti4
    Inscrit en
    Février 2004
    Messages
    442
    Détails du profil
    Informations forums :
    Inscription : Février 2004
    Messages : 442
    Par défaut
    Est-ce que ça veut dire que les syntaxes du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ma_chaine = fonction(...) ;
    sont un privilège réservé au languages OO ?
    Ben non, exemple en C:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    const char *foo()
    {
        return "une chaine";
    }
     
    const char *chaine = foo();
    Si tu veux retourner une variable locale, elle doit être static.

  13. #13
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    sont un privilège réservé au languages OO ?
    Cela n'a rien à voir avec l'objet.
    C'est simplement que C++ permet la définition de sémantiques de valeur personnalisées pour les types utilisateurs, ce que, d'ailleurs, aucun autre langage ne permet à ma connaissance.

  14. #14
    Membre habitué
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10
    Par défaut
    Si tu veux retourner une variable locale, elle doit être static.
    Ah, ça ça m'interresse, en fait j'aurais peut-être du poser ma question ainsi : "peut-on retourner proprement une variable locale ?"

    Est-ce que ça veut dire qu'il me suffit de mettre static dans la déclaration de ma variable locale à ma fonction pour pouvoir la retourner sans risques ?

  15. #15
    Membre chevronné Avatar de Mayti4
    Inscrit en
    Février 2004
    Messages
    442
    Détails du profil
    Informations forums :
    Inscription : Février 2004
    Messages : 442
    Par défaut
    Citation Envoyé par supernoob
    Est-ce que ça veut dire qu'il me suffit de mettre static dans la déclaration de ma variable locale à ma fonction pour pouvoir la retourner sans risques ?
    Oui, mais une variable static n'est rien d'autre qu'une variable globale limité à la fonction.

    Il faut donc faire attention si tu garde un pointeur dessus.

    Exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const char *foo( const char *test )
    {
        static char chaine[100];
        strcpy( chaine, test );
        return chaine;
    }
     
    const char *test1 = foo( "TEST1" );
    const char *test2 = foo( "TEST2" );
    test2 vaudra "TEST2" mais test1 aussi.

  16. #16
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Le mot-clé static t'assurera juste que la variable vivra en dehors de l'appel de fonction. Rien à voir avec le fait de "renvoyer correctement une variable".

    Si ce que tu renvoies peut être renvoyé par valeur (au pif : std::string) alors renvoie par valeur, ce sera une copie qui sera retournée et la variable locale pourra ne pas survivre à la fonction sans que cela pose de problème à quiconque.

  17. #17
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    static n'a rien à voir avec le retour de variables par recopie.
    C'est une feinte qui permet de retourner un pointeur vers une zone (tableau statique) qui continue d'exister à la fin de l'exécution de la fonction. Le genre de feinte qui ne permet pas d'avoir du code réentrant, et qui ne permet pas d'avoir des buffers de taille quelconque.
    (C'est aussi applicable aux retours par référence et cie).

    Franchement, à part pour comprenndre pourquoi il faut préférer les fonctions xxxx_r dans les pages de man, mieux vaut oublier l'existence de cette application -- J'avoue, je bosse dans un contexte temps réel complètement multi-threadé.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  18. #18
    Membre confirmé Avatar de Chen norris
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2004
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2004
    Messages : 216
    Par défaut Nouveau problème
    Je viens de voir que le retour d'un string via une fonction ne posait pas de problème puisqu'il s'agissait d'un objet. Pourtant, mon compilo (Dev C++) me renvoit l'erreur suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    In member function `std::string CLASSE::fonction(char, std::string)':
    invalid use of member (did you forget the `&' ?)
    Pourtant, je ne cherche pas à passer d'adresse. Ma fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    string CLASSE::fonction(char motif, string buffer) {
     
    	// ...
     
    	return buffer.substr(	...	);
    }
    Je ne dois pas bien comprendre l'erreur.

    EDIT : Aarrgh... Désolé. J'ai posté trop vite. L'erreur ne vient pas de là. Finalement, aucun problème pour cette fonction.

Discussions similaires

  1. Tuto MVVM : dans le source, une property de type string retourne un booléen ?!
    Par zax-tfh dans le forum Windows Presentation Foundation
    Réponses: 5
    Dernier message: 23/03/2009, 08h35
  2. Type String* en paramètre...
    Par Invité dans le forum MFC
    Réponses: 4
    Dernier message: 24/02/2004, 19h48
  3. Convertir une date en type string
    Par ziboux dans le forum SQL Procédural
    Réponses: 2
    Dernier message: 29/10/2003, 10h52
  4. [VB6] creation de variable de type string dynamiquement
    Par da40 dans le forum VB 6 et antérieur
    Réponses: 10
    Dernier message: 12/06/2003, 16h59
  5. Réponses: 2
    Dernier message: 05/06/2002, 12h29

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