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 :

Le vilain scanf


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Deficient visuel
    Inscrit en
    Mai 2019
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Deficient visuel

    Informations forums :
    Inscription : Mai 2019
    Messages : 216
    Par défaut Le vilain scanf
    Bonjour a toutes et a tous ,
    Puisque le scanf n'est pas conseillé , je cherche a le remplacer par une fonction plus sécurisé , du genre fgets ou autre chose . Pouvez vous m'apporter votre aide ?

    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    #include <stdio.h>
    #define prixCle 10
    #define prixOrdi 500
    float calcul(int nArticle,float prix);
     
    int main(void)
    {
     char tab[3];
     float remiseIm,remise,prix,result,paiement,billet500;
     int nArticle,choix,billet100,billet50,billet10,pieces2,pieces1,
    rendu;
     choix=0;prix=0;remise=0;remiseIm=0;nArticle=0;paiement=0; result=0;rendu=0;billet100=0;billet50=0;billet10=0;pieces2=0;
     pieces1=0;
     puts("***************************************************\n");
    puts("********** |BIENVENUE CHEZ OIM| *******************\n");
    puts("***************************************************\n");
     
     puts("Article disponible en stock:\n");
     
     puts("\tCHOIX 1:cle usb");
     puts("\t(prix a l'unité: 10 euros)\n");
     
     puts("\tCHOIX 2:ordinateur portable(10%% remise supplementaire)");
     puts("\t(prix a l'unité: 500 euros)\n");
     
     puts("\t10 article acheté,5%% de remise sur tous les articles");
     puts("\t50 article acheté,10%% de remise sur tous les articles\n");
     
     printf("Quel est votre choix?\n");
     scanf("%d",&choix);
     while((choix!=1)&&(choix!=2))
       {
        printf("Mauvais choix,recommencez\n");
        scanf("%d",&choix);
       }
     do
       {printf("Combien d'article désirez vous?\n"); 
        scanf("%d",&nArticle); 
       }while(nArticle<1);
     
     switch(choix)
     {
      case 1:
          result=calcul(nArticle,prixCle);
          printf("Vous avez choisis %d cle usb\n",nArticle);
          printf("Votre montant total sera de:%4.0f Euros\n",result);
          break; 
      case 2:
     
          result=calcul(nArticle,prixOrdi);
          printf("Vous avez choisis %d ordinateurs portable\n",nArticle);
          printf("Votre montant total sera de:%5.0f Euros\n",result);
          remise=(result*10)/100;
          result=result-remise;
          printf("Le montant total apres remise supplémentaire:%5.0f Euros\n",result);
          break;
      default:
          break;
     }
     printf("\n\n");
     printf("________________________ PAIEMENT_________________________\n\n");
     printf("Paiement par espece seulement disponible!\n");
     printf("Quel sera le montant de votre paiement?\n");
     scanf("%f",&paiement);
     
     if(paiement<result)
       { printf("Votre paiement est insuffisant!\n");
         while(paiement<result)
         { printf("Entrez un montant plus élevé\n");
           scanf("%f",&paiement);
         }
       }
     else if(paiement==result)
      printf("Aucune monnaie a vous rendre\n");
     else(paiement>result);
     {
       rendu=paiement-result;
       billet100=rendu/100;
       rendu %= 100;
       billet50=rendu/50;
       rendu %= 50;
       billet10=rendu/10;
       rendu %= 10;
       pieces2=rendu/2;
       rendu %= 2;
       pieces1=rendu/1;
       rendu %= 1;
     
     printf("\n______________ MONNAIE RENDUE _____________________\n\n");
     printf("Nombre de billet de 100 Euros: %d\n",billet100);
     printf("Nombre de billet de 50 Euros: %d\n",billet50);
     printf("Nombre de bilet de 10 Euros: %d\n",billet10);
     printf("Nombre de pieces de 2 Euros: %d\n",pieces2);
     printf("Nombre de pieces de 1 Euros: %d\n",pieces1);
     }
     printf("\n\n");
     printf("\t--> code promo pour un futur achat  :\"MANGER DES POMMES!\"\n");
     
     return 0;
    }
     
     float calcul(int nArticle,float prix)
     {float remise=0,result=0;
       if((nArticle>=10) && (nArticle<50))
         {
          remise=(prix*5)/100;
          result=prix-remise;
          result=result*nArticle;
         }
       else if(nArticle>=50)
         {
          remise=(prix*10)/100;
          result=prix-remise;
          result=result*nArticle;
         }
       else
         {
          result=prix*nArticle;
         }
       return result;
     }

  2. #2
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2019
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2019
    Messages : 108
    Par défaut
    Je pense que fgets, atoi, atof devrait faire l'affaire.

    atoi et atof se trouvent dans stdlib.h

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par baragouine Voir le message
    Je pense que fgets, atoi, atof devrait faire l'affaire.

    atoi et atof se trouvent dans stdlib.h
    Citation Envoyé par chrtophe Voir le message
    - convertit cette chaine en entier vai atol()
    - vérifie la validité de la conversion (voir man de atol)
    Non, on n'utilise pas atoi et atof.

    http://manpagesfr.free.fr/man/man3/atoi.3.html
    [La fonction atoi() convertit le début de la chaîne pointée par nptr en entier de type int . Le résultat est identique à un appel
    strtol(nptr, (char **) NULL, 10); à la différence que atoi() ne détecte pas d'erreur.
    http://www.cplusplus.com/reference/cstdlib/atoi/
    On success, the function returns the converted integral number as an int value.
    If the converted value would be out of the range of representable values by an int, it causes undefined behavior. See strtol for a more robust cross-platform alternative when this is a possibility.
    Voir aussi (et surtout ?) https://wiki.sei.cmu.edu/confluence/...ng+to+a+number


  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 258
    Par défaut
    Non, on n'utilise pas atoi et atof.
    Je l'ai indiqué dans ma réponse en #7. J'ai présenté la fonction en précisant qu'il faut utiliser strtol.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  5. #5
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Je te citais car tu parles de vérifier la conversion, ce qui n'est pas possible.

  6. #6
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 258
    Par défaut
    Je te citais car tu parles de vérifier la conversion, ce qui n'est pas possible.
    Pas avec atoi, mais avec strol.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  7. #7
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par chris7522 Voir le message
    Bonjour a toutes et a tous
    Bonjour

    Citation Envoyé par chris7522 Voir le message
    Puisque le scanf n'est pas conseillé , je cherche a le remplacer par une fonction plus sécurisé , du genre fgets ou autre chose . Pouvez vous m'apporter votre aide ?
    Il serait bon d'être plus clair sur ce que tu entends par "sécurisée" (au féminin car c'est un adjectif qui s'accorde avec "fonction") car c'est une notion assez large. La sécurité ça peut être "ne pas saisir plus que ce que la zone peut recevoir" ou bien "saisir en masqué pour pas qu'on voie ce que je tape" ou autre. Et peut-être alors qu'il serait bon que tu saches exactement ce qu'on reproche à scanf()...

    Si ton souci c'est "faire saisir un int et pas autre chose", alors moi j'utilise ce genre de technique
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int saisieInt(char *prompt) {
    	char saisie[100];
    	int nb;
    	while (1) {
    		fputs(prompt, stdout);
    		fflush(stdout);
    		fgets(saisie, 100, stdin);
    		if (sscanf(saisie, "%d", &nb) == 1) break;
    		fputs("Mauvaise saisie - Recommencez !!!\n", stdout);
    	}
    	return nb;
    }
    Pas parfait parfait car ça n'empêche pas le golio moyen de taper 5000 caractères (mon truc boucle en torche jusqu'à ce que tout soit consommé) mais ça va pour les cas les plus standards.

    Et c'est bien évidemment adaptable aux floats ou autres...

    En fait, le vrai souci de scanf() c'est que si ce qu'on tape ne correspond pas exactement à ce qu'il attend, il bloque en laissant tout ce qui ne convient pas dans stdin ce qui va foutre le why dans toutes les saisies suivantes. Et même quand on tape exactement ce qu'il attend (ex "12" pour un int), on valide par <return> et ce <return> ce n'est pas de l'int. Donc même quand on veut être réglo, on ne peut quand-même pas l'être.
    Cette façon de faire consiste simplement à faire saisir en string pour être sûr que stdin soit clean puis ensuite, voir ce qu'on peut faire avec ce qui a été saisi..."
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  8. #8
    Membre confirmé
    Homme Profil pro
    Deficient visuel
    Inscrit en
    Mai 2019
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Deficient visuel

    Informations forums :
    Inscription : Mai 2019
    Messages : 216
    Par défaut
    Merci de votre aide .
    Je ne connais pas grand chose au C , puisque débutant , mais a chaque fois que j'écris quelque chose avec cette fonction , on me tombe dessus .
    J'entends souvent parler de buffer overflow , j'en ai compris qu'il était possible d'entrer du code par le biais de scanf et de l'éxecuter ensuite . Bref , j'ai cru comprendre qu'il fallait bannir scanf .

  9. #9
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par chris7522 Voir le message
    mais a chaque fois que j'écris quelque chose avec cette fonction , on me tombe dessus .
    Pas nous. Nous ici, on explique posément le pourquoi des choses.

    Citation Envoyé par chris7522 Voir le message
    J'entends souvent parler de buffer overflow , j'en ai compris qu'il était possible d'entrer du code par le biais de scanf et de l'éxecuter ensuite .
    Oui effectivement. Le buffer overflow c'est rentrer plus d'octets que ceux prévus dans la zone réceptrice et faire en sorte que ce qui dépasse ce soit du code exécutable.
    Et donc typiquement ce type de code char saisie[10]; scanf("%s", saisie) permet cette action.

    Citation Envoyé par chris7522 Voir le message
    Bref , j'ai cru comprendre qu'il fallait bannir scanf .
    Exact. Mais maintenant tu sais pourquoi. Et en passant par fgets() comme je l'ai montré tu évites ce souci et tous ceux liés à l'utilisation d'une fonction qui attend un truc "formaté" alors que ce que tape un humain est tout sauf formaté.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  10. #10
    Membre confirmé
    Homme Profil pro
    Deficient visuel
    Inscrit en
    Mai 2019
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Deficient visuel

    Informations forums :
    Inscription : Mai 2019
    Messages : 216
    Par défaut
    Merci a toi . J'ai bien pris note de ta technique .
    Par contre , ignorant que je suis , je n'arrive pas a mettre en oeuvre ta technique dans le code que j'ai envoyé précédemment .
    Sans pour autant repercuter la technique a l'ensemble du code (peut etre un peu fastidieux), peut tu me montrer ce que cela donne sur le debut du code .
    Merci par avance .

  11. #11
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 258
    Par défaut
    Tu crée un buffer pour ta chaine, buffer de 50 octets par exemple (choix arbitraire).

    Tu utilises la fonction fgets qui attend la chaine, le nombre de caractère maximal, et le handle du fichier à lire (handle=variable contenant le moyen pour l'OS e gérer le fichier, élement retourné par la fonction fopen ). Cette fonction est prévue pour lire sur un fichier. Il existe une fonction prévue pour lire une chaine sur la console : gets, mais elle ne permet pas de limiter la taille lue.

    Si tu utilises en handle la valeur stdin, fgets lira ce qui est saisie sur la console. stdin est un fichier virtual correspondant à ce qui est lue sur la console.
    Tu dois limiter le nombre de caractère à lire à la taille de ton buffer -1 (donc dans notre cas plus haut 49), ceci afin de laisser la place au caractère \0 finissant la chaine de caractère (voir cours sur les chaines de caractères en C). La lecture s'arrêtera après un retour chariot ou ateinte du nombre max de caractères pouvant être lu.

    Comme tu veux un entier, il te faut ensuite convertir la chaine lue en entier. Il existe une fonction nommée atoi qui fait cela, simple à utiliser, mais ne faisant pas de vérification. Il ne faut donc pas l'utiliser sauf si tu es sûr que ta chaine est fiable (un peu comme l'exemple d'utilisation de sscanf par Sver), ce qui est impossible avec une saisie console. C'est un peu comme scanf, sauf qu'elle ne provoquera pas de risque de buffer overflow mais une valeur non fiable. Tu peux par contre utiliser la fonction strtol. Cette fonction est très intéressante car elle accepte des espaces en début de chaine sans générer d'erreur, elle gère aussi le symbole + ou - . (voir son man)

    Essayes donc de créer un code qui :
    - lit une chaine avec fgets
    - convertit cette chaine en entier vai atol()
    - vérifie la validité de la conversion (voir man de atol)
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  12. #12
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par chris7522 Voir le message
    Sans pour autant repercuter la technique a l'ensemble du code (peut etre un peu fastidieux), peut tu me montrer ce que cela donne sur le debut du code .
    Tu remplaces
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     printf("Quel est votre choix?\n");
     scanf("%d",&choix);
    while((choix!=1)&&(choix!=2))
       {
        printf("Mauvais choix,recommencez\n");
        scanf("%d",&choix);
       }
    par
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    while (1) {
    	choix=saisieInt("Quel est votre choix?");
    	if (choix >=1 && choix <=2) break;
    	printf("Mauvais choix,recommencez\n");
    }

    Citation Envoyé par chrtophe Voir le message
    Tu dois limiter le nombre de caractère à lire à la taille de ton buffer -1 (donc dans notre cas plus haut 49), ceci afin de laisser la place au caractère \0 finissant la chaine de caractère (voir cours sur les chaines de caractères en C).
    Non. fgets() réserve elle-même la place pour stocker le '\0'. Donc tu lui donnes 50 et elle s'arrête d'elle-même à 49. D'où mon exemple où je définis une zone à 100 et je lui passe 100.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. scanf
    Par bourinator dans le forum C
    Réponses: 8
    Dernier message: 26/09/2003, 14h04
  2. bp scanf...
    Par drKzs dans le forum C
    Réponses: 6
    Dernier message: 18/09/2003, 23h08
  3. PB avec scanf
    Par ché dans le forum C
    Réponses: 6
    Dernier message: 13/08/2003, 07h25
  4. [debutant]la fonction scanf
    Par kalaka dans le forum C
    Réponses: 7
    Dernier message: 01/07/2003, 15h15
  5. Réponses: 6
    Dernier message: 10/09/2002, 03h35

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