Partage
  • Partager sur Facebook
  • Partager sur Twitter

Résultat "étrange" avec opération sur caractère.

Sujet résolu
    1 mars 2021 à 12:38:40

    Bonjour,

    je suis actuellement entrain de résoudre les problèmes posés par France IOI, je viens de résoudre l'exercice "chiffrement par décalage" du niveau 3.

    J'ai écris une fonction pour déchiffrer un caractère que voici :

    char dechiffrer(char lettre, int num_page)
    {   
        if(num_page % 2 == 0)
        {
            lettre = lettre - ((3*num_page)%26);
            if(lettre < 'a')
                lettre += 26;
            return lettre;
        }
        else
        {
            lettre = lettre + (5*num_page)%26;
            if(lettre > 122)
                lettre -= 26;
            return lettre;
        }
    }

    Dans mon test, avec lettre = 'r' et num_page = 3 par exemple, la fonction renvoyé comme valeur -127, incapable de trouver d'où venait ce résultat, j'ai fini par tenter avec une autre variable et cela fonctionne, voici la fonction modifiée:

    char dechiffrer(char lettre, int num_page)
    {   int lettre_a_retourner = 0;
    if(num_page % 2 == 0) { lettre = lettre - ((3*num_page)%26); if(lettre < 'a') lettre += 26; return lettre; } else { lettre_a_retourner = lettre + (5*num_page)%26; if(lettre_a_retourner > 'z') lettre_a_retourner -= 26; return lettre_a_retourner; } }



    Ma question est alors, pourquoi cela ne marche pas avec ma fonction originale? J'ai vérifié mes variables lettre et num_page avant le calcul et celles-ci était bien 'r' et 3.

    -
    Edité par goldenboby 1 mars 2021 à 12:45:48

    • Partager sur Facebook
    • Partager sur Twitter
      1 mars 2021 à 12:54:38

      Bonjour !

      Est-ce que tu sais calculer 127+1 ? Ça fait -128. Tu vois pourquoi ? Du moins c'est vrai si on additionne des 'char', comme la variable 'lettre'. Par contre, avec des 'int', ça donne bien 128 comme prévu. Et justement, 'lettre_a_retourner' est un 'int'. Bref, dans la première version les additions dépassent la limite des 'char' (qui vont de -128 à 127 en général) d'où les résultats surprenants.

      • Partager sur Facebook
      • Partager sur Twitter
        1 mars 2021 à 13:10:46

        Ah! Merci beaucoup, je vais de ce pas revoir les types de variables et la prochaine fois je penserai à vérifier celui utilisé.
        • Partager sur Facebook
        • Partager sur Twitter
          1 mars 2021 à 14:18:52

          De manière générale il est vrai qu'on a plutôt tendance à manipuler les char par des entiers. D'ailleurs quasiment toutes les fonctions de la bibliothèque standard utilisent des int plutôt que des char (isblank, putc, tolower). Cela a pour but de pouvoir utiliser la constante EOF et ne pas avoir à gérer le fait que char n'est pas défini comme signé ou non par défaut (bon en pratique, certaines de ces fonctions nécessitent que la valeur ne dépasse pas un unsigned char).

          Ou alors, utiliser un unsigned char/signed char explicite. Faut aussi savoir que les entiers sont les types préférés pour optimiser les appels de fonction. Contrairement aux idées reçues, passer/retourner un char ou bool est moins performant qu'un int.

          -
          Edité par markand 1 mars 2021 à 14:20:16

          • Partager sur Facebook
          • Partager sur Twitter

          git is great because Linus did it, mercurial is better because he didn't.

            4 mars 2021 à 23:44:02

            robun a écrit:

            Bonjour !

            Est-ce que tu sais calculer 127+1 ? Ça fait -128. Tu vois pourquoi ? Du moins c'est vrai si on additionne des 'char', comme la variable 'lettre'.

            Expliqué ainsi, on pourrait penser que le code suivant risque un dépassement de capacité, alors qu'il affichera toujours 300, garanti sur facture:

            #include <stdio.h>
            
            int main(void)
            {
            	char c = 100;
            	printf("%d\n", c + c + c);
            	return 0;
            }
            



            -
            Edité par Marc Mongenet 4 mars 2021 à 23:44:40

            • Partager sur Facebook
            • Partager sur Twitter
              5 mars 2021 à 9:55:23

              Marc Mongenet a écrit:

              robun a écrit:

              Bonjour !

              Est-ce que tu sais calculer 127+1 ? Ça fait -128. Tu vois pourquoi ? Du moins c'est vrai si on additionne des 'char', comme la variable 'lettre'.

              Expliqué ainsi, on pourrait penser que le code suivant risque un dépassement de capacité, alors qu'il affichera toujours 300, garanti sur facture:

              #include <stdio.h>
              
              int main(void)
              {
              	char c = 100;
              	printf("%d\n", c + c + c);
              	return 0;
              }
              


              robun parle des limites d'un type et toi tu montres autre chose : que l'on peut effectuer des calculs avec des variables de types différents du type du résultat.

              Le spécificateur %d (sans autre modificateur de taille) attend un résultat sous forme d'un int, il n'y a donc pas de débordement.

              • Partager sur Facebook
              • Partager sur Twitter
                5 mars 2021 à 10:45:07

                Dlks a écrit:

                Marc Mongenet a écrit:

                robun a écrit:

                Bonjour !

                Est-ce que tu sais calculer 127+1 ? Ça fait -128. Tu vois pourquoi ? Du moins c'est vrai si on additionne des 'char', comme la variable 'lettre'.

                Expliqué ainsi, on pourrait penser que le code suivant risque un dépassement de capacité, alors qu'il affichera toujours 300, garanti sur facture:

                #include <stdio.h>
                
                int main(void)
                {
                	char c = 100;
                	printf("%d\n", c + c + c);
                	return 0;
                }
                


                robun parle des limites d'un type et toi tu montres autre chose : que l'on peut effectuer des calculs avec des variables de types différents du type du résultat.

                Le spécificateur %d (sans autre modificateur de taille) attend un résultat sous forme d'un int, il n'y a donc pas de débordement.

                C'est surtout le mécanisme de promotion des entiers qui intervient ici, et la promotion des paramètres → tout se calcule en int dans cas ; le "%d" n'intervient pas, sauf pour l'affichage évidemment (= l'interprétation de la valeur calculée) ce qui montre bien que les calculs ne sont pas fait avec des chars.

                • Partager sur Facebook
                • Partager sur Twitter
                  5 mars 2021 à 15:58:40

                  Justement, je ne comprends pas : pourquoi c + c + c est calculé avec des 'int' et pas des 'char' ? Parce que c'est un paramètre de 'printf' ? (J'avoue que je me serais attendu à ce que c + c + c soit calculé et donne 44, et ensuite 44 serait transformé en 'int' pour l'affichage.)
                  • Partager sur Facebook
                  • Partager sur Twitter
                    5 mars 2021 à 18:28:47

                    Il y effectivement deux étapes dans l'exemple de code posté par Marc Mongenet: comme le souligne White Crow très justement il y a une promotion des entiers en int et ensuite l'action du spécificateur de printf qui peut aussi conduire à une conversion, ce qui n'est pas le cas ici car %d attend un int.

                    Le spécificateur intervient aussi dans la conversion de type, mais après le mécanisme de promotion des entiers.

                    Ainsi, avec le spécificateur %hhd (C99) au lieu de %d, l'expression évaluée en int est convertie en char et affiche 44.

                    Extrait de C99 :

                    hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a
                    signed char or unsigned char argument (the argument will have
                    been promoted according to the integer promotions, but its value shall be
                    converted to signed char or unsigned char before printing); or that
                    a following n conversion specifier applies to a pointer to a signed char
                    argument.

                    Sur les règles de promotion des entiers, je trouve C99 très compliquée (en 6.3.1.1 a priori). Dans C89 c'était plus clair à mon goût :

                    3.2.1 Arithmetic operands

                    3.2.1.1 Characters and integers

                       A char, a short int, or an int bit-field, or their signed or
                    unsigned varieties, or an object that has enumeration type, may be
                    used in an expression wherever an int or unsigned int may be used.  If
                    an int can represent all values of the original type, the value is
                    converted to an int; otherwise it is converted to an unsigned int.
                    These are called the integral promotions.

                       The integral promotions preserve value including sign.  As
                    discussed earlier, whether a ``plain'' char is treated as signed is
                    implementation-defined.

                    -
                    Edité par Dlks 5 mars 2021 à 18:43:35

                    • Partager sur Facebook
                    • Partager sur Twitter
                      5 mars 2021 à 18:41:17

                      Dlks a écrit:

                      comme le souligne White Crow très justement il y a une promotion des entiers en int et ensuite l'action du spécificateur de printf qui peut aussi conduire à une conversion

                      Donc on est bien d'accord, ce n'est pas le "%d" qui convertit en 'int' l'opération c + c + c ? Alors c'est quoi ? Tu dis qu'il y a une promotion. Pourquoi donc ? Parce que c'est 'printf' ? Ou bien c'est propre à tout passage d'argument par valeur ?

                      • Partager sur Facebook
                      • Partager sur Twitter
                        5 mars 2021 à 18:59:05

                        je pense que tu as tapé ton post pendant que j'éditais mon message et rajoutais des explications et références.

                        %d est cependant bien un spécificateur de conversion et il a son effet propre.

                        dans notre cas, comme le résultat évalué est déjà en int le %d n'a aucune action additionnelle visible.

                        -
                        Edité par Dlks 5 mars 2021 à 18:59:55

                        • Partager sur Facebook
                        • Partager sur Twitter
                          5 mars 2021 à 20:36:45

                          Ah oui, tu as apporté plein de compléments entre temps...

                          C'est donc en fait une règle concernant les expressions : si une expression manipule des 'char', ils seront convertis en 'int' (pour le calcul de l'expression).

                          Si j'ai bien compris, ça signifie que c + c + c fait bien 300 (malgré que 'c' soit un 'char'), mais si je veux stocker le résultat dans un 'char', là il vaudra 44 (et l'affichage donnera aussi 44).

                          Essayons :

                          #include <stdio.h>
                          
                          int main(void)
                          {
                              char c = 100;
                              if ((c + c + c) % 11 != 0)
                              {   // pour tester indépendamment de 'printf'
                                  printf("Pas divisible par 11, donc vaut 300 et non 44\n");
                              }
                              printf("%d\n", c + c + c);
                              char c2 = c + c + c;
                              if (c2 % 11 == 0)
                              {   // pour tester indépendamment de 'printf'
                                  printf("Divisible par 11, donc vaut 44 et non 300\n");
                              }
                              printf("%d\n", c2);
                              return 0;
                          }

                          Résultat :

                          robun@Ordi:~ > ./zzz
                          Pas divisible par 11, donc vaut 300 et non 44
                          300
                          Divisible par 11, donc vaut 44 et non 300
                          44
                          

                          OK, c'est donc bien dans les expressions que se passent les conversions.

                          Merci Dlks et White Crow pour les explications, et merci Marc Mongenet pour la remarque initiale !

                          -
                          Edité par robun 5 mars 2021 à 20:47:51

                          • Partager sur Facebook
                          • Partager sur Twitter
                            6 mars 2021 à 0:23:46

                            robun a écrit:

                            OK, c'est donc bien dans les expressions que se passent les conversions.

                            Oui c'est bien dans les expressions. Ainsi, le standard dit à propos de l'opérateur d'addition + (C17 6.5.6§4):
                            If both operands have arithmetic type, the usual arithmetic conversions are performed on them.

                            Concernant les conversions arithmétiques habituelles, le standard dit (C17 6.3.1.8§1) plein de choses, mais notamment:
                            Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
                            If both operands have the same type, then no further conversion is needed.

                            Concernant les promotions d'entier, le standard (C17 6.3.1.1§2) dit encore plus de choses, mais notamment:
                            If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.

                            C'est fou tout ce qu'il faut lire dans le standard pour tout savoir ne serait-ce que sur une addition! Et encore, je n'ai pas cité ce qui permet de vérifier que char est un type arithmétique, ni que tout int peut représenter 300.

                            -
                            Edité par Marc Mongenet 6 mars 2021 à 0:24:05

                            • Partager sur Facebook
                            • Partager sur Twitter
                              6 mars 2021 à 9:33:06

                              Marc Mongenet a écrit:

                              C'est fou tout ce qu'il faut lire dans le standard pour tout savoir ne serait-ce que sur une addition!


                              Oui, c'est pourquoi je suis remonté à C89 qui est beaucoup plus concis et générique, même s'il est moins complet.

                              Marc Mongenet a écrit:

                              Et encore, je n'ai pas cité ce qui permet de vérifier que char est un type arithmétique, ni que tout int peut représenter 300.

                              Pour cela c'est assez simple, il suffit d'aller voir la section "Numerical limits" (5.2.4.2 dans C17) qui définit les valeurs minimales qui doivent figurer dans limits.h

                              • Partager sur Facebook
                              • Partager sur Twitter

                              Résultat "étrange" avec opération sur caractère.

                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                              • Editeur
                              • Markdown