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 :

Saisie d'un entier sans débordement


Sujet :

C

  1. #1
    Membre éclairé Avatar de SteelBox
    Profil pro
    Inscrit en
    Novembre 2002
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Novembre 2002
    Messages : 446
    Par défaut Saisie d'un entier sans débordement
    Bonjour, je voudrais faire une fonction propre qui permet de récupérer un entier en évitant tout débordement.
    J'ai bien quelques idées, mais c'est pas super propre ou efficace (allouer un buffer assez gros ou réallouer de la mémoire si nécessaire et comparer les caractères un à un avec ceux de INT_MAX dans limits.h dès que l'on atteint le nombre max de caractère composant INT_MAX).

    Voici une fonction par exemple qui limite la saisie à 4 caractères. Perso, je ne vois pas comment faire un débordement sur un système 32 bits, mais c'est pas la panacée de limiter à 4 caractères ni à un nombre fixes. Je voudrais pouvoir atteindre la limite max du système.
    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
     
    static int gets_int() 
    { 
    	char buffer[sizeof(int)+1];
    	int i;
    	const int NB_MAX_CAR=4;
     
    	for(i=0;i<NB_MAX_CAR;i++)
    	{
    		buffer[i]=charGet();
    		if(buffer[i]=='\r')
    			break;
    	}
    	buffer[i]=0;
     
     
    	return atoi(buffer);
    }
    Help :-D

  2. #2
    Rédacteur

    Avatar de gege2061
    Femme Profil pro
    Administrateur de base de données
    Inscrit en
    Juin 2004
    Messages
    5 840
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Juin 2004
    Messages : 5 840
    Par défaut Re: Saisie d'un entier sans débordement
    Bonjour,
    Attention, tu pourrait rencontrer des surprises avec un prototype incomplet : Liste de paramètres vide
    Qu'elle est le rôle de cette fontion? une redéfinition de getchar? Tu peux utiliser fgets qui attend la taille du tableau de caractères comme argument.
    int atoi(const char* s);
    Equivalent to (int)strtol(s, (char**)NULL, 10) except that errno is not necessarily set on conversion error.
    Beaucoup mieux

    Après pour éviter les dépassement, en utilisant le type signed int, une fois INT_MAX atteint, tu repasse en négatif (ce comportement est surement dépendant du codage des entiers dans la mémoire) donc un petit teste pour vérifier si le nombre saisie n'est pas négatif suffit.

  3. #3
    Membre éclairé Avatar de SteelBox
    Profil pro
    Inscrit en
    Novembre 2002
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Novembre 2002
    Messages : 446
    Par défaut
    charGet();
    Qu'elle est le rôle de cette fontion? une redéfinition de getchar?
    Oui

    En fait, ce que je veux, c'est récupérer le nombre de caractères dans INT_MAX une fois celui ci convertit en chaine. Donc, en gros, je peux compter ce nombre de caractères en faisant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int nbCar=0;
    r=INT_MAX;
    while(r>0)
    {
      r/=10;
      nbCar++;
    }
    Comme ca j'alloue un buffer de taille nbCar et je n'autorise la saisie que de nbCar caractères au maximum. Ainsi on peut saisir un nombre qui va juqu'à INT_MAX. Bien sur j'effectue une conversion ensuite de mon nombre pour voir s'il n'y a pas eu une erreur (il est toujours possible de rentrer un nombre supérieur à INT_MAX, même si l'on ne peut pas saisir plus de caractères qu'il n'y en a dans INT_MAX).
    Voilà, je trouve cette solution pas très performante. Vous n'en voyez pas une autre ?

    Merci

  4. #4
    Membre Expert
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Par défaut
    J'avoue ne pas très bien comprendre ce que tu souhaites faire. Si tu veux qu'un débordement cause une erreur, alors strtol() est faite pour toi. Si tu veux "rattraper" un débordement de facon silencieuse, c'est plus difficile.

    En cas de débordement, strtol() positionne errno sur ERANGE, et retourne LONG_MIN ou LONG_MAX. Si tu te trouves dans ce cas de figure, alors tu as deux choix: avertir l'utilisateur de son erreur, et faire échouer le programme, ou tenter la lecture dans un long long avec strtoll(). Cf. les man pages de ces fonctions pour plus d'infos. Note que strtoll() est C99.

  5. #5
    Membre éclairé Avatar de SteelBox
    Profil pro
    Inscrit en
    Novembre 2002
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Novembre 2002
    Messages : 446
    Par défaut
    strol prend une chaine de caractères en premier argument. Comment savoir quelle taille donner à cette chaine de caractères ? perso, je veux lui donner le nombre de caractères contenus dans INT_MAX.

    Si INT_MAX=2147483647, alors j'alloue un buffer de 10+1 caractères (+1 à cause du 0 terminal) et je fais la conversion avec strol.
    Ceci est la solution que je propose. Je veux juste savoir si vous n'en avez pas une meilleure car il faut récupérer le nombre de caractères de INT_MAX avec la boucle que j'ai donnée dans mon post précédent.
    That's all

  6. #6
    Membre Expert
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Par défaut
    Citation Envoyé par SteelBox
    strol prend une chaine de caractères en premier argument. Comment savoir quelle taille donner à cette chaine de caractères ? perso, je veux lui donner le nombre de caractères contenus dans INT_MAX.
    Je comprends de moins en moins. Il suffit d'utiliser une chaine de caractere de, par exemple, 100 caracteres. Ok, tu ne les utiliseras pas tous, mais peu importe.
    Je suis un peu perdu. En regle general, on fait attention a ce qu'un nombre entier tienne dans le type qu'on utilise. La taille des chaines de caracteres est le moindre des soucis...

  7. #7
    Membre éclairé Avatar de SteelBox
    Profil pro
    Inscrit en
    Novembre 2002
    Messages
    446
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Novembre 2002
    Messages : 446
    Par défaut
    oui, je peux surdimenssioner la chaine sans problème, mais ce n'est pas une solution très propre dans le principe.
    Bon, je proprose ceci :
    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
     
    static int gets_int()
    { 
    	char buffer=0;
    	int i;
    	int nb=0;
     
    	for(i=0;buffer!='\r';i++)
    	{
    		buffer=charGet();
    		if(isdigit(buffer)!=0)
    			nb=nb*10+(buffer-'0');
    	}
     
    	return nb;
    } /*----- fin de my_gets */
    Maintenant, je veux détecter s'il y a eu un dépassement lors de l'opération qui affecte nb. Comment fait strol pour faire ca ? On peut récupérer les flags du processeur avec une instruction il me semble, non ?

  8. #8
    HRS
    HRS est déconnecté
    Membre chevronné
    Avatar de HRS
    Inscrit en
    Mars 2002
    Messages
    678
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 678
    Par défaut
    faut faire l'opération inverse

    comparer la chaîne saisie (après avoir vérifié la numéricité)
    avec la chaine "INT_MAX" avant conversion

    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
     
    #include <limits.h>
    #include <stdio.h>
    int VerifEntier (char * entier) {
        char maxint[20];
        int sailg,maxlg,i;
        sprintf (maxint,"%d",INT_MAX);
        maxlg = strlen (maxint);
        sailg = strlen (entier);
        for (i=0;i<sailg;i++) 
            if (entier[i] < '0' || entier[i] > '9') 
                return -1;
        if (sailg < maxlg) return 0; 
        if (sailg > maxlg) return -1;
        if (strcmp (entier,maxint) == 1)
            return -1;
        else
            return 0;                          
    }    
    int main (void) {
        int nb;
        char chainesaisie[100] = "1432988541";
        if (VerifEntier (chainesaisie) == -1) {
           printf ("chaine saisie pas OK");
           nb = 0; }
        else {
           printf ("chaine saisie Ok");
           sscanf(chainesaisie, "%d", &nb);}                  
        getchar();
        return 0;
    }

  9. #9
    Membre Expert
    Inscrit en
    Décembre 2004
    Messages
    1 478
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 478
    Par défaut
    Mais pourquoi utiliser des chaines de caracteres ? Cela complique tout.
    Voici un programme qui fait la conversion d'un argument en ligne de commande. Si l'entier ne tient pas dans un long integer, on tente un long long, sinon on echoue.
    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
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
     
    int main(int argc, char *argv[])
    {
      long i;
      long long j;
      int ret = EXIT_SUCCESS;
      char *p;
     
      if(argc != 2)
      {
        fprintf(stderr, "Usage: %s <integer>\n", argv[0]);
        ret = EXIT_FAILURE;
      }
      else
      {
        i = strtol(argv[1], &p, 10);
        if(*p != '\0')
        {
          fprintf(stderr, "Could not convert %s to integer.\n", argv[1]);
          ret = EXIT_FAILURE;
        }
        else if(errno == ERANGE)
        {
          errno = 0;
          /*
           * value overflows an long integer.
           * trying long long integer (C99)
           */
          j = strtoll(argv[1], &p, 10);
     
          if(errno == ERANGE)
          {
            fprintf(stderr, "Conversion to integer leads to overflow.\n");
            ret = EXIT_FAILURE;
          }
          else
          {
            printf("Value is %lld (long long integer).\n", j);
          }
        }
        else
        {
          printf("Value is %ld (long integer).\n", i);
        }
      }
     
      return ret;
    }
    On essaie:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    > toto 123456789
    Value is 1234567899 (long integer).
    > toto 12345678990
    Value is 12345678990 (long long integer).
    > toto 12345678990988998909890
    Conversion to integer leads to overflow.
    Il est simple d'adapter ce programme a une chaine obtenue par fgets(). La longueur de la chaine peut etre mise a 100 caracteres, ou 200, ou 1000. Sauf si l'on est en embarque, 1ko de memoire, ce n'est rien.

Discussions similaires

  1. Récupération d'un entier sans un zéro devant
    Par piotrr dans le forum Langage
    Réponses: 4
    Dernier message: 03/05/2007, 22h48
  2. Des <li> en float sans débordement ?
    Par vinzzonline dans le forum Balisage (X)HTML et validation W3C
    Réponses: 3
    Dernier message: 29/06/2006, 16h22
  3. lire des entiers sans appuier sur entree
    Par zied.ellouze dans le forum C
    Réponses: 5
    Dernier message: 30/04/2006, 13h00
  4. Réponses: 18
    Dernier message: 15/11/2005, 10h13
  5. [VB.NET] [VS.NET] Tester si le texte saisi est un entier
    Par San Soussy dans le forum Windows Forms
    Réponses: 2
    Dernier message: 19/10/2004, 10h41

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