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 :

Utilisation de NULL comme 1er argument de sprintf()


Sujet :

C

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut Utilisation de NULL comme 1er argument de sprintf()
    Bonjour à tous.

    Je cherche à écrire un petit code pour transformer un nombre en chaîne de caractères. Après quelques recherches Google, je décide d'utiliser la fonction SPRINTF. J'ai écrit quelques lignes de codes qui fonctionnent mais je voulais savoir si elles étaient correctes, dans le sens "respectent-elles les règles de bon usage?".

    Cela concerne notamment l'utilisation de NULL comme argument de SPRINTF pour connaitre la taille de la chaine à écrire.

    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
    18
    19
    20
    21
    22
    23
    24
    #include "stdlib.h"
    #include "stdio.h"
    
    int main(void)
    {
        int a = 0x42;
        int i=-666;
        char *ptChaine=NULL;
    
    
        // on regarde la taille necessaire pour ecrire la chaine
        i = sprintf(NULL, "le nombre a est egal a : %x\ n",a);
        // on alloue la chaine sinon elle reste à NULL avec le sprintf
        ptChaine = malloc(i);
        // on peut enfin l'écrire
        sprintf(ptChaine,"le nombre a est egal a : 0x%x\n",a);
        // et l'afficher
        puts(ptChaine);
    
    
        system("pause");
        return 0;
    }

    Merci d'avance

  2. #2
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    Je ne pense pas que la norme accepte un paramètre NULL comme premier argument. C'est peut-être spécifique à une implémentation.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  3. #3
    Membre confirmé 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
    Points : 496
    Points
    496
    Par défaut
    Je pense aussi que cela viens de l'implémentation de ta libc et que tu as de la chance que cela fonctionne. Et du devrai changer ton code, pourquoi ne pas déclarer un tableau sur la stack ? Cela permet de ne pas oublier de libéré la mémoire chose que je ne vois pas dans ton code ? Mise à part si tu es dans le mode de l'embarqué on est plus à quelques octets d'économie.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    char szChaine[256];
    sprintf(szChaine,"le nombre a est egal a : 0x%x\n",a);
        // et l'afficher
        puts(szChaine);
        //PLus la peinne d'appellé free et j'ai tout compris !
    ________________________________________________
    http://bliquid.fr : Blog sur Android et l'Acer Liquid

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Mise à part si tu es dans le mode de l'embarqué
    Pas de chance alors

    Le code donné a été fait sur CodeBlock/GCC, mais en fait j'en ai besoin sur un micro-contrôleur. D'ailleurs en compilant avec le logiciel de développement associé (MPLAB avec C30 de Microchip), ça ne marche plus (ou alors l'origine des nombreux "errors", "warnings" et autres "build failed" m'échappent ^^)

    Dans mon cas présent, je peux contourner le problème en jugeant que mon nombre ne dépassera pas une certaine limite, donc je peux contraindre en avance la taille de la chaîne, mais cela n'est pas générique. Dommage.

    Exact pour la libération de mémoire. Une habitude que j'ai du mal à prendre. Pourriez-vous me rappeler pourquoi char szChaine[256]; ne bloque pas un morceau de mémoire (ou en tout cas pourquoi c'est moins grave qu'avec malloc)?

  5. #5
    Membre confirmé 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
    Points : 496
    Points
    496
    Par défaut
    Généralement ton OS t'alloue de la mémoire pour l'exécution de ton programme.
    Lorsque tu déclare des variables dans ton programme elle sont ajouté "sur" la pile (stack), et sont libère automatiquement lors de la sortie de ton champ.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Func(){
    int a, b; //généralement 4x2 octets sur la pile
    return ; //Lors de la sortie de la func libération de la mémoire pour a et b
    }
    L'utilisation de malloc ou (new en C++) tu demande a l'OS un bloc mémoire qui sera conservé jusqu'à la fin de l'exécution de ton programme ou libérée avec l'instruction free (delete en C++). L'oublie d'appel de free génere une fuite de mémoire (memory leak). C'est pour cela que l'utilisation de pointeur est à surveiller.
    Dans l'embarqué la stack peut être très petite et la déclaration de tableau sur la stack impossible, des appels à malloc/free sont donc obligatoire.
    ________________________________________________
    http://bliquid.fr : Blog sur Android et l'Acer Liquid

  6. #6
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Cela concerne notamment l'utilisation de NULL comme argument de SPRINTF pour connaitre la taille de la chaine à écrire.
    Non, ce n'est pas correct. Par contre si tu as un compilateur C99, tu peux utiliser snprintf() avec une destination à NULL et une taille à 0 pour obtenir la taille de la chaîne à écrire (ce comportement est garanti pour snprintf).

  7. #7
    Membre actif Avatar de ironzorg
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 288
    Points : 245
    Points
    245
    Par défaut
    Citation Envoyé par themadmax Voir le message
    Je pense aussi que cela viens de l'implémentation de ta libc et que tu as de la chance que cela fonctionne. Et du devrai changer ton code, pourquoi ne pas déclarer un tableau sur la stack ? Cela permet de ne pas oublier de libéré la mémoire chose que je ne vois pas dans ton code ? Mise à part si tu es dans le mode de l'embarqué on est plus à quelques octets d'économie.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char szChaine[256];
    snprintf(szChaine, sizeof(szChaine), "le nombre a est egal a : 0x%x\n",a);
        // et l'afficher
        puts(szChaine);
        //PLus la peinne d'appellé free et j'ai tout compris !
    Plus prudent.

  8. #8
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    @themadmax : ah oui ! Comme je raisonnais avec un tableau déclaré dans mon main, je ne voyais pas quand ça pouvait se libérer.... Normal Merci pour le rappel


    @gl : du coup je suis allé lire les derniers paragraphes du man.developpez.com qui donnent quelques détails assez spéciaux. Dont ton truc, qui colle à ce que je recherche, cool ! Par contre, je ne suis pas très familier avec toutes ses histoires de normes... Un jour peut-être je m'y intéresserais. Comment je peux savoir dans l'immédiat si mon compilateur respecte telle ou telle norme ?


    @ironzorg : c'est "plus prudent" car ça évite de dépasser la table de ton tableau alloué si j'ai bien suivi ?

  9. #9
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Comment je peux savoir dans l'immédiat si mon compilateur respecte telle ou telle norme ?
    Tu regardes dans la doc dudit compilateur.

    Sinon, pour savoir si ton compilateur supporte snprintf(), essaies tout simplement de compiler et d'exécuter un code contenant snprintf().

  10. #10
    Membre actif Avatar de ironzorg
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    288
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 288
    Points : 245
    Points
    245
    Par défaut
    Citation Envoyé par Bktero Voir le message
    @ironzorg : c'est "plus prudent" car ça évite de dépasser la table de ton tableau alloué si j'ai bien suivi ?
    Lorsque des donnees sont traitees et dependent de l'utilisateur (ou meme de toi), c'est toujours mieux de fixer des bornes au stockage de ces donnees. En l'occurrence il n'y aurait pas eu de probleme, mais si jamais tu decidais de reduire la taille du tableau par soucis d'economie de memoire, il y aurait eu risque de depassement de tampon.

    Un cas dangereux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #include <sys/types.h>
     
    char t[10];
    u_int a;
     
    sprintf(t, "Number: %d", a);
    u_int est un typedef de unsigned int qui permet le stockage d'un entier dont la valeur est comprise entre 0 et 2^32, soit 10 chiffres. Si tu laisses l'utilisateur gerer (ou si ton code modifie par je-ne-sais-quel-moyen) a, tu as potentiellement une chance d'avoir un SIGSEGV (strlen("Number: ") + 10 > sizeof(t)).

  11. #11
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Merci à tous pour les informations, je place une balise "résolu"

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

Discussions similaires

  1. Réponses: 3
    Dernier message: 30/06/2015, 13h35
  2. Vecteur comme 1er argument de rnorm()
    Par powdaddy dans le forum R
    Réponses: 1
    Dernier message: 22/04/2013, 02h48
  3. Utiliser une structure comme argument d'une fonction
    Par guilermo dans le forum Débuter
    Réponses: 7
    Dernier message: 15/10/2009, 10h05
  4. Réponses: 4
    Dernier message: 28/11/2006, 09h50
  5. Réponses: 18
    Dernier message: 03/07/2006, 15h53

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