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 :

Soucis avec strcat_s


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2011
    Messages
    83
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mars 2011
    Messages : 83
    Par défaut Soucis avec strcat_s
    Bonjour,

    j'avais pour habitude de travailler avec la fonction strcat, mais depuis que j'ai constaté qu'elle provoque des warnings, je me suis mis a utiliser strcat_s.

    Pour les besoins d'un programme, je dois convertir un float en un int (respectant le standard IEEE754) que je stock ensuite dans un char*.

    Mon soucis est que le programme fonctionne bien quand j'utilise strcat mais il "crash" si j'utilise strcat_s, en faisant apparaitre le message "expression (l string is not null terminated && 0)"

    Voici ma fonction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void GamepadController:: add_Float_value_IEEE754(float value, char* dest)
    {
     	int convert = *(int*)&value;
     
     	string str; 
    	str = to_string(convert);
    	const char* cc =  str.c_str();
     
    	strcat_s(dest, strlen(cc), cc); //crash
    	//strcat(dest, cc); //Fonctionne  	
    }
    quelqu'un pourrait m'expliquer d’où vient mon erreur ?

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    D'abord, il faut savoir que les fonctions str**_s ne sont, à ma connaissance du moins, que des "extensions" propres à microsoft.

    Je ne vais pas rentrer dans le débat "pour ou contre ces extensions" car ce n'est très clairement pas le sujet, mais il faut savoir que, comme toute extension fournie par un compilateur donné, ben... c'est pas portable

    Je me demande sincèrement si tu n'aurais pas largement intérêt simplement désactiver ces avertissements.

    Cela peut normalement être fait en définissant le sysmbole _CRT_SECURE_NO_WARNINGS au niveau des options de compilation

    Ceci étant dit, je me demande sincèrement si tu ne te fais pas beaucoup de mal pour rien en voulant impérativement travailler sur du char * en interne...

    Je m'explique : ta fonction s'appelle add_Float_value_IEEE7. Si je ne m'en tiens qu'au nom de la fonction (et à l'utilisation de strcat(_s) ), cette fonction doit concaténer la valeur d'un float (tiens, au passage, pourquoi le convertir en int ???) à une chaine de caractères.

    Si tu as le réel 3.141592 et la chaine "salut", tu t'attend donc à avoir "salut3.141592" après exécution de ta fonction.

    Comme tu transmets un char * à ta fonction, cela oblige l'utilisateur qui a appeler la dite fonction à s'assurer au préalable que le pointeur qu'il transmettra à la fonction pointe bel et bien vers un espace mémoire suffisant pour y placer le contenu de la chaine de caractères finale!!!

    Voilà bien un comportement particulièrement dangereux

    D'abord, parce que cela signifie que la logique qui fera cette vérification devra se retrouver avant chaque appel de la fonction, et que cela ne respecte pas un des principes de base DRY (Don't Repeat Yourself!).

    Ensuite, parce que si l'utilisateur de la fonction oublie de faire la vérification en question, ben... tu te retrouveras au mieux avec une chaine tronquée, au pire, avec un débordement de mémoire

    Enfin parce que cela implique que l'utilisateur de la fonction doit être en mesure d'évaluer le nombre de caractères susceptibles d'être rajoutés par ta fonction.

    Cela oblige donc l'utilisateur de ta fonction à calculer ce nombre afin de s'assurer que l'espace mémoire vers lequel pointe le pointeur qu'il compte transmettre à la fonction représente suffisamment d'espace (et donc à allouer de l'espace supplémentaire s'il échoit) alors que ta fonction elle-même va, d'une manière ou d'une autre (ad minima au moment de l'appel à to_string ) effectuer exactement le même calcul pour pouvoir créer la chaine en question.

    Heu... tu n'as pas -- ne serait-ce que très vaguement -- l'impression que c'est faire deux fois le boulot

    Une autre raison pour laquelle je te dis que tu te fais du mal pour rien, c'est que l'utilisation de pointeurs dont la gestion dynamique de la mémoire en générale et l'utilisation des chaines de caractères C-style en particulier est régulièrement source d'erreur:

    Il suffit vraiment de pas grand chose pour que deux objets en viennent à partager le même espace mémoire et pour que l'on en arrive à observer une modification qui n'a pas lieu d'être dans un objet alors que la modification en question était sensée s'appliquer à un autre.

    Et ce n'est encore sans doute que le moindre mal, car le corollaire à ce problème, c'est le risque de tentatives de double libération de la mémoire et / ou de fuites mémoire... Deux choses qu'il faut arriver à éviter à tout prix

    Comprenons nous bien : je ne dis pas qu'il est impossible de les éviter, loin de là! Je dis que le risque de se retrouver dans ces situations augmente considérablement quand on essaye de gérer la mémoire à la main .

    Et je dis aussi que, si tu décides (à tord ou à raison) de laisser l'utilisateur se charger de cet aspect de la chose, l'évolution des risque devient rapidement exponentielle

    Or, la bibliothèque standard de C++ vient avec un classe qui est faite pour gérer les chaines de caractères et qui est spécialement étudiée pour éviter tous les problèmes auxquels on peut être confronté lorsqu'on décide d'utiliser un char *.

    Et tu utilises déjà cette classe, vu qu'il s'agit de la classe ( std:: ) string!

    Dés lors, pourquoi ne pas utiliser cette classe à chaque fois que possible, par exemple, en la transmettant sous la forme d'une référence non constante à ta fonction au lieu de transmettre un char *

    L'opérateur += et la fonction membre de std::string "append()" sont spécifiquement prévus pour permettre la concaténation de deux chaines de caractères (entre autres, parce que c'est ce qui nous intéresse ici )!

    Pourquoi n'envisagerais tu pas d'utiliser systématiquement la classe std::string partout dans ton code, y compris comme paramètre et comme retour de fonction

    Finalement, le seul cas où tu as besoin d'un (const) char *, c'est si tu veux transmettre la chaine de caractères à une bibliothèque écrite en C (mettons, OpenGl ). Pourquoi ne pas te contenter d'invoquer la fonction membre c_str() que quand tu dois, effectivement, effectuer cette transmission

    Dans le pire des cas, tu devras peut être envisager de transmettre un char * qui, d'une manière ou d'une autre, sera modifié par la bibliothèque écrite en C.

    Dans ce cas, il te restera toujours la possibilité d'écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    char * temp = new char[str.size()];
    strcpy(temp, str.c_str(),str.size());
    laFonctionCQuiModifieLeContenuDUnCharSter(temp );
    Ne crois tu pas que cette approche serait beaucoup plus sensée et sécurisante
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    869
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 869
    Par défaut
    Je n'ai qu'une remarque a faire (ce que t'a explique koala01 par ailleurs) : pourquoi ne pas utiliser les string ?

  4. #4
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2011
    Messages
    83
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mars 2011
    Messages : 83
    Par défaut
    Un grand merci koala01 de m'avoir expliqué ta vision des choses. Je vais appliquer ton conseil qui me fera gagner beaucoup de temps et qui rendra mon code plus "propre" : utiliser les string.

    Pour information je travaillé sur un char* car dans le contexte d’envois de données via une socket, les données doivent être données sous ce format. Pour simplifier les chose je transformerai au dernier moment mon string avec c_str()

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    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 394
    Par défaut
    En plus de toutes les remarques données, tu utilises mal strcat_s().
    En fait, la façon dont tu as spécifié son paramètre de longueur, c'est tenter de soigner une fièvre en cassant le thermomètre: À bannir.

    Même si la "vraie" réponse est de ne pas utiliser les strxxx() du tout, je voulais au moins te montrer comment les utiliser correctement, pour que tu ne répètes plus jamais une telle erreur.

    Code C++ "correct" : 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
    18
    19
    void GamepadController:: add_Float_value_IEEE754(float value, char* dest, size_t destSize)
    {
     	int convert = *reinterpret_cast< int const* >(&value);
     
     	string str; 
    	str = to_string(convert);
    	const char* cc =  str.c_str();
     
    	//Ici, tu as le choix:
    	//1) Si tu t'attends à avoir la taille libre dans le buffer, 
    	//   et considères un échec comme "erreur du programmeur",
    	//   tu utilises strcat_s():
    	strcat_s(dest, destSize, cc); 
     
    	//2) Si tu t'attends à ce que le buffer soit trop petit, 
    	//   et veux tronquer la chaîne plutôt que crasher,
    	//   tu utilises strncat_s():
    	strncat_s(dest, destSize, cc, _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.

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

Discussions similaires

  1. quelques soucis avec word 2000
    Par ramchou dans le forum Word
    Réponses: 3
    Dernier message: 06/09/2004, 18h13
  2. SOucis avec une reequete imbriquee
    Par Ni4k dans le forum Langage SQL
    Réponses: 6
    Dernier message: 30/03/2004, 08h56
  3. souci avec un algorithme
    Par slider16 dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 22/03/2004, 17h17
  4. [DEBUTANT] petits soucis avec un prgm de chat
    Par LechucK dans le forum MFC
    Réponses: 8
    Dernier message: 19/01/2004, 16h52
  5. Réponses: 4
    Dernier message: 16/02/2003, 12h16

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