Forum Programmation.c++ deux char pour faire un short

Posté par  .
Étiquettes : aucune
0
2
mar.
2007
Je reçois d'une communication deux "char" qui sont en fait un "short" coupé en deux. Lorsque je les combine j'ai une erreur dans la valeur.
exemple lorsque je fais :


char test1,test2 ;
test1 = 0xaa;
test2 = 0xaa;

short result = ((test1<<8)+test2);

cout << hex << "result = " << result << endl;

Ceci me retourne :
result = a9aa

Est-ce que quelqu'un peut me dire ou est l'erreur ?
  • # au hasard ...

    Posté par  . Évalué à 1.


    short result = ((test1<<8)+test2)


    ou tu fais un decalage d'un char sur lui meme avant de concatener tes 2 chars en un short.

    et bizarrement ton <<8 devient au final un soustraction puisque 0xaa devient 0xa9
    • [^] # Re: au hasard ...

      Posté par  . Évalué à 1.

      j'ai pas compris l'histoire de la soustraction. Tu peux me réexpliquer ça simplement.

      Je suis ralenti c'est vendredi

      PS. si je test la meme chose avec, par exemple test1=test2= 0x77 le résultat est bien 7777
      • [^] # Re: au hasard ...

        Posté par  . Évalué à 1.

        le coup de la soustraction

        test1=0xaa
        test1 - 1 = 0xa9

        et on dirait que c'est ce que fait ton
        test1 decalée de 8 => 0xa900

        mais cela aurait du faire pareil avec 0x77 => 0x7677
        et les effets de bords ne devrait se faire sentir que vers 0xFF
      • [^] # Re: au hasard ...

        Posté par  . Évalué à 5.

        A cause du bit de signe !

        Un char fait huit bits de large, donc :

        1) (test1 << 8) fait huit décalages sur un registre de huit bits, ce qui revient en principe à tous les faire sortir et donc ramener le registre à zéro. Le C++ fait donc soit un transtypage implicite à priori, soit une optimisation spécifique à l'architecture et au compilateur, comme stocker des char sur des long et faire de l'alignement sur des adresses multiples de 4. Mais bon, passons.

        2) Ce qui est surtout intéressant, c'est le bit de signe. 0x77, ça fait 119 en décimal et c'est un nombre positif. Par contre, sur huit bits signés, 0xaa, ça ne pas 170 mais -86 ! Comme tu utilises "+", tu obtiens une soustraction.

        Utilise "unsigned char" plutôt que char pour commencer.

        Ensuite, tu peux également utiliser une tableau "unsigned char tableau [2];" et déposer tes valeurs directement dedans avec tableau[0] et et tableau[1]. Au moins tu es sûr que tes valeurs sont concaténées en mémoire, et dans le bon ordre. Sachant cela, tu n'as plus qu'à les lire comme s'il s'agissait d'un short, en utilisant un transtypage (cast) :

        short result = *(short *)tableau;

        Ca peut être déroutant pour un coder de haut niveau, c'est la manière la plus naturelle de travailler pour le microprocesseur, qui ne fera aucune conversion dans le code final.

        Enfin, tu essaies d'être propre et portable, tu oublies la dernière proposition et tu fais man ntohs.

        Si tu veux que je développe, tu n'hésites pas.
        • [^] # Re: au hasard ...

          Posté par  . Évalué à 2.

          Sinon

          unsigned char test1, test2;
          unsigned short result = (((unsigned short)test1) <<8) | test2;


          Note le " | " à la place du place " + ".

          Je suis presque sûr que le C++ a transformé le "AA" en short signé devenu "FFAA", ce qui explique le -1 sur l'octet de poids fort, donc le "A9AA".

          Si l'on garde le code original, mais que l'on remplace + par | et que l'on arrive à FFAA, c'est que c'est ça.
        • [^] # Re: au hasard ...

          Posté par  . Évalué à 1.

          Bon, je ne connais pas le C++, mais je connais le C, alors pardon si je dis des conneries:

          Un char fait huit bits de large


          En C, c'est faux. Le standard te dit qu'il fait au moins 8 bits de large (pour stocker jusqu'à 2^7-1). Mais il peut faire plus.

          Ensuite, tu peux également utiliser une tableau "unsigned char tableau [2];" et déposer tes valeurs directement dedans avec tableau[0] et et tableau[1]. Au moins tu es sûr que tes valeurs sont concaténées en mémoire, et dans le bon ordre. Sachant cela, tu n'as plus qu'à les lire comme s'il s'agissait d'un short, en utilisant un transtypage (cast) :

          short result = *(short *)tableau;


          Mauvaise idée...
          Rien ne te dit comment les données sont alignées en mémoire. Pour la plupart des processeurs, les données doivent être alignées à une adresse spécifique (multiple de 2, ou 4 par exemple pour un long). Lorsque tu fais un cast comme ça, tu peux lui demander d'aller chercher un short à une adresse qu'il n'aime pas, et provoquer une erreur de segmentation.
          C'est comme lorsque tu essaies de dépaqueter une structure:
          cf: http://c-faq.com/strangeprob/ptralign.html

          Les types ont une (bonne) raison d'exister: il ne faut pas utiliser un cast pour "prétendre que telle donnée est de tel type", ou pour demander au compilo de la fermer...
          • [^] # Re: au hasard ...

            Posté par  . Évalué à 3.

            En C, c'est faux. Le standard te dit qu'il fait au moins 8 bits de large (pour stocker jusqu'à 2^7-1). Mais il peut faire plus.


            Sauf qu'ici, il y a de bonnes chances pour qu'il fasse effectivement huit bits, et que son format provoque des effets de bord. Chaque chose en son temps ...

            Mauvaise idée...


            Si tu avais lu mon commentaire jusqu'au bout, et celui qui suit avec, tu serais tombé sur :

            Enfin, tu essaies d'être propre et portable, tu oublies la dernière proposition et tu fais man ntohs.

            Sinon

            unsigned char test1, test2;
            unsigned short result = (((unsigned short)test1) <<8) | test2;
        • [^] # Re: au hasard ...

          Posté par  . Évalué à 1.

          Ensuite, tu peux également utiliser une tableau "unsigned char tableau [2];" et déposer tes valeurs directement dedans avec tableau[0] et et tableau[1]. Au moins tu es sûr que tes valeurs sont concaténées en mémoire, et dans le bon ordre. Sachant cela, tu n'as plus qu'à les lire comme s'il s'agissait d'un short, en utilisant un transtypage (cast) :

          short result = *(short *)tableau;

          Arg !
          J'avais pas fait gaffe à première lecture, mais quand même...
          short result = 0;
          unsigned char *p = (unsigned char*) &result;
          p[0] = 0xaa;
          p[1] = 0xaa;

          Ce code n'a aucun problème d'alignement (ce qui est correctement aligné pour un truc quelconque qui n'est pas un champ de bits sera toujours correctement aligné pour un unsigned char). Par contre, la valeur de result reste dépendante de CHAR_BITS.

          (il y a plantage éventuel suivant sizeof(short) qui peut être égal à 1, mais un if ou une bête boucle peut suffire à gérer correctement tous les cas)
  • # Comportement non défini en C

    Posté par  . Évalué à 2.

    L'opération "a << b" est indéfini en C si b est plus grand que le nombre de bits de a.
    En général, avec gcc, cette expression vaut simplement :
    a << (b % (sizeof(a) * 8))

    En général, un char fait 8 bits. Donc 'test1 << 8' vaut simplement test1, avec le type 'char'. Ton expression vaut donc la somme de deux char sur 8 bits.

    Tu peux écrire si tu veux (((short)test1) << 8)) + test2


    • [^] # Re: Comportement non défini en C

      Posté par  . Évalué à 1.

      ça ne change rien. J'avais déjà essayé.

      Merci quand même
      • [^] # Re: Comportement non défini en C

        Posté par  . Évalué à 0.

        Ah ben çà, quand on prend des choux, qu'on les décale comem des carottes et qu'on les stoques en poire à la fin faut pas trop s'etonner du résultat.

        Rapidement en char signé (par défaut) si tu fais

        char test1 = 0xaa;

        Ton char va en fait valoir bit à bit en mémoire 0xffaa.

        Avec unsigned char ca marche. Maintenant quand on veut concaténer des bits il vaut nettement mieux utiliser les outils & et | logiques bit à bit.
    • [^] # Re: Comportement non défini en C

      Posté par  . Évalué à 3.

      Ce n'est pas le décalage qui est en cause ici, c'est le fait que test2 est signé, par défaut.
    • [^] # Re: Comportement non défini en C

      Posté par  . Évalué à 1.

      L'opération "a << b" est indéfini en C si b est plus grand que le nombre de bits de a.

      Plus grand ou égal. D'où un problème (en C du moins).

      Mais il y a autre chose. Si le type char est signé (en C, c'est possible, et même courant. En C++, à voir, mais je suppose que c'est pareil.), et si CHAR_BIT vaut 8 (ce qui est garanti par POSIX, si je ne m'abuse), alors la valeur maximale pour un char est 127.

      Or 0xaa == 170.

      D'où débordement, sur un type signé, et donc comportement indéfini (en C toujours).
      En utilisant unsigned char à la place, il n'y a plus débordement à ce niveau, et a vaut bien 170.

      Ensuite, j'ai un doute et la flemme de resortir la norme, mais il me semble que les opérandes de << subissent la promotion entière. Donc la valeur de a est convertie en int avant qu'on calcule le a << 8. Pasunsigned int.

      Et 170 << 8, hors débordement, ça fait 170 * 2^8 = 43520 (et donc 43690 avec le + b). Si ton int est sur plus de 16 bits, ça passe. Puis tu stockes le tout dans un short. Si short fait aussi plus de 16 bits, ça passe.

      Si short et/ou int est signé et fait 16 bits, alors je rappelle que la valeur max d'un type signé sur 16 bits est 32767... i.e. il y aura débordement (et donc comportement indéfini pour ceux qui suivent).

      D'où, sauf erreur de ma part, une solution :
      unsigned char a = 0xaa;
      unsigned char b = 0xaa;
      unsigned short s1 = a << 8 + b;
      /* si jamais je me suis trompé sur la promotion entière */
      unsigned short s2 = (((unsigned int) a) << 8) + b;


      Vu tous les comportements indéfinis rencontrés en chemin, je ne prends pas la peine de chercher pourquoi tu as 0xa9aa plutôt que -1, 42 ou SIGSEV...
      • [^] # Re: Comportement non défini en C

        Posté par  . Évalué à 1.

        En C++, un char a toujours effectivement une taille de 'byte' par définition ... sauf qu'un 'byte' C++ n'est pas forcément 8 bits.

        Bon, c'est quand même très souvent le cas.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.